Working With Arrays
Almost everything in ojAlgo makes use of or interacts with some array based data structure. To always code directly against raw arrays like double[], float[] or Number[] would be awkward. Therefore there are classes that encapsulate these raw arrays and, using generics and inheritance, defines a set of standard methods to interact with them. This is done in the org.ojalgo.array package.
In the org.ojalgo.array package there are classes supporting 1, 2 or “any” dimensional “arrays” of double, float, BigDecimal, ComplexNumber, Quaternion and RationalNumber. The arrays can be sparse or dense and arbitrarily large (using long indices). Further more the actual data do NOT have to be in plain Java arrays (like double[] or float[]) but may reside in byte buffers, memory mapped files, native off heap memory or whatever else you can think of.
The linear algebra stuff builds on this, and that in turn enable stuff like the optimisation solvers, neural network and more. But the array classes can also be used directly.
Example Code
WorkingWithArrays.javaimport java.io.File;
import java.math.BigDecimal;
import java.util.List;
import org.ojalgo.OjAlgoUtils;
import org.ojalgo.array.*;
import org.ojalgo.netio.BasicLogger;
import org.ojalgo.random.Cauchy;
import org.ojalgo.random.Normal;
import org.ojalgo.random.Uniform;
import org.ojalgo.scalar.ComplexNumber;
import org.ojalgo.scalar.Quadruple;
import org.ojalgo.scalar.Quaternion;
import org.ojalgo.scalar.RationalNumber;
import org.ojalgo.structure.AccessAnyD.MatrixView;
import org.ojalgo.structure.AccessAnyD.VectorView;
import org.ojalgo.structure.ElementView1D;
import org.ojalgo.type.math.MathType;
/**
* Demonstrate some basic array functionality.
*
* @see https://www.ojalgo.org/2021/08/working-with-arrays/
* @see https://github.com/optimatika/ojAlgo/wiki/Working-with-arrays
*/
public class WorkingWithArrays {
public static void main(final String[] args) {
BasicLogger.debug();
BasicLogger.debug(WorkingWithArrays.class);
BasicLogger.debug(OjAlgoUtils.getTitle());
BasicLogger.debug(OjAlgoUtils.getDate());
BasicLogger.debug();
/*
* The top level classes in org.ojalgo.array are:
*/
Array1D<BigDecimal> array1D = Array1D.R256.make(12);
Array2D<ComplexNumber> array2D = Array2D.C128.newDenseBuilder(12, 8);
ArrayAnyD<Double> arrayAnyD = ArrayAnyD.R064.newSparseBuilder(12, 8, 4);
/*
* They represent 1-, 2- and N-dimensional arrays. They are generic, so they could hold many different
* element/component types, but the generic type argument doesn't always describe exactly what the
* element type is. For instance "Double" does NOT only represent 'double' but any/all primitive types
* (double, float, long, int short and byte). There is an enum outlining which number types ojAlgo
* supports.
*/
BasicLogger.debug("Element types supported ny ojAlgo");
BasicLogger.debug("=================================");
for (MathType mathType : MathType.values()) {
BasicLogger.debug("{} := Math number set {} implemented as {} x {}", mathType, mathType.getNumberSet(), mathType.getComponents(),
mathType.getJavaType());
}
BasicLogger.debug();
/*
* There's a couple of things to take note of regarding those 3 top level classes: (1) Every single
* method implemented is declared in some interface elsewhere – typically in the org.ojalgo.structure
* package – and those same interfaces are widely used throughout ojAlgo. (2) If you look at the
* source code implementations you'll find that essentially nothing actually happens in these classes.
* Instead just about everything is delegeted to lower level classes. Those lower level
* implementations can in turn be a number of different things, but in the end there has to be a
* plain/dense array like float[], double[] or Number[]. There is a wide range of such plain/dense
* array implementations in ojAlgo.
*/
/*
* Plain Java array based
*/
PrimitiveArray.Factory plainByteArrayFactory = ArrayZ008.FACTORY; // 8-bit Integer (byte)
PrimitiveArray.Factory plainShortArrayFactory = ArrayZ016.FACTORY; // 16-bit Integer (short)
PrimitiveArray.Factory plainIntArrayFactory = ArrayZ032.FACTORY; // 32-bit Integer (int)
PrimitiveArray.Factory plainLongArrayFactory = ArrayZ064.FACTORY; // 64-bit Integer (long)
PrimitiveArray.Factory plainFloatArrayFactory = ArrayR032.FACTORY; // 32-bit Real (float)
PrimitiveArray.Factory plainDoubleArrayFactory = ArrayR064.FACTORY; // 64-bit Real (double)
ReferenceTypeArray.Factory<Quadruple> plainQuadrupleArrayFactory = ArrayR128.FACTORY; // 128-bit Real (Quadruple)
ReferenceTypeArray.Factory<BigDecimal> plainBigDecimalArrayFactory = ArrayR256.FACTORY; // 256-bit Real (BigDecimal)
ReferenceTypeArray.Factory<RationalNumber> plainRationalNumberArrayFactory = ArrayQ128.FACTORY; // 128-bit Rational Number (2 x long)
ReferenceTypeArray.Factory<ComplexNumber> plainComplexNumberArrayFactory = ArrayC128.FACTORY; // 128-bit Complex Number (2 x double)
ReferenceTypeArray.Factory<Quaternion> plainQuaternionArrayFactory = ArrayH256.FACTORY; // 256-bit Quaternion (4 x double)
/*
* Buffer based "arrays"
*/
BufferArray.Factory bufferByteArrayFactory = BufferArray.Z008;
BufferArray.Factory bufferShortArrayFactory = BufferArray.Z016;
BufferArray.Factory bufferIntArrayFactory = BufferArray.Z032;
BufferArray.Factory bufferLongArrayFactory = BufferArray.Z064;
BufferArray.Factory bufferFloatArrayFactory = BufferArray.R032;
BufferArray.Factory bufferDoubleArrayFactory = BufferArray.R064;
/*
* Using Unsafe there are also off-heap "arrays"
*/
OffHeapArray.Factory offHeapByteArrayFactory = OffHeapArray.Z008;
OffHeapArray.Factory offHeapShortArrayFactory = OffHeapArray.Z016;
OffHeapArray.Factory offHeapIntArrayFactory = OffHeapArray.Z032;
OffHeapArray.Factory offHeapLongArrayFactory = OffHeapArray.Z064;
OffHeapArray.Factory offHeapFloatArrayFactory = OffHeapArray.R032;
OffHeapArray.Factory offHeapDoubleArrayFactory = OffHeapArray.R064;
/*
* Those factories dictate both the element type and where/how they are stored. The factories can be
* used directly to instantiate plain/dense arrays,
*/
DenseArray<BigDecimal> plainBigDecimalArray = plainBigDecimalArrayFactory.make(512);
DenseArray<ComplexNumber> plainComplexNumberArray = plainComplexNumberArrayFactory.makeFilled(512, Normal.standard());
DenseArray<Double> bufferFloatArray = bufferFloatArrayFactory.makeFilled(512, Uniform.standard());
DenseArray<Double> offHeapDoubleArray = offHeapDoubleArrayFactory.makeFilled(512, Cauchy.standard());
DenseArray<Double> plainFloatArray = plainFloatArrayFactory.copy(plainBigDecimalArray);
DenseArray<Double> plainDoubleArray = plainDoubleArrayFactory.copy(plainComplexNumberArray);
DenseArray<Quaternion> plainQuaternionArray = plainQuaternionArrayFactory.copy(bufferFloatArray);
DenseArray<RationalNumber> plainRationalNumberArray = plainRationalNumberArrayFactory.copy(offHeapDoubleArray);
/*
* or they can be used as input to creating higher level factories (factories that create higher level
* data structures).
*/
array1D = Array1D.factory(plainBigDecimalArrayFactory).make(100);
array2D = Array2D.factory(plainComplexNumberArrayFactory).make(10, 10);
arrayAnyD = ArrayAnyD.factory(offHeapDoubleArrayFactory).make(10, 100, 1000, 3);
SparseArray<Double> sparse = SparseArray.factory(plainFloatArrayFactory).make(1000);
List<Double> list = NumberList.factory(bufferDoubleArrayFactory).make();
LongToNumberMap<Double> map = LongToNumberMap.factory(bufferFloatArrayFactory).make();
/*
* If you implement your own dense array factory, then just plug it in and get access to all higher
* level functionalaity.
*/
/*
* For the buffer based array classes there is also an option to create "arrays" backed by memory
* mapped files:
*/
/*
* Regarding Any-D Arrays
*/
long[] shape = { 2, 3, 1, 4 };
arrayAnyD = ArrayAnyD.factory(plainDoubleArrayFactory).make(shape);
/*
* The rank of an Any-D array is the length of the shape used to instatiate it.
*/
BasicLogger.debug("shape.lengtgh {} = rank() {}", shape.length, arrayAnyD.rank());
BasicLogger.debug("Original shape: {}", arrayAnyD.shape());
ArrayAnyD<Double> squeezed = arrayAnyD.squeeze();
/*
* squeeze() removes all indices/dimensions of range 1.
*/
BasicLogger.debug("Squeezed shape: {}", squeezed.shape());
ArrayAnyD<Double> reshaped = arrayAnyD.reshape(3, 2, 4);
/*
* reshape(...) changes the indexing but must maintain the same total number of elements.
*/
BasicLogger.debug("Reshaped shape: {}", reshaped.shape());
/*
* Now we have 3 ArrayAnyD instances, with different shapes, all backed by the same lower level
* plain/dense array.
*/
BasicLogger.debug("3 different shapes: {}, {} and {}", arrayAnyD.shape(), squeezed.shape(), reshaped.shape());
/*
* Let's fill that squeezed instance with some numbers
*/
squeezed.loopAllReferences(ref -> squeezed.set(ref, 1D + ref[2]));
/*
* Now using the reshaped instance we'll check the contents. Apart from random access contents can be
* iterated element-wise, vector-wise or matrix-wise. In this case there are 24 elements, 8 vectors
* (each of length 3) and 4 matrices (each of 2 column vectors of length 3 – 3 rows and 2 columns).
*/
BasicLogger.debug();
BasicLogger.debug("Element-wise iteration");
for (ElementView1D<Double, ?> element : reshaped.elements()) {
BasicLogger.debug("ElementView {}: {}", element.index(), element);
}
BasicLogger.debug();
BasicLogger.debug("Vector-wise iteration");
for (VectorView<Double> vector : reshaped.vectors()) {
BasicLogger.debug("VectorView {}: {}", vector.index(), vector);
}
BasicLogger.debug();
BasicLogger.debug("Matrix-wise iteration");
for (MatrixView<Double> matrix : reshaped.matrices()) {
BasicLogger.debug("MatrixView {}: {}", matrix.index(), matrix);
}
}
}
Console Output
class WorkingWithArrays
ojAlgo
2022-09-27
Element types supported ny ojAlgo
=================================
C128 := Math number set C implemented as 2 x DOUBLE
H256 := Math number set H implemented as 4 x DOUBLE
Q128 := Math number set Q implemented as 2 x LONG
R032 := Math number set R implemented as 1 x FLOAT
R064 := Math number set R implemented as 1 x DOUBLE
R128 := Math number set R implemented as 1 x REFERENCE
Z008 := Math number set Z implemented as 1 x BYTE
Z016 := Math number set Z implemented as 1 x SHORT
Z032 := Math number set Z implemented as 1 x INT
Z064 := Math number set Z implemented as 1 x LONG
shape.lengtgh 4 = rank() 4
Original shape: [2, 3, 1, 4]
Squeezed shape: [2, 3, 4]
Reshaped shape: [3, 2, 4]
3 different shapes: [2, 3, 1, 4], [2, 3, 4] and [3, 2, 4]
Element-wise iteration
ElementView 0: 0 = 1.0
ElementView 1: 1 = 1.0
ElementView 2: 2 = 1.0
ElementView 3: 3 = 1.0
ElementView 4: 4 = 1.0
ElementView 5: 5 = 1.0
ElementView 6: 6 = 2.0
ElementView 7: 7 = 2.0
ElementView 8: 8 = 2.0
ElementView 9: 9 = 2.0
ElementView 10: 10 = 2.0
ElementView 11: 11 = 2.0
ElementView 12: 12 = 3.0
ElementView 13: 13 = 3.0
ElementView 14: 14 = 3.0
ElementView 15: 15 = 3.0
ElementView 16: 16 = 3.0
ElementView 17: 17 = 3.0
ElementView 18: 18 = 4.0
ElementView 19: 19 = 4.0
ElementView 20: 20 = 4.0
ElementView 21: 21 = 4.0
ElementView 22: 22 = 4.0
ElementView 23: 23 = 4.0
Vector-wise iteration
VectorView 0: { 1.0, 1.0, 1.0 }
VectorView 1: { 1.0, 1.0, 1.0 }
VectorView 2: { 2.0, 2.0, 2.0 }
VectorView 3: { 2.0, 2.0, 2.0 }
VectorView 4: { 3.0, 3.0, 3.0 }
VectorView 5: { 3.0, 3.0, 3.0 }
VectorView 6: { 4.0, 4.0, 4.0 }
VectorView 7: { 4.0, 4.0, 4.0 }
Matrix-wise iteration
MatrixView 0: { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }
MatrixView 1: { 2.0, 2.0, 2.0, 2.0, 2.0, 2.0 }
MatrixView 2: { 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 }
MatrixView 3: { 4.0, 4.0, 4.0, 4.0, 4.0, 4.0 }
- ← Previous
Artificial Neural Network Example v2 - Next →
Common Mistake