-
-
Notifications
You must be signed in to change notification settings - Fork 213
Getting Started
The linear algebra part of ojAlgo is one of its main attractions as well as an essential component to the other parts. It's not difficult to use at all, but to fully exploit its capabilities there are a few things you need to know.
The BasicMatrix interface and its 3 implementations, BigMatrix, ComplexMatrix and PrimitiveMatrix, is what most new users find when they're looking for "ojAlgo's matrix class", but there are actually two matrix implementation layers in ojAlgo. There's the BasicMatrix interface and its implementations as well as the MatrixStore/PhysicalStore family of interfaces and classes.
BasicMatrix is a higher/application/logic level interface with a fixed and limited feature set, and MatrixStore/PhysicalStore are lower/algorithm/implementation level interfaces offering greater flexibility and control. Initially BasicMatrix was "the" interface to use. Everything else was just implementation stuff preferably hidden to users. This has changed. The lower level stuff is since long open and available to use for anyone. It is also where most of the development has been lately.
The BasicMatrix interface is designed for immutable implementations, and the BigMatrix, ComplexMatrix and PrimitiveMatrix implementations are indeed immutable. This can be very practical, but is an unusual feature for mathematical matrix classes, and most likely not what you expected. One of the things new users tend to get wrong is how to instantiate, and fully populate, an immutable matrix.
Each of the two implementation layers support three element types: double, BigDecimal and ComplexNumber. Most people will just use the double implementations, but some need ComplexNumber. If the matrices are not too large and you need that extra precision you can use BigDecimal.
The two layers are to some extent interoperable, but most users should choose either or. Have a look at both PrimitiveMatrix and PrimitiveDenseStore (assuming you need primitive double elements) and try to get some idea about the differences before you write too much code.
Below is some code demonstrating how to do some basic stuff, as well as pointing out some differences between PrimitiveMatrix and PrimitiveDenseStore.
import org.ojalgo.OjAlgoUtils;
import org.ojalgo.RecoverableCondition;
import org.ojalgo.matrix.BasicMatrix;
import org.ojalgo.matrix.BasicMatrix.Builder;
import org.ojalgo.matrix.PrimitiveMatrix;
import org.ojalgo.matrix.decomposition.QR;
import org.ojalgo.matrix.store.ElementsSupplier;
import org.ojalgo.matrix.store.MatrixStore;
import org.ojalgo.matrix.store.PhysicalStore;
import org.ojalgo.matrix.store.PrimitiveDenseStore;
import org.ojalgo.matrix.task.InverterTask;
import org.ojalgo.matrix.task.SolverTask;
import org.ojalgo.netio.BasicLogger;
import org.ojalgo.random.Weibull;
public class GettingStarted {
public static void main(final String[] args) {
BasicLogger.debug();
BasicLogger.debug(GettingStarted.class.getSimpleName());
BasicLogger.debug(OjAlgoUtils.getTitle());
BasicLogger.debug(OjAlgoUtils.getDate());
BasicLogger.debug();
final BasicMatrix.Factory<PrimitiveMatrix> mtrxFactory = PrimitiveMatrix.FACTORY;
final PhysicalStore.Factory<Double, PrimitiveDenseStore> storeFactory = PrimitiveDenseStore.FACTORY;
// BasicMatrix.Factory and PhysicalStore.Factory are very similar.
// Every factory in ojAlgo that makes 2D-structures extends/implements the same interface.
final PrimitiveMatrix mtrxA = mtrxFactory.makeEye(5, 5);
// Internally this creates an "eye-structure" - not a large array.
final PrimitiveDenseStore storeA = storeFactory.makeEye(5, 5);
// A PrimitiveDenseStore is always a "full array". No smart data structures here.
final PrimitiveMatrix mtrxB = mtrxFactory.makeFilled(5, 3, new Weibull(5.0, 2.0));
final PrimitiveDenseStore storeB = storeFactory.makeFilled(5, 3, new Weibull(5.0, 2.0));
// When you create a matrix with random elements you can specify their distribution.
/***********************************************************************
* Matrix multiplication
*/
final PrimitiveMatrix mtrxC = mtrxA.multiply(mtrxB);
// Multiplying two PrimitiveMatrix:s is trivial. There are no alternatives,
// and the returned product is a PrimitiveMatrix (same as the inputs).
// Doing the same thing using PrimitiveDenseStore (MatrixStore) you have options...
BasicLogger.debug("Different ways to do matrix multiplication with MatrixStore:s");
BasicLogger.debug();
final MatrixStore<Double> storeC = storeA.multiply(storeB);
// One option is to do exactly what you did with PrimitiveMatrix.
// The only difference is that the return type is MatrixStore rather than
// PhysicalStore, PrimitiveDenseStore or whatever else you input.
BasicLogger.debug("MatrixStore MatrixStore#multiply(MatrixStore)", storeC);
final PrimitiveDenseStore storeCpreallocated = storeFactory.makeZero(5, 3);
// Another option is to first create the matrix that should hold the resulting product,
storeA.multiply(storeB, storeCpreallocated);
// and then perform the multiplication. This enables reusing memory (the product matrix).
BasicLogger.debug("void MatrixStore#multiply(Access1D, ElementsConsumer)", storeCpreallocated);
final ElementsSupplier<Double> storeCsupplier = storeB.premultiply(storeA);
// A third option is the premultiply method:
// 1) The left and right argument matrices are interchanged.
// 2) The return type is an ElementsSupplier rather than a MatrixStore.
// This is because the multiplication is not yet performed.
// It is possible to define additional operation on an ElementsSupplier.
final MatrixStore<Double> storeClater = storeCsupplier.get();
// The multiplication, and whatever additional operations you defined,
// is performed when you call #get().
BasicLogger.debug("ElementsSupplier MatrixStore#premultiply(Access1D)", storeClater);
// A couple of more alternatives that will do the same thing.
storeCpreallocated.fillByMultiplying(storeA, storeB);
BasicLogger.debug("void ElementsConsumer#fillByMultiplying(Access1D, Access1D)", storeClater);
storeCsupplier.supplyTo(storeCpreallocated);
BasicLogger.debug("void ElementsSupplier#supplyTo(ElementsConsumer)", storeClater);
mtrxA.invert();
// With MatrixStore:s you need to use an InverterTask
final InverterTask<Double> tmpInverter = InverterTask.PRIMITIVE.make(storeA);
// There are many implementations of that interface. This factory method
// will return one that may be suitable, but most likely you will want to
// choose implementaion based on what you know about the matrix.
try {
tmpInverter.invert(storeA);
} catch (final RecoverableCondition exception) {
// Will throw and exception if inversion fails
}
mtrxA.solve(mtrxC);
final SolverTask<Double> tmpSolver = SolverTask.PRIMITIVE.make(storeA, storeC);
try {
tmpSolver.solve(storeA, storeC);
} catch (final RecoverableCondition exception) {
// Will throw and exception if solving fails
}
// Most likely you want to do is to instantiate some matrix decomposition (there are several)
final QR<Double> tmpQR = QR.PRIMITIVE.make(storeA);
tmpQR.decompose(storeA);
if (tmpQR.isSolvable()) {
tmpQR.getSolution(storeC);
} else {
// You should verify that the equation system is solvable,
// and do something else if it is not.
}
/***********************************************************************
* Setting individual elements
*/
storeA.set(3, 1, 3.14);
storeA.set(3, 0, 2.18);
// PhysicalStore instances are naturally mutable.
// If you want to set or modify something - just do it
final Builder<PrimitiveMatrix> mtrxBuilder = mtrxA.copy();
// PrimitiveMatrix is immutable. To modify anything you need to copy it to Builder instance.
mtrxBuilder.add(3, 1, 3.14);
mtrxBuilder.add(3, 0, 2.18);
mtrxBuilder.build();
/***********************************************************************
* Creating matrices by explicitly setting all elements
*/
final double[][] tmpData = new double[][] { { 1.0, 2.0, 3.0 }, { 4.0, 5.0, 6.0 }, { 7.0, 8.0, 9.0 } };
mtrxFactory.rows(tmpData);
storeFactory.rows(tmpData);
// If you don't want/need to first create some (intermediate) array to for
// the elements, you can of course set them on the matrix directly.
final PrimitiveDenseStore storeZ = storeFactory.makeEye(3, 3);
// Since PrimitiveMatrix is immutable this has to be done via a builder.
final Builder<PrimitiveMatrix> mtrxZbuilder = mtrxFactory.getBuilder(3, 3);
for (int j = 0; j < 3; j++) {
for (int i = 0; i < 3; i++) {
mtrxZbuilder.set(i, j, i * j);
storeZ.set(i, j, i * j);
}
}
mtrxZbuilder.get();
}
}
GettingStarted
ojAlgo
2018-03-15
Different ways to do matrix multiplication with MatrixStore:s
MatrixStore MatrixStore#multiply(MatrixStore)
0.247755 0.239503 0.127607
0.246613 0.15259 0.167258
0.13745 0.118256 0.267297
0.124159 0.183955 0.200316
0.175797 0.263439 0.285527
void MatrixStore#multiply(Access1D, ElementsConsumer)
0.247755 0.239503 0.127607
0.246613 0.15259 0.167258
0.13745 0.118256 0.267297
0.124159 0.183955 0.200316
0.175797 0.263439 0.285527
ElementsSupplier MatrixStore#premultiply(Access1D)
0.247755 0.239503 0.127607
0.246613 0.15259 0.167258
0.13745 0.118256 0.267297
0.124159 0.183955 0.200316
0.175797 0.263439 0.285527
void ElementsConsumer#fillByMultiplying(Access1D, Access1D)
0.247755 0.239503 0.127607
0.246613 0.15259 0.167258
0.13745 0.118256 0.267297
0.124159 0.183955 0.200316
0.175797 0.263439 0.285527
void ElementsSupplier#supplyTo(ElementsConsumer)
0.247755 0.239503 0.127607
0.246613 0.15259 0.167258
0.13745 0.118256 0.267297
0.124159 0.183955 0.200316
0.175797 0.263439 0.285527