11

I am trying to solve a system of nonlinear functions in java. Particularly I have 3 equations with 3 unknown variables. Although I have managed to solve simple equations, my final goal is to solve some pretty big ones. For example, each equation consists of hundreds of lines (sometimes even thousands) in the form of:

 public static double f2 (double x, double y, double z) {
        double result = (0.49*Math.exp(-y - x)*Math.pow(x,2)*
                (1 - Math.pow(z,94)*(0.00666 +
                        0.98*Math.exp(-y - x) + 0.98*Math.exp(-y - x)*
                        y*x + 0.245*Math.exp(-y - x)*Math.pow(y,2)*
                        Math.pow(x,2) + 0.02722*
                        Math.exp(-y - x)*Math.pow(y,3)*Math.pow(x,3) +
                        0.00170*Math.exp(-y - x)*
                                Math.pow(y,4)*Math.pow(x,4) + 0.00006*
                        Math.exp(-y - x)*Math.pow(y,5)*Math.pow(x,5) +
                        1.89043*Math.pow(10,-6)*Math.exp(-y - x)*
                                Math.pow(y,6)*Math.pow(x,6) + 3.85802*Math.pow(10,-8)*
                        Math.exp(-y - x)*Math.pow(y,7)*Math.pow(x,7) +
                        6.02816*Math.pow(10,-10)*Math.exp(-y - x)*
                                Math.pow(y,8)*Math.pow(x,8) + 7.44217*Math.pow(10,-12)*
                        Math.exp(-y - x)*Math.pow(y,9)*Math.pow(x,9) +
                        7.44217*Math.pow(10,-14)*Math.exp(-y - x)*
                                Math.pow(y,10)*Math.pow(x,10))))/(0.01333 +
                0.98*Math.exp(-y - x)*y +
                0.49*Math.exp(-y - x)*Math.pow(y,2) +
                0.16333*Math.exp(-y - x)*Math.pow(y,3) +
                0.04083*Math.exp(-y - x)*Math.pow(y,4) +
                0.00816*Math.exp(-y - x)*Math.pow(y,5) + .....

The problem is that two of my classes are significantly larger than 65k, particularly 650k/ class so I am out of limitations.

Is there any way to run/ compile my code, overcoming this limits?

My 3 equations have been generated from another language (wolfram) but I need to implement my goal in java (mathematical/matlab etc is not n option).

This answer suggests using .properties but I cannot see how this can help in my case ("Code too large" compilation error in Java)

Arghavan
  • 1,125
  • 1
  • 11
  • 17
Fierce82
  • 408
  • 5
  • 17
  • Cut it into two classes? –  Oct 30 '17 at 11:31
  • 1
    why on earth would you need something that big? – Lino Oct 30 '17 at 11:38
  • @RC.I have cut it into more than 1, actually 3 classes for my 3 equations, still, 2 of them are 625k... – Fierce82 Oct 30 '17 at 11:40
  • @Lino I have to transfer the solution to a complex problem in java. It is already implemented in Mathematica, but I am trying to figure out my way in java. I understand my coding is not proper or elegant, but still, I need a solution. – Fierce82 Oct 30 '17 at 11:42
  • use `import static` for `Math.exp` `Math.pow`, ... you can recover some bytes here and there with that. – AxelH Oct 30 '17 at 11:47
  • And of course, are you sure this can't be rewritten with an algorithm ? This seems to be linear .. `0.98 /2 = 0.49 /3 = 0.163333 /4= 0.04....` (the divisor being the value of the `pow` ...) – AxelH Oct 30 '17 at 11:53
  • 14
    If all else fails, there's always the option of writing an equation interpreter that takes the equations in as data from a file. Limits on the sizes of disk files and in-memory data structures are likely to be much more generous than 64K. – Kevin Anderson Oct 30 '17 at 12:00
  • 1
    @AxelH Does the qualification make any difference to the bytecode? Import static ultimately is more syntactic sugar, best I recall. – Danikov Oct 30 '17 at 12:14
  • @KevinAnderson The equations might be more readable in a tabular form, as well as avoiding the class size issue. – Patricia Shanahan Oct 30 '17 at 15:35
  • Just so you know, the way that this code's written is really inefficient in terms of run-time performance. If that's not a problem for you, then awesome - no sense in spending time optimizing something that's working well enough for your purposes. But if you're going to be doing this sorta thing a lot, you might want to take this over to SE.CodeReview or SE.ComputationalScience and ask for tips on how to get stuff like this to run faster. – Nat Oct 30 '17 at 17:20
  • In my experience for most computational purposes Java doesn't run, it crawls. :) – mathreadler Oct 30 '17 at 19:36
  • Sorry, but to me this *screams* [XY problem](http://xyproblem.info/). Could you clarify why you need to use thousands of lines of (presumably) auto-generated Java code, rather than some other approach (tabular approach, some specialised library...)? – sleske Oct 30 '17 at 19:46
  • Are you sure you actually are solving the equations with the above code? This seems like floating point errors seem to add up quickly during the calculation of the large expressions. – SpaceTrucker Oct 31 '17 at 15:51
  • @SpaceTrucker actually yes, I have managed to replicate my mathematica results. I was surprised my self, since I am totally new to both java and numerical methods. – Fierce82 Nov 01 '17 at 09:12

3 Answers3

14

The limit of 64K is applicable to a method, so you can put your code into different methods that are called one after the other passing the intermediate result.

If you reach a class-size limit (I'm not aware of one but the last time I ran into this problem myself was in the year 2000 and with Java 1.1), you can do the same trick and separate your code over multiple classes. Inner classes should be OK for this, so you still can output your code into one source file.

Edit: You can also look for calculation results that can be reused. For example you calculate Math.exp(-y - x) quite often. Putting that into a temporary variable should save space and should make the whole calculation significantly faster. As well there's the same kind of calculation taking place that can be put into its own method:

0.98*Math.exp(-y - x)*y +
0.49*Math.exp(-y - x)*Math.pow(y,2) +
0.16333*Math.exp(-y - x)*Math.pow(y,3) +
0.04083*Math.exp(-y - x)*Math.pow(y,4) +
0.00816*Math.exp(-y - x)*Math.pow(y,5) + ...

could be change to the following (typed directly into this text, so there might be compile errors IRL):

private static double calcLoop(double x, double y, int max) {
    double expVal = Math.exp(-y - x);
    double startVal = 0.98;
    double sum = startVal * y
    for (int i = 2; i <= max; i++) {
        startVal = startVal / i;
        sum += startVal * Math.pow(y, i);
    }
    return sum * expVal;
}

This method also "optimized" the calculation by only multiplying the resulting sum with expVal

Lothar
  • 5,323
  • 1
  • 11
  • 27
  • It can't be split unfortunately. Each class consists of 1 mathematical equation only... – Fierce82 Oct 30 '17 at 11:43
  • @TomZinger equation can be splitted .. you just need to understand the equation to see where you could split it. – AxelH Oct 30 '17 at 11:49
  • 1
    It looks like it can be split fairly easy, just have to be careful around those braces. It's a bit like algebra, you replace parts of the equation with a symbol which becomes your method, which you can then call. – Danikov Oct 30 '17 at 11:49
  • can you give me a naive example to understand what you mean? For instance if f2 return x + y + z + x*y + y*z + x*y*z + 15 , how can I split it? @Danikov – Fierce82 Oct 30 '17 at 11:52
  • 2
    `callPart1(x, y, z) + callPart2(x, y, z) + 15` with the method `callPart1` to be `return x + y + z;` and `callPart2` to be `return x*y + y*z + x*y*z;` – Lothar Oct 30 '17 at 11:54
  • 1
    You can make a method a(x, y, z) { return x + y + z; } and b(x, y, z) { return xy + yz + xyz + 15} and then f2 becomes return a() + b(). You just have to be careful around the parenthesis as you won't be able to extract across them. – Danikov Oct 30 '17 at 11:56
  • 2
    @AxelH done (and added `+15` because I missed that as well ;-) – Lothar Oct 30 '17 at 11:57
  • 1
    @Lothar maybe you can augment your answer with a nicer formatted example with a larger chunk of the equation, say, the example provided split into two methods? I think we're trying to convey a little too much in the comments. – Danikov Oct 30 '17 at 12:05
  • 1
    @Danikov It's hard to do that with the actual caluclation because it's incomplete so I don't know where it can actually be splitted. But while you added you comment I was working on my answer with more hints and an example how to get things even smaller. – Lothar Oct 30 '17 at 12:10
  • 1
    @Lothar extracted out a separate answer as I had something simpler in mind, although your changes are quite valid too, just more involved. – Danikov Oct 30 '17 at 13:07
  • Question here is if this will get the *same* results if using the loop because numerical erros may aggregate in `startVal` due to iterated division. – SpaceTrucker Nov 01 '17 at 09:17
10

The problem here is a 64k limit on the bytecode of a method, so the only option is to split things up. The minimal approach to this is to identify large, extractable sequences and pull them out. Take, for example:

                    0.98*Math.exp(-y - x) + 0.98*Math.exp(-y - x)*
                    y*x + 0.245*Math.exp(-y - x)*Math.pow(y,2)*
                    Math.pow(x,2) + 0.02722*
                    Math.exp(-y - x)*Math.pow(y,3)*Math.pow(x,3) +
                    0.00170*Math.exp(-y - x)*
                            Math.pow(y,4)*Math.pow(x,4) + 0.00006*
                    Math.exp(-y - x)*Math.pow(y,5)*Math.pow(x,5) +
                    1.89043*Math.pow(10,-6)*Math.exp(-y - x)*
                            Math.pow(y,6)*Math.pow(x,6) + 3.85802*Math.pow(10,-8)*
                    Math.exp(-y - x)*Math.pow(y,7)*Math.pow(x,7) +
                    6.02816*Math.pow(10,-10)*Math.exp(-y - x)*
                            Math.pow(y,8)*Math.pow(x,8) + 7.44217*Math.pow(10,-12)*

As none of these lines have non-matched braces and we know it is all one equation (no variables change in the course of calculation), they can be safely extracted as a block with identical function parameters:

 public static double a1 (double x, double y, double z) {
    return 0.98*Math.exp(-y - x) + 0.98*Math.exp(-y - x)*
                    y*x + 0.245*Math.exp(-y - x)*Math.pow(y,2)*
                    Math.pow(x,2) + 0.02722*
                    Math.exp(-y - x)*Math.pow(y,3)*Math.pow(x,3) +
                    0.00170*Math.exp(-y - x)*
                            Math.pow(y,4)*Math.pow(x,4) + 0.00006*
                    Math.exp(-y - x)*Math.pow(y,5)*Math.pow(x,5) +
                    1.89043*Math.pow(10,-6)*Math.exp(-y - x)*
                            Math.pow(y,6)*Math.pow(x,6) + 3.85802*Math.pow(10,-8)*
                    Math.exp(-y - x)*Math.pow(y,7)*Math.pow(x,7) +
                    6.02816*Math.pow(10,-10)*Math.exp(-y - x)*
                            Math.pow(y,8)*Math.pow(x,8) + 7.44217*Math.pow(10,-12);
}

This can then be injected into the original function as so:

public static double f2 (double x, double y, double z) {
    double result = (0.49*Math.exp(-y - x)*Math.pow(x,2)*
            (1 - Math.pow(z,94)*(0.00666 +
                    a1()*
                    Math.exp(-y - x)*Math.pow(y,9)*Math.pow(x,9) +
                    7.44217*Math.pow(10,-14)*Math.exp(-y - x)*
                            Math.pow(y,10)*Math.pow(x,10))))/(0.01333 +
            0.98*Math.exp(-y - x)*y +
            0.49*Math.exp(-y - x)*Math.pow(y,2) +
            0.16333*Math.exp(-y - x)*Math.pow(y,3) +
            0.04083*Math.exp(-y - x)*Math.pow(y,4) +
            0.00816*Math.exp(-y - x)*Math.pow(y,5) + .....

This clearly makes no effort to optimise or eliminate duplication; it's the simplest technique to overcome the method size limit.

Danikov
  • 735
  • 3
  • 10
  • ok, I had to split my classes into 15 smaller ones, and then assemble them as you suggested. Thanks – Fierce82 Oct 30 '17 at 14:20
1

Java says try to be modular and think in terms of Objects. Create smaller classes and try to reuse the existing code and override if necessary.

There could be multiple problems with your code. From the snippet you posted here and my past experience I would suggest following:-

  1. Don't put constants within the same Class if those are more than 3-4. Just create another class that just maintains such constants.

  2. Never put data inside class except some necessary initializations.

  3. Break down a class into multiple classes.

  4. Try to find repeated code segments/statements inside the equation and pull those out to a method. You could find even nested repeated statements or segments. Think about your mathematical equation is formed of multiple method calls which could involve methods only calling other sets of methods.

  5. Once you are able to pull those out to a method, you could easily pull those out to a class.

  6. Use static imports if possible but never tried those to save some space. Hence, bit experimental.

Hope it helps!

Vinay Prajapati
  • 7,199
  • 9
  • 45
  • 86
  • 1
    While this fair advice for general Java, I don't think any of this helps this specific problem. – Danikov Oct 30 '17 at 11:53
  • 1
    4. is actually the point which *could* help with the size-problem – Lino Oct 30 '17 at 11:59
  • 4. addresses repeated statements, which are probably eliminated by the equation generation by Wolfram alpha. In this specific case, extraction of non-common code is necessary because of the method size. Same method (if you excuse the pun), different motivation. – Danikov Oct 30 '17 at 12:01