Commons Math vs. ojAlgo
There are numerous math libraries for Java. This is frustrating as a user because it’s hard to decide which to use. Sometimes an algorithm is implemented in one library, but not another, which means you must marshal your data between proprietary formats. I was working on solving systems of linear equations and there was no good Java-only solution available, so I had to write my own. I decided to contribute the SimplexSolver to Commons Math because it used a friendly license and because it already had significant mindshare. After doing so, I was informed that ojAlgo has a LinearSolver as well. Today I decided to test it out to see whether I’d wasted my time by writing my own implementation. It turns out that the ojAlgo implementation is buggy as shown by the unit test below which I created.
package com.benmccann.test;
import static org.ojalgo.constant.BigMath.EIGHT;
import static org.ojalgo.constant.BigMath.FIVE;
import static org.ojalgo.constant.BigMath.FOUR;
import static org.ojalgo.constant.BigMath.ONE;
import static org.ojalgo.constant.BigMath.SEVEN;
import static org.ojalgo.constant.BigMath.SIX;
import static org.ojalgo.constant.BigMath.TEN;
import static org.ojalgo.constant.BigMath.TENTH;
import static org.ojalgo.constant.BigMath.THREE;
import static org.ojalgo.constant.BigMath.TWO;
import static org.ojalgo.constant.BigMath.ZERO;
import java.math.BigDecimal;
import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import org.ojalgo.matrix.BasicMatrix;
import org.ojalgo.matrix.store.PhysicalStore;
import org.ojalgo.matrix.store.PrimitiveDenseStore;
import org.ojalgo.optimisation.OptimisationSolver;
import org.ojalgo.optimisation.Variable;
import org.ojalgo.optimisation.OptimisationSolver.Result;
import org.ojalgo.optimisation.linear.LinearExpressionsModel;
public class SolverTest {
@Test
public void testMath286() {
Variable[] objective = new Variable[] {
new Variable("X1").weight(TENTH.multiply(EIGHT)),
new Variable("X2").weight(TENTH.multiply(TWO)),
new Variable("X3").weight(TENTH.multiply(SEVEN)),
new Variable("X4").weight(TENTH.multiply(THREE)),
new Variable("X5").weight(TENTH.multiply(SIX)),
new Variable("X6").weight(TENTH.multiply(FOUR))};
LinearExpressionsModel model = new LinearExpressionsModel(objective);
model.setMaximisation(true);
model.addWeightExpression("C1",
new BigDecimal[] { ONE, ZERO, ONE, ZERO, ONE, ZERO }
).level(new BigDecimal(23));
model.addWeightExpression("C2",
new BigDecimal[] { ZERO, ONE, ZERO, ONE, ZERO, ONE }
).level(new BigDecimal(23));
model.addWeightExpression("C3",
new BigDecimal[] { ONE, ZERO, ZERO, ZERO, ZERO, ZERO }
).lower(TEN);
model.addWeightExpression("C4",
new BigDecimal[] { ZERO, ZERO, ONE, ZERO, ZERO, ZERO }
).lower(EIGHT);
model.addWeightExpression("C5",
new BigDecimal[] { ZERO, ZERO, ZERO, ZERO, ONE, ZERO }
).lower(FIVE);
Result result = model.getDefaultSolver().solve();
List solution = result.getSolution()
.getRows(new int[] { 0, 1, 2, 3, 4, 5 })
.toPrimitiveStore().asList();
// A valid solution of 25.8 can be produced with:
// X1=10, X2=0, X3=8, X4=0, X5=5, X6=23
// However, ojAlgo returns 21.7
Assert.assertEquals(25.8, solution.get(0) * .8 + solution.get(1) * .2
+ solution.get(2) * .7 + solution.get(3) * .3
+ solution.get(4) * .6 + solution.get(5) * .4, .1);
}
}
After releasing the Common Math SimplexSolver, I received numerous bug reports, which has been the main benefit of open sourcing it. The code is now more robust as a result. The test above was just one of many cases that Commons Math initially had trouble with, so I don’t fault the ojAlgo developers for getting it wrong the first time around – I did too. However, I’m glad I chose to contribute to a well-recognized project because it led to flushing out many of these problems. A lesser known project such as ojAlgo doesn’t have that advantage. I’m not sure why so many people are still writing their own smaller libraries instead of contributing to make the larger players better. Hopefully ojAlgo will consider recommitting its efforts towards Commons Math or another of the larger projects at some point. It would be good for the community to see some consolidation. At one point, matrix-toolkits-java was talking about combining with Commons Math, which would be a great move towards consolidated APIs.
Did you report that bug to the ojAlgo developers?
Hi Anders,
I did report the bug to the ojAlgo developers. Unfortunately, they don’t have a bug tracker, but I did send an email to their mailing list. I haven’t gotten any response yet.
I’m new to programming, so please forgive me if this is a dumb question, but how do I use your SimplexSolver? I think you wrote elsewhere that I should download it from a subversion repository on Apache, but I’m not actually sure what that means. I think I downloaded apache “commons math 2.2”, but when I tried your test code that you posted somewhere else, I get messages like “package org.junit does not exist” and “cannot find symbol GoalType”, “cannot find symbol OptimizationException”, etc. Am I missing something? Is there something I need to do to use it beyond downloading “commons math 2.2”?
Thanks!
Hi SG,
Downloading Commons Math 2.2 is enough to run the SimplexSolver.
A Subversion repository stores source code. In the past there were some bugs in the released version of SimplexSolver so it was sometimes better to get unreleased beta code from the Subversion repository. However, Commons Math 2.2 has all the submitted bug fixes, so that’s no longer necessary.
If you want to reference GoalType then you need to make sure that it’s imported into your class “import org.apache.commons.math.optimization.GoalType”. You also need to make sure the Commons Math library is on your classpath. That means you have to tell Java how to find the library when it runs your program. If you’re using Eclipse then this is called a build path http://www.wikihow.com/Add-JARs-to-Project-Build-Paths-in-Eclipse-(Java)
JUnit is a testing library. It allows you to write code to test units of program functionality. If you’re simply trying to run a program you’ve written you shouldn’t use JUnit anywhere in your code. It’s only used to write a program that tests your program.
Hi Ben,
There are some practical reasons why people write their own code. For example, I needed to solve quadratic optimization with simplex method, it is not there in Commons Math. My code is far from perfect, but at least it is
mine, so it is easily integrated with my other stuff.
Ideally, I should consider to share it with Commons Math, but this is totally different level of efforts, it would take too much time that I unfortunately do not have now (maybe when I retire …).
On the other hand, I really need good library for the quadratic optimization, so I looked at OjAlgo. My immediate
impression was that in general OjAlgo is awfully overwhelming. I tried to set up a simple test, and after 10 minutes
I still am not done with the simple thing: prepare a matrix for the goal function. I don’t see a simple way to just
pass double[][].
I wonder what is your opinion about it. Does it seem over-engineered?
Hi Benjamin,
I appreciate your explanation about a specific implementation that does not properly work, but I do not understand why you provide some specific code (rather than say some pseudo-codes) that does not work! You get the ‘solution’ as a List and then in the body of Assert trying to get the solutions using indices (returning Objects) and multiplying these Objects to numbers. I am afraid this does not really work …
Hi Jean, I made this post nearly a year ago. It’s possible that the library’s API has changed during that time as I’m pretty sure the code worked at the time of posting it.
Hello Ben,
I was wondering whether it is possible to use your contribution and solve multi-objective linear programming models where we have more that one objective function?
Hi Pezham, I don’t believe that multi-object linear programming models were supported.