Commons Math vs. ojAlgo

01/23/2011

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.