Skip to content

Commit af81dec

Browse files
committed
WIP #982 Matlab file read/write for double and boolean matrices
Use library: - https://github.com/HebiRobotics/MFL
1 parent 1d13711 commit af81dec

File tree

10 files changed

+391
-35
lines changed

10 files changed

+391
-35
lines changed

symja_android_library/matheclipse-core/pom.xml

+4
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@
107107
<groupId>de.labathome</groupId>
108108
<artifactId>AdaptiveQuadrature</artifactId>
109109
</dependency>
110+
<dependency>
111+
<groupId>us.hebi.matlab.mat</groupId>
112+
<artifactId>mfl-core</artifactId>
113+
</dependency>
110114
<dependency>
111115
<groupId>org.apache.logging.log4j</groupId>
112116
<artifactId>log4j-api</artifactId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.matheclipse.core.convert.matlab;
2+
3+
import static us.hebi.matlab.mat.format.Mat5WriteUtil.computeArrayHeaderSize;
4+
import static us.hebi.matlab.mat.format.Mat5WriteUtil.writeArrayHeader;
5+
import static us.hebi.matlab.mat.format.Mat5WriteUtil.writeMatrixTag;
6+
import java.io.IOException;
7+
import org.hipparchus.linear.AnyMatrix;
8+
import us.hebi.matlab.mat.format.Mat5;
9+
import us.hebi.matlab.mat.format.Mat5Serializable;
10+
import us.hebi.matlab.mat.types.AbstractArray;
11+
import us.hebi.matlab.mat.types.Sink;
12+
13+
/**
14+
* Serializes a Symja Matrix into a MAT 5 file that can be read by MATLAB
15+
*
16+
* Note that implementing 'Mat5Attributes' lets us get around the overhead of implementing the
17+
* Matrix / Sparse interfaces, or alternatively writing the header manually.
18+
*
19+
*/
20+
abstract class AbstractMatrixWrapper<M extends AnyMatrix> extends AbstractArray
21+
implements Mat5Serializable, Mat5Serializable.Mat5Attributes {
22+
23+
protected final M matrix;
24+
25+
protected AbstractMatrixWrapper(M matrix) {
26+
super(Mat5.dims(matrix.getRowDimension(), matrix.getColumnDimension()));
27+
this.matrix = matrix;
28+
}
29+
30+
@Override
31+
public void close() throws IOException {
32+
}
33+
34+
@Override
35+
public int[] getDimensions() {
36+
dims[0] = matrix.getRowDimension();
37+
dims[1] = matrix.getColumnDimension();
38+
return dims;
39+
}
40+
41+
protected abstract int getMat5DataSize();
42+
43+
@Override
44+
public int getMat5Size(String name) {
45+
return Mat5.MATRIX_TAG_SIZE
46+
+ computeArrayHeaderSize(name, this)
47+
+ getMat5DataSize();
48+
}
49+
50+
@Override
51+
public int getNzMax() {
52+
return 0;
53+
}
54+
55+
@Override
56+
public boolean isComplex() {
57+
return false;
58+
}
59+
60+
@Override
61+
public boolean isLogical() {
62+
return false;
63+
}
64+
65+
@Override
66+
protected boolean subEqualsGuaranteedSameClass(Object otherGuaranteedSameClass) {
67+
AnyMatrixWrapper other = (AnyMatrixWrapper) otherGuaranteedSameClass;
68+
return other.matrix.equals(matrix);
69+
}
70+
71+
@Override
72+
protected int subHashCode() {
73+
return matrix.hashCode();
74+
}
75+
76+
@Override
77+
public void writeMat5(String name, boolean isGlobal, Sink sink) throws IOException {
78+
writeMatrixTag(name, this, sink);
79+
writeArrayHeader(name, isGlobal, this, sink);
80+
writeMat5Data(sink);
81+
}
82+
83+
/**
84+
* Writes data part in column-major order
85+
*
86+
* @param sink
87+
* @throws IOException
88+
*/
89+
protected abstract void writeMat5Data(Sink sink) throws IOException;
90+
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.matheclipse.core.convert.matlab;
2+
3+
import java.io.IOException;
4+
import java.io.NotSerializableException;
5+
import org.hipparchus.linear.AnyMatrix;
6+
import org.hipparchus.linear.RealMatrix;
7+
import org.matheclipse.core.eval.exception.SymjaMathException;
8+
import org.matheclipse.core.expression.ASTRealMatrix;
9+
import org.matheclipse.core.interfaces.IAST;
10+
import us.hebi.matlab.mat.format.Mat5Type;
11+
import us.hebi.matlab.mat.types.MatlabType;
12+
import us.hebi.matlab.mat.types.Sink;
13+
14+
class AnyMatrixWrapper extends AbstractMatrixWrapper<AnyMatrix> {
15+
16+
AnyMatrixWrapper(AnyMatrix matrix) {
17+
super(matrix);
18+
}
19+
20+
@Override
21+
protected int getMat5DataSize() {
22+
return Mat5Type.Double
23+
.computeSerializedSize(matrix.getRowDimension() * matrix.getColumnDimension());
24+
}
25+
26+
@Override
27+
public MatlabType getType() {
28+
return MatlabType.Double;
29+
}
30+
31+
@Override
32+
protected void writeMat5Data(Sink sink) throws IOException {
33+
// Real data in column major format
34+
if (matrix instanceof ASTRealMatrix) {
35+
ASTRealMatrix astMatrix = (ASTRealMatrix) matrix;
36+
RealMatrix realMatrix = astMatrix.getRealMatrix();
37+
int rows = realMatrix.getRowDimension();
38+
int columns = realMatrix.getColumnDimension();
39+
int getNumElements = rows * columns;
40+
41+
Mat5Type.Double.writeTag(getNumElements, sink);
42+
for (int col = 0; col < rows; col++) {
43+
for (int row = 0; row < columns; row++) {
44+
sink.writeDouble(realMatrix.getEntry(row, col));
45+
}
46+
}
47+
Mat5Type.Double.writePadding(getNumElements, sink);
48+
} else if (matrix instanceof IAST) {
49+
try {
50+
IAST astMatrix = (IAST) matrix;
51+
int rows = astMatrix.getRowDimension();
52+
int columns = astMatrix.getColumnDimension();
53+
int getNumElements = rows * columns;
54+
55+
Mat5Type.Double.writeTag(getNumElements, sink);
56+
for (int col = 0; col < rows; col++) {
57+
for (int row = 0; row < columns; row++) {
58+
sink.writeDouble(astMatrix.getPart(row + 1, col + 1).evalf());
59+
}
60+
}
61+
Mat5Type.Double.writePadding(getNumElements, sink);
62+
} catch (SymjaMathException sme) {
63+
throw new NotSerializableException();
64+
}
65+
}
66+
}
67+
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package org.matheclipse.core.convert.matlab;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.nio.ByteBuffer;
6+
import org.hipparchus.linear.AnyMatrix;
7+
import org.hipparchus.linear.Array2DRowRealMatrix;
8+
import org.hipparchus.linear.RealMatrix;
9+
import org.matheclipse.core.expression.ASTRealMatrix;
10+
import org.matheclipse.core.expression.F;
11+
import org.matheclipse.core.interfaces.IAST;
12+
import org.matheclipse.core.interfaces.IASTAppendable;
13+
import org.matheclipse.core.interfaces.IExpr;
14+
import us.hebi.matlab.mat.format.Mat5;
15+
import us.hebi.matlab.mat.format.Mat5File;
16+
import us.hebi.matlab.mat.types.AbstractMatrixBase;
17+
import us.hebi.matlab.mat.types.Array;
18+
import us.hebi.matlab.mat.types.MatFile;
19+
import us.hebi.matlab.mat.types.MatlabType;
20+
import us.hebi.matlab.mat.types.Matrix;
21+
import us.hebi.matlab.mat.types.Source;
22+
import us.hebi.matlab.mat.types.Sources;
23+
24+
/**
25+
* Matlab file format conversion to Symja
26+
*/
27+
public class Mat5Symja {
28+
29+
private static AnyMatrix convertToAnyMatrix(AbstractMatrixBase input,
30+
Class<? extends AnyMatrix> clazz) {
31+
final int rows = input.getNumRows();
32+
final int cols = input.getNumCols();
33+
if (clazz.isAssignableFrom(ASTRealMatrix.class)) {
34+
RealMatrix realMatrix = convertToArray2DRowRealMatrix(input, rows, cols);
35+
return new ASTRealMatrix(realMatrix, false);
36+
} else if (clazz.isAssignableFrom(IAST.class)) {
37+
IASTAppendable astMatrix = F.ListAlloc(rows);
38+
for (int i = 0; i < rows; i++) {
39+
astMatrix.append(F.ListAlloc(cols));
40+
}
41+
for (int col = 0; col < cols; col++) {
42+
for (int row = 0; row < rows; row++) {
43+
astMatrix.setPart(input.getDouble(row, col), row, col);
44+
}
45+
}
46+
} else if (clazz.isAssignableFrom(RealMatrix.class)) {
47+
return convertToArray2DRowRealMatrix(input, rows, cols);
48+
}
49+
return F.NIL;
50+
}
51+
52+
private static RealMatrix convertToArray2DRowRealMatrix(Matrix input, final int rows,
53+
final int cols) {
54+
RealMatrix realMatrix = new Array2DRowRealMatrix(rows, cols);
55+
for (int col = 0; col < cols; col++) {
56+
for (int row = 0; row < rows; row++) {
57+
realMatrix.setEntry(row, col, input.getDouble(row, col));
58+
}
59+
}
60+
return realMatrix;
61+
}
62+
63+
public static IAST getTensor(AbstractMatrixBase baseMatrix) {
64+
int[] dimensions = baseMatrix.getDimensions();
65+
if (dimensions.length == 2) {
66+
if (baseMatrix.getType() == MatlabType.Double) {
67+
return (ASTRealMatrix) Mat5Symja.convertToAnyMatrix(baseMatrix, ASTRealMatrix.class);
68+
}
69+
if (baseMatrix.getType() == MatlabType.Sparse) {
70+
return (IAST) Mat5Symja.convertToAnyMatrix(baseMatrix, IAST.class);
71+
}
72+
}
73+
if (baseMatrix.getType() == MatlabType.Sparse) {
74+
return F.NIL;
75+
// int[] indices = new int[dimensions.length];
76+
// final int size = dimensions[0];
77+
// ISparseArray sparse =
78+
// F.sparseArray(F.List(F.Rule(F.List(1, 2, 3), F.b), F.Rule(F.List(1, 4, 5), F.a)));
79+
// IASTAppendable result = F.ListAlloc();
80+
// for (int i = 0; i < size; i++) {
81+
// indices[0] = i;
82+
// getSparseRecursive(baseMatrix, dimensions, indices, 1, result);
83+
// }
84+
// return result;
85+
}
86+
int[] indices = new int[dimensions.length];
87+
final int size = dimensions[0];
88+
IASTAppendable result = F.ListAlloc(size);
89+
for (int i = 0; i < size; i++) {
90+
indices[0] = i;
91+
getTensorRecursive(baseMatrix, dimensions, indices, 1, result);
92+
}
93+
return result;
94+
}
95+
96+
private static void getSparseRecursive(AbstractMatrixBase baseMatrix, int[] dimensions,
97+
int[] indices, int indexCounter, IASTAppendable result) {
98+
int newCounter = indexCounter + 1;
99+
if (indexCounter == dimensions.length) {
100+
MatlabType type = baseMatrix.getType();
101+
switch (type) {
102+
case UInt8:
103+
result.append(baseMatrix.getBoolean(indices));
104+
return;
105+
case Double:
106+
result.append(baseMatrix.getDouble(indices));
107+
return;
108+
case Single:
109+
result.append(baseMatrix.getFloat(indices));
110+
return;
111+
case Sparse:
112+
double d = baseMatrix.getDouble(indices);
113+
result.append(d);
114+
return;
115+
}
116+
return;
117+
}
118+
final int size = dimensions[indexCounter];
119+
IASTAppendable subRow = F.ListAlloc();
120+
for (int i = 0; i < size; i++) {
121+
indices[indexCounter] = i;
122+
getTensorRecursive(baseMatrix, dimensions, indices, newCounter, subRow);
123+
}
124+
result.append(subRow);
125+
}
126+
127+
private static void getTensorRecursive(AbstractMatrixBase baseMatrix, int[] dimensions,
128+
int[] indices, int indexCounter, IASTAppendable result) {
129+
int newCounter = indexCounter + 1;
130+
if (indexCounter == dimensions.length) {
131+
MatlabType type = baseMatrix.getType();
132+
switch (type) {
133+
case UInt8:
134+
result.append(baseMatrix.getBoolean(indices));
135+
return;
136+
case Double:
137+
result.append(baseMatrix.getDouble(indices));
138+
return;
139+
case Single:
140+
result.append(baseMatrix.getFloat(indices));
141+
return;
142+
}
143+
return;
144+
}
145+
final int size = dimensions[indexCounter];
146+
IASTAppendable subRow = F.ListAlloc(size);
147+
for (int i = 0; i < size; i++) {
148+
indices[indexCounter] = i;
149+
getTensorRecursive(baseMatrix, dimensions, indices, newCounter, subRow);
150+
}
151+
result.append(subRow);
152+
}
153+
154+
public static IExpr importMAT(InputStream inputStream, String inputName)
155+
throws IOException, AssertionError {
156+
ByteBuffer buffer = ByteBuffer.allocate(inputStream.available());
157+
int bytes = inputStream.read(buffer.array());
158+
if (bytes != buffer.array().length) {
159+
throw new AssertionError("Could not read full contents of " + inputName);
160+
}
161+
try (Source source = Sources.wrap(buffer)) {
162+
Mat5File mat = Mat5.newReader(source)//
163+
.setReducedHeader(false)//
164+
.readMat();
165+
System.out.println(mat.toString());
166+
for (MatFile.Entry entry : mat.getEntries()) {
167+
// String name = entry.getName();
168+
Array value = entry.getValue();
169+
if (value instanceof AbstractMatrixBase) {
170+
return getTensor((AbstractMatrixBase) value);
171+
}
172+
}
173+
}
174+
return F.NIL;
175+
}
176+
}

symja_android_library/matheclipse-io/pom.xml

-4
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,6 @@
107107
<groupId>org.apache.arrow</groupId>
108108
<artifactId>arrow-memory-netty</artifactId>
109109
</dependency>
110-
<dependency>
111-
<groupId>us.hebi.matlab.mat</groupId>
112-
<artifactId>mfl-core</artifactId>
113-
</dependency>
114110
<!-- logging dependencies -->
115111

116112
<dependency>

0 commit comments

Comments
 (0)