package org.openlca.core.matrix.io; import java.io.File; import java.io.FileInputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.nio.file.Files; import org.openlca.core.math.IMatrix; import org.openlca.core.math.IMatrixFactory; /** * Reads a binary Matlab file (version?) with a matrix with 64 bit floating * point numbers into a IMatrix. */ public class MatBinMatrixReader { /** * Number of bytes per number in matrix (8 bytes == double). */ private final int SIZE = 8; private final File file; private final IMatrixFactory<?> factory; private boolean useStreaming = false; private ByteBuffer buffer; public MatBinMatrixReader(File file, IMatrixFactory<?> factory) { this.file = file; this.factory = factory; buffer = ByteBuffer.allocate(SIZE); buffer.order(ByteOrder.LITTLE_ENDIAN); } public void setUseStreaming(boolean useStreaming) { this.useStreaming = useStreaming; } public IMatrix read() throws Exception { if (useStreaming) return readViaStreaming(); else return readInMemory(); } private IMatrix readInMemory() throws Exception { byte[] bytes = Files.readAllBytes(file.toPath()); checkFormat(bytes); int rows = (int) readNumber(3, bytes); int cols = (int) readNumber(4, bytes); IMatrix matrix = factory.create(rows, cols); for (int col = 0; col < cols; col++) { for (int row = 0; row < rows; row++) { int offset = 5 + row + col * matrix.rows(); double val = readNumber(offset, bytes); matrix.set(row, col, val); } } return matrix; } private IMatrix readViaStreaming() throws Exception { try (FileInputStream fis = new FileInputStream(file); FileChannel channel = fis.getChannel()) { checkFormat(channel); int rows = (int) readNumber(channel); // pos = 3 int cols = (int) readNumber(channel); // pos = 4 IMatrix matrix = factory.create(rows, cols); for (int col = 0; col < cols; col++) { for (int row = 0; row < rows; row++) { double val = readNumber(channel); matrix.set(row, col, val); } } return matrix; } } private void checkFormat(FileChannel channel) throws Exception { int dims = (int) readNumber(channel); // pos = 0 int isSparse = (int) readNumber(channel); // pos = 1 int size = (int) readNumber(channel); // pos = 2 checkFormat(dims, isSparse, size); } private void checkFormat(byte[] bytes) throws Exception { int dims = (int) readNumber(0, bytes); int isSparse = (int) readNumber(1, bytes); int size = (int) readNumber(2, bytes); checkFormat(dims, isSparse, size); } private void checkFormat(int dims, int isSparse, int size) { // number of dimensions if (dims != 2) throw new IllegalStateException("Number of dimensions must be 2; " + "but was " + dims); // sparse flag (1 if sparse) if (isSparse != 0) throw new IllegalStateException("Can only read dense matrices; " + "but the matrix is indicated as sparse " + isSparse); // number precision in bytes if (size != SIZE) throw new IllegalStateException( "The number precision does not match; " + "expected " + SIZE + " byte numbers; but precision is " + size + " bytes"); } private double readNumber(int i, byte[] bytes) { buffer.put(bytes, i * SIZE, SIZE); buffer.flip(); double d = buffer.getDouble(); buffer.compact(); return d; } private double readNumber(FileChannel channel) throws Exception { channel.read(buffer); buffer.flip(); double d = buffer.getDouble(); buffer.compact(); return d; } }