From 2b177f935a9306dd1c3920b49e1f578514a7a991 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 22 Jul 2014 16:09:28 +0200 Subject: [PATCH] Data: Matlab: static MatlabWriter api design (consistent with MatlabReader) --- src/Data/Matlab/Formatter.cs | 732 +++++++++++++++++ src/Data/Matlab/Matlab.csproj | 1 + src/Data/Matlab/MatlabMatrix.cs | 14 +- src/Data/Matlab/MatlabReader.cs | 18 +- src/Data/Matlab/MatlabWriter.cs | 775 +----------------- src/Data/Matlab/Parser.cs | 143 ++-- src/DataUnitTests/Matlab/MatlabWriterTests.cs | 134 +-- 7 files changed, 899 insertions(+), 918 deletions(-) create mode 100644 src/Data/Matlab/Formatter.cs diff --git a/src/Data/Matlab/Formatter.cs b/src/Data/Matlab/Formatter.cs new file mode 100644 index 00000000..28b5cfad --- /dev/null +++ b/src/Data/Matlab/Formatter.cs @@ -0,0 +1,732 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// http://mathnetnumerics.codeplex.com +// +// Copyright (c) 2009-2014 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Numerics; +using System.Text; +using MathNet.Numerics.LinearAlgebra; +using MathNet.Numerics.LinearAlgebra.Storage; +using MathNet.Numerics.Properties; + +namespace MathNet.Numerics.Data.Matlab +{ + /// + /// Format a MATLAB file. + /// + internal static class Formatter + { + /// + /// The file header value + /// + const string HeaderText = "MATLAB 5.0 MAT-file, Platform: .NET 4 - Math.NET Numerics, Created on: "; + + /// + /// The length of the header text. + /// + const int HeaderTextLength = 116; + + /// + /// Format a matrix block byte array + /// + internal static MatlabMatrix FormatMatrix(Matrix matrix, string name) + where T : struct, IEquatable, IFormattable + { + if (matrix == null) + { + throw new ArgumentNullException("matrix"); + } + + if (string.IsNullOrEmpty(name)) + { + throw new ArgumentException(Resources.StringNullOrEmpty, "name"); + } + + if (name.IndexOf(' ') > -1) + { + throw new ArgumentException(string.Format(Resources.NameCannotContainASpace, name), "name"); + } + + if (typeof(T) == typeof(double)) + { + var sparse = matrix as LinearAlgebra.Double.SparseMatrix; + return sparse != null + ? GetSparseDataArray(sparse, name) + : GetDenseDataArray((LinearAlgebra.Double.Matrix)(object)matrix, name); + } + + if (typeof(T) == typeof(float)) + { + var sparse = matrix as LinearAlgebra.Single.SparseMatrix; + return sparse != null + ? GetSparseDataArray(sparse, name) + : GetDenseDataArray((LinearAlgebra.Single.Matrix)(object)matrix, name); + } + + if (typeof(T) == typeof(Complex)) + { + var sparse = matrix as LinearAlgebra.Complex.SparseMatrix; + return sparse != null + ? GetSparseDataArray(sparse, name) + : GetDenseDataArray((LinearAlgebra.Complex.Matrix)(object)matrix, name); + } + + if (typeof(T) == typeof(Complex32)) + { + var sparse = matrix as LinearAlgebra.Complex32.SparseMatrix; + return sparse != null + ? GetSparseDataArray(sparse, name) + : GetDenseDataArray((LinearAlgebra.Complex32.Matrix)(object)matrix, name); + } + + throw new NotSupportedException(); + } + + /// + /// Writes all matrix blocks to a stream. + /// + internal static void FormatFile(Stream stream, IEnumerable matrices) + { + using (var buffer = new BufferedStream(stream)) + using (var writer = new BinaryWriter(buffer)) + { + WriteHeader(writer); + + foreach (var matrix in matrices) + { + // write data type + writer.Write((int)DataType.Compressed); + + WriteCompressedData(writer, matrix.Data); + } + + writer.Flush(); + writer.Close(); + } + } + + /// + /// Writes the matrix tag and name. + /// + /// The writer we are using. + /// The array class we are writing. + /// if set to true if this a complex matrix. + /// The name of the matrix. + /// The number of rows. + /// The columns of columns. + /// The maximum number of non-zero elements. + static void WriteMatrixTagAndName(BinaryWriter writer, ArrayClass arrayClass, bool isComplex, + string name, int rows, int columns, int nzmax) + { + writer.Write((int)DataType.Matrix); + + // add place holder for data size + writer.Write(0); + + // write flag, data type and size + writer.Write((int)DataType.UInt32); + writer.Write(8); + + // write array class and flags + writer.Write((byte)arrayClass); + if (isComplex) + { + writer.Write((byte)ArrayFlags.Complex); + } + else + { + writer.Write((byte)0); + } + + writer.Write((short)0); + writer.Write(nzmax); + + // write dimensions + writer.Write((int)DataType.Int32); + writer.Write(8); + writer.Write(rows); + writer.Write(columns); + + var nameBytes = Encoding.ASCII.GetBytes(name); + + // write name + if (nameBytes.Length > 4) + { + writer.Write((int)DataType.Int8); + writer.Write(nameBytes.Length); + writer.Write(nameBytes); + var pad = 8 - (nameBytes.Length%8); + PadData(writer, pad); + } + else + { + writer.Write((short)DataType.Int8); + writer.Write((short)nameBytes.Length); + writer.Write(nameBytes); + PadData(writer, 4 - nameBytes.Length); + } + } + + /// + /// Gets the dense data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetDenseDataArray(Matrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + WriteMatrixTagAndName(writer, ArrayClass.Double, false, name, matrix.RowCount, matrix.ColumnCount, 0); + + // write data + writer.Write((int)DataType.Double); + writer.Write(matrix.RowCount*matrix.ColumnCount*8); + + for (var j = 0; j < matrix.ColumnCount; j++) + { + var column = matrix.Column(j); + foreach (var value in column) + { + writer.Write(value); + } + } + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + + } + + /// + /// Gets the dense data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetDenseDataArray(Matrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + WriteMatrixTagAndName(writer, ArrayClass.Single, false, name, matrix.RowCount, matrix.ColumnCount, 0); + + // write data + int size = matrix.RowCount*matrix.ColumnCount*4; + writer.Write((int)DataType.Single); + writer.Write(size); + + for (var j = 0; j < matrix.ColumnCount; j++) + { + var column = matrix.Column(j); + foreach (var value in column) + { + writer.Write(value); + } + } + + PadData(writer, size%8); + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Gets the dense data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetDenseDataArray(Matrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + WriteMatrixTagAndName(writer, ArrayClass.Double, true, name, matrix.RowCount, matrix.ColumnCount, 0); + + // write data + int size = matrix.RowCount*matrix.ColumnCount*8; + writer.Write((int)DataType.Double); + writer.Write(size); + + for (var j = 0; j < matrix.ColumnCount; j++) + { + var column = matrix.Column(j); + foreach (var value in column) + { + writer.Write(value.Real); + } + } + + writer.Write((int)DataType.Double); + writer.Write(size); + + for (var j = 0; j < matrix.ColumnCount; j++) + { + var column = matrix.Column(j); + foreach (var value in column) + { + writer.Write(value.Imaginary); + } + } + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Gets the dense data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetDenseDataArray(Matrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + WriteMatrixTagAndName(writer, ArrayClass.Single, true, name, matrix.RowCount, matrix.ColumnCount, 0); + + // write data + int size = matrix.RowCount*matrix.ColumnCount*4; + writer.Write((int)DataType.Single); + writer.Write(size); + + for (var j = 0; j < matrix.ColumnCount; j++) + { + var column = matrix.Column(j); + foreach (var value in column) + { + writer.Write(value.Real); + } + } + + PadData(writer, size%8); + + writer.Write((int)DataType.Single); + writer.Write(size); + + for (var j = 0; j < matrix.ColumnCount; j++) + { + var column = matrix.Column(j); + foreach (var value in column) + { + writer.Write(value.Real); + } + } + + PadData(writer, size%8); + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Gets the sparse data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetSparseDataArray(LinearAlgebra.Double.SparseMatrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + var nzmax = matrix.NonZerosCount; + WriteMatrixTagAndName(writer, ArrayClass.Sparse, false, name, matrix.RowCount, matrix.ColumnCount, nzmax); + + // write ir + writer.Write((int)DataType.Int32); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item1); + } + } + + // add pad if needed + if (nzmax%2 == 1) + { + writer.Write(0); + } + + // write jc + writer.Write((int)DataType.Int32); + writer.Write((matrix.ColumnCount + 1)*4); + writer.Write(0); + var count = 0; + foreach (var column in matrix.EnumerateColumns()) + { + count += ((SparseVectorStorage)column.Storage).ValueCount; + writer.Write(count); + } + + // add pad if needed + if (matrix.ColumnCount%2 == 0) + { + writer.Write(0); + } + + // write data + writer.Write((int)DataType.Double); + writer.Write(nzmax*8); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item2); + } + } + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Gets the sparse data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetSparseDataArray(LinearAlgebra.Single.SparseMatrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + var nzmax = matrix.NonZerosCount; + WriteMatrixTagAndName(writer, ArrayClass.Sparse, false, name, matrix.RowCount, matrix.ColumnCount, + nzmax); + + // write ir + writer.Write((int)DataType.Int32); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item1); + } + } + + // add pad if needed + if (nzmax%2 == 1) + { + writer.Write(0); + } + + // write jc + writer.Write((int)DataType.Int32); + writer.Write((matrix.ColumnCount + 1)*4); + writer.Write(0); + var count = 0; + foreach (var column in matrix.EnumerateColumns()) + { + count += ((SparseVectorStorage)column.Storage).ValueCount; + writer.Write(count); + } + + // add pad if needed + if (matrix.ColumnCount%2 == 0) + { + writer.Write(0); + } + + // write data + writer.Write((int)DataType.Single); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item2); + } + } + + var pad = nzmax*4%8; + PadData(writer, pad); + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Gets the sparse data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetSparseDataArray(LinearAlgebra.Complex.SparseMatrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + var nzmax = matrix.NonZerosCount; + WriteMatrixTagAndName(writer, ArrayClass.Sparse, true, name, matrix.RowCount, matrix.ColumnCount, + nzmax); + + // write ir + writer.Write((int)DataType.Int32); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item1); + } + } + + // add pad if needed + if (nzmax%2 == 1) + { + writer.Write(0); + } + + // write jc + writer.Write((int)DataType.Int32); + writer.Write((matrix.ColumnCount + 1)*4); + writer.Write(0); + var count = 0; + foreach (var column in matrix.EnumerateColumns()) + { + count += ((SparseVectorStorage)column.Storage).ValueCount; + writer.Write(count); + } + + // add pad if needed + if (matrix.ColumnCount%2 == 0) + { + writer.Write(0); + } + + // write data + writer.Write((int)DataType.Double); + writer.Write(nzmax*8); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item2.Real); + } + } + + writer.Write((int)DataType.Double); + writer.Write(nzmax*8); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item2.Imaginary); + } + } + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Gets the sparse data array. + /// + /// The matrix to get the data from. + /// The name of the matrix. + /// The matrix data as an array. + static MatlabMatrix GetSparseDataArray(LinearAlgebra.Complex32.SparseMatrix matrix, string name) + { + using (var stream = new MemoryStream()) + using (var writer = new BinaryWriter(stream)) + { + var nzmax = matrix.NonZerosCount; + WriteMatrixTagAndName(writer, ArrayClass.Sparse, true, name, matrix.RowCount, matrix.ColumnCount, + nzmax); + + // write ir + writer.Write((int)DataType.Int32); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item1); + } + } + + // add pad if needed + if (nzmax%2 == 1) + { + writer.Write(0); + } + + // write jc + writer.Write((int)DataType.Int32); + writer.Write((matrix.ColumnCount + 1)*4); + writer.Write(0); + var count = 0; + foreach (var column in matrix.EnumerateColumns()) + { + count += ((SparseVectorStorage)column.Storage).ValueCount; + writer.Write(count); + } + + // add pad if needed + if (matrix.ColumnCount%2 == 0) + { + writer.Write(0); + } + + // write data + writer.Write((int)DataType.Single); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item2.Real); + } + } + + var pad = nzmax*4%8; + PadData(writer, pad); + + writer.Write((int)DataType.Single); + writer.Write(nzmax*4); + + foreach (var column in matrix.EnumerateColumns()) + { + foreach (var row in column.EnumerateNonZeroIndexed()) + { + writer.Write(row.Item2.Imaginary); + } + } + + PadData(writer, pad); + + writer.Flush(); + return new MatlabMatrix(name, stream.ToArray()); + } + } + + /// + /// Writes the file header. + /// + static void WriteHeader(BinaryWriter writer) + { + var header = Encoding.ASCII.GetBytes(HeaderText + DateTime.Now.ToString(Resources.MatlabDateHeaderFormat)); + writer.Write(header); + PadData(writer, HeaderTextLength - header.Length + 8, 32); + + // write version + writer.Write((short)0x100); + + // write little endian indicator + writer.Write((byte)0x49); + writer.Write((byte)0x4D); + } + + /// + /// Pads the data with the given byte. + /// + /// Where to write the pad values. + /// The number of bytes to pad. + /// What value to pad with. + static void PadData(BinaryWriter writer, int bytes, byte pad = (byte)0) + { + for (var i = 0; i < bytes; i++) + { + writer.Write(pad); + } + } + + /// + /// Writes the compressed data. + /// + /// The data to write. + static void WriteCompressedData(BinaryWriter writer, byte[] data) + { + // fill in data size + var size = BitConverter.GetBytes(data.Length); + data[4] = size[0]; + data[5] = size[1]; + data[6] = size[2]; + data[7] = size[3]; + + // compress data + var compressedData = CompressData(data); + + // write compressed data to file + writer.Write(compressedData.Length); + writer.Write(compressedData); + } + + /// + /// Compresses the data array. + /// + /// The data to compress. + /// The compressed data. + static byte[] CompressData(byte[] data) + { + var adler = BitConverter.GetBytes(Adler32.Compute(data)); + using (var compressedStream = new MemoryStream()) + { + compressedStream.WriteByte(0x58); + compressedStream.WriteByte(0x85); + using (var outputStream = new DeflateStream(compressedStream, CompressionMode.Compress, true)) + { + outputStream.Write(data, 0, data.Length); + } + + compressedStream.WriteByte(adler[3]); + compressedStream.WriteByte(adler[2]); + compressedStream.WriteByte(adler[1]); + compressedStream.WriteByte(adler[0]); + return compressedStream.ToArray(); + } + } + } +} \ No newline at end of file diff --git a/src/Data/Matlab/Matlab.csproj b/src/Data/Matlab/Matlab.csproj index 08710462..76631e7a 100644 --- a/src/Data/Matlab/Matlab.csproj +++ b/src/Data/Matlab/Matlab.csproj @@ -52,6 +52,7 @@ + diff --git a/src/Data/Matlab/MatlabMatrix.cs b/src/Data/Matlab/MatlabMatrix.cs index 00f99c04..0d25634d 100644 --- a/src/Data/Matlab/MatlabMatrix.cs +++ b/src/Data/Matlab/MatlabMatrix.cs @@ -37,18 +37,16 @@ namespace MathNet.Numerics.Data.Matlab /// Name of the matrix public string Name { get; private set; } - /// Matrix size in bytes - public int Size { get; private set; } - - /// Number of dimensions - public int Dimensions { get; private set; } + /// Size of the packed matrix in bytes + public int ByteSize + { + get { return Data.Length; } + } - internal MatlabMatrix(string name, int size, int dimensions, byte[] data) + internal MatlabMatrix(string name, byte[] data) { Data = data; Name = name; - Size = size; - Dimensions = dimensions; } } } diff --git a/src/Data/Matlab/MatlabReader.cs b/src/Data/Matlab/MatlabReader.cs index 73f95ea1..5ad36534 100644 --- a/src/Data/Matlab/MatlabReader.cs +++ b/src/Data/Matlab/MatlabReader.cs @@ -37,7 +37,7 @@ using MathNet.Numerics.LinearAlgebra; namespace MathNet.Numerics.Data.Matlab { /// - /// Creates matrices from MATLAB files. + /// Creates matrices from MATLAB 5 files. /// public static class MatlabReader { @@ -46,7 +46,7 @@ namespace MathNet.Numerics.Data.Matlab /// public static List List(Stream stream) { - return Parser.ParseAll(stream); + return Parser.ParseFile(stream); } /// @@ -56,7 +56,7 @@ namespace MathNet.Numerics.Data.Matlab { using (var stream = File.OpenRead(filePath)) { - return Parser.ParseAll(stream); + return List(stream); } } @@ -67,7 +67,7 @@ namespace MathNet.Numerics.Data.Matlab public static Matrix Unpack(MatlabMatrix matrixData) where TDataType : struct, IEquatable, IFormattable { - return Parser.ReadMatrixBlock(matrixData.Data); + return Parser.ParseMatrix(matrixData.Data); } /// @@ -77,11 +77,11 @@ namespace MathNet.Numerics.Data.Matlab public static Matrix Read(Stream stream, string matrixName = null) where TDataType : struct, IEquatable, IFormattable { - var matrices = Parser.ParseAll(stream); + var matrices = List(stream); if (string.IsNullOrEmpty(matrixName)) { - return Parser.ReadMatrixBlock(matrices.First().Data); + return Unpack(matrices.First()); } var matrix = matrices.Find(m => m.Name == matrixName); @@ -90,7 +90,7 @@ namespace MathNet.Numerics.Data.Matlab throw new KeyNotFoundException("Matrix with the provided name was not found."); } - return Parser.ReadMatrixBlock(matrix.Data); + return Unpack(matrix); } /// @@ -114,9 +114,9 @@ namespace MathNet.Numerics.Data.Matlab where TDataType : struct, IEquatable, IFormattable { var names = new HashSet(matrixNames); - return Parser.ParseAll(stream) + return List(stream) .Where(m => names.Count == 0 || names.Contains(m.Name)) - .ToDictionary(m => m.Name, m => Parser.ReadMatrixBlock(m.Data)); + .ToDictionary(m => m.Name, Unpack); } /// diff --git a/src/Data/Matlab/MatlabWriter.cs b/src/Data/Matlab/MatlabWriter.cs index ab43ecd5..a12359ee 100644 --- a/src/Data/Matlab/MatlabWriter.cs +++ b/src/Data/Matlab/MatlabWriter.cs @@ -31,789 +31,86 @@ using System; using System.Collections.Generic; using System.IO; -using System.IO.Compression; -using System.Numerics; -using System.Text; +using System.Linq; using MathNet.Numerics.LinearAlgebra; -using MathNet.Numerics.LinearAlgebra.Storage; -using MathNet.Numerics.Properties; namespace MathNet.Numerics.Data.Matlab { /// - /// Writes matrices to a MATLAB file. + /// Writes matrices to a MATLAB 5 file. /// - public class MatlabWriter : IDisposable + public static class MatlabWriter { - /// - /// The file header value - /// - const string HeaderText = "MATLAB 5.0 MAT-file, Platform: .NET 4 - Math.NET Numerics, Created on: "; - - /// - /// The length of the header text. - /// - const int HeaderTextLength = 116; - - /// - /// Have we written the header yet. - /// - bool _headerWritten; - - /// - /// The binary writer to write to. - /// - BinaryWriter _writer; - - /// - /// Initializes a new instance of the class. - /// - /// The name of the MATLAB file to save the matrices to. - public MatlabWriter(string filename) + public static void Store(Stream stream, IEnumerable matrices) { - if (string.IsNullOrEmpty(filename)) - { - throw new ArgumentException(Resources.StringNullOrEmpty, "filename"); - } - - _writer = - new BinaryWriter( - new BufferedStream(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))); + Formatter.FormatFile(stream, matrices); } - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() + public static void Store(string filePath, IEnumerable matrices) { - if (_writer != null) + using (var stream = File.OpenWrite(filePath)) { - _writer.Flush(); - _writer.Close(); - _writer = null; + Store(stream, matrices); } } - /// - /// Writes the given to the file. - /// - /// The matrix to write. - /// The name of the matrix to store in the file. - /// If either or is null. /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. - public void WriteMatrix(Matrix matrix, string name) + public static MatlabMatrix Pack(Matrix matrix, string matrixName) where TDataType : struct, IEquatable, IFormattable { - if (matrix == null) - { - throw new ArgumentNullException("matrix"); - } - - if (string.IsNullOrEmpty(name)) - { - throw new ArgumentException(Resources.StringNullOrEmpty, "name"); - } - - if (name.IndexOf(' ') > -1) - { - throw new ArgumentException(string.Format(Resources.NameCannotContainASpace, name), "name"); - } - - if (!_headerWritten) - { - WriteHeader(); - _headerWritten = true; - } - - // write data type - _writer.Write((int)DataType.Compressed); - - byte[] data; - - if (typeof (TDataType) == typeof (double)) - { - var o = matrix as LinearAlgebra.Double.SparseMatrix; - data = o != null - ? GetSparseDataArray((LinearAlgebra.Double.SparseMatrix)(object)matrix, name) - : GetDenseDataArray((LinearAlgebra.Double.Matrix)(object)matrix, name); - } - else if (typeof (TDataType) == typeof (float)) - { - var o = matrix as LinearAlgebra.Single.SparseMatrix; - data = o != null - ? GetSparseDataArray((LinearAlgebra.Single.SparseMatrix)(object)matrix, name) - : GetDenseDataArray((LinearAlgebra.Single.Matrix)(object)matrix, name); - } - else if (typeof (TDataType) == typeof (Complex)) - { - var o = matrix as LinearAlgebra.Complex.SparseMatrix; - data = o != null - ? GetSparseDataArray((LinearAlgebra.Complex.SparseMatrix)(object)matrix, name) - : GetDenseDataArray((LinearAlgebra.Complex.Matrix)(object)matrix, name); - } - else if (typeof (TDataType) == typeof (Complex32)) - { - var o = matrix as LinearAlgebra.Complex32.SparseMatrix; - data = o != null - ? GetSparseDataArray((LinearAlgebra.Complex32.SparseMatrix)(object)matrix, name) - : GetDenseDataArray((LinearAlgebra.Complex32.Matrix)(object)matrix, name); - } - else - { - throw new NotSupportedException(); - } - - WriteCompressedData(data); + return Formatter.FormatMatrix(matrix, matrixName); } - /// - /// Writes the given to the file. - /// - /// The matrices to write. - /// The names of the matrices to store in the file. - /// If either or is null. /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. - public void WriteMatrices(IList> matrices, IList names) + public static void Write(Stream stream, Matrix matrix, string matrixName) where TDataType : struct, IEquatable, IFormattable { - if (matrices == null) - { - throw new ArgumentNullException("matrices"); - } - - if (names == null) - { - throw new ArgumentNullException("names"); - } - - if (matrices.Count != names.Count) - { - throw new ArgumentException(Resources.ArgumentMatrixDimensions); - } - - for (var i = 0; i < matrices.Count; i++) - { - WriteMatrix(matrices[i], names[i]); - } - } - - /// - /// Closes the stream the being written to. - /// - /// Calls . - public void Close() - { - Dispose(); - } - - /// - /// Writes the matrix tag and name. - /// - /// The writer we are using. - /// The array class we are writing. - /// if set to true if this a complex matrix. - /// The name of the matrix. - /// The number of rows. - /// The columns of columns. - /// The maximum number of non-zero elements. - static void WriteMatrixTagAndName(BinaryWriter writer, ArrayClass arrayClass, bool isComplex, - string name, int rows, int columns, int nzmax) - { - writer.Write((int)DataType.Matrix); - - // add place holder for data size - writer.Write(0); - - // write flag, data type and size - writer.Write((int)DataType.UInt32); - writer.Write(8); - - // write array class and flags - writer.Write((byte)arrayClass); - if (isComplex) - { - writer.Write((byte)ArrayFlags.Complex); - } - else - { - writer.Write((byte)0); - } - - writer.Write((short)0); - writer.Write(nzmax); - - // write dimensions - writer.Write((int)DataType.Int32); - writer.Write(8); - writer.Write(rows); - writer.Write(columns); - - var nameBytes = Encoding.ASCII.GetBytes(name); - - // write name - if (nameBytes.Length > 4) - { - writer.Write((int)DataType.Int8); - writer.Write(nameBytes.Length); - writer.Write(nameBytes); - var pad = 8 - (nameBytes.Length%8); - PadData(writer, pad); - } - else - { - writer.Write((short)DataType.Int8); - writer.Write((short)nameBytes.Length); - writer.Write(nameBytes); - PadData(writer, 4 - nameBytes.Length); - } - } - - /// - /// Compresses the data array. - /// - /// The data to compress. - /// The compressed data. - static byte[] CompressData(byte[] data) - { - var adler = BitConverter.GetBytes(Adler32.Compute(data)); - using (var compressedStream = new MemoryStream()) - { - compressedStream.WriteByte(0x58); - compressedStream.WriteByte(0x85); - using (var outputStream = new DeflateStream(compressedStream, CompressionMode.Compress, true)) - { - outputStream.Write(data, 0, data.Length); - } - - compressedStream.WriteByte(adler[3]); - compressedStream.WriteByte(adler[2]); - compressedStream.WriteByte(adler[1]); - compressedStream.WriteByte(adler[0]); - return compressedStream.ToArray(); - } - } - - /// - /// Gets the dense data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetDenseDataArray(Matrix matrix, string name) - { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) - { - WriteMatrixTagAndName(dataWriter, ArrayClass.Double, false, name, matrix.RowCount, matrix.ColumnCount, 0); - - // write data - dataWriter.Write((int)DataType.Double); - dataWriter.Write(matrix.RowCount*matrix.ColumnCount*8); - - for (var j = 0; j < matrix.ColumnCount; j++) - { - var column = matrix.Column(j); - foreach (var value in column) - { - dataWriter.Write(value); - } - } - - data = dataMemoryStream.ToArray(); - } - - return data; - } - - /// - /// Gets the dense data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetDenseDataArray(Matrix matrix, string name) - { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) - { - WriteMatrixTagAndName(dataWriter, ArrayClass.Single, false, name, matrix.RowCount, matrix.ColumnCount, 0); - - // write data - dataWriter.Write((int)DataType.Single); - - dataWriter.Write(matrix.RowCount*matrix.ColumnCount*4); - - for (var j = 0; j < matrix.ColumnCount; j++) - { - var column = matrix.Column(j); - foreach (var value in column) - { - dataWriter.Write(value); - } - } - - var pad = (matrix.RowCount*matrix.ColumnCount*4)%8; - PadData(dataWriter, pad); - - data = dataMemoryStream.ToArray(); - } - - return data; - } - - /// - /// Gets the dense data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetDenseDataArray(Matrix matrix, string name) - { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) - { - WriteMatrixTagAndName(dataWriter, ArrayClass.Double, true, name, matrix.RowCount, matrix.ColumnCount, 0); - - // write data - dataWriter.Write((int)DataType.Double); - dataWriter.Write(matrix.RowCount*matrix.ColumnCount*8); - - for (var j = 0; j < matrix.ColumnCount; j++) - { - var column = matrix.Column(j); - foreach (var value in column) - { - dataWriter.Write(value.Real); - } - } - - dataWriter.Write((int)DataType.Double); - dataWriter.Write(matrix.RowCount*matrix.ColumnCount*8); - - for (var j = 0; j < matrix.ColumnCount; j++) - { - var column = matrix.Column(j); - foreach (var value in column) - { - dataWriter.Write(value.Imaginary); - } - } - - data = dataMemoryStream.ToArray(); - } - - return data; - } - - /// - /// Gets the dense data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetDenseDataArray(Matrix matrix, string name) - { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) - { - WriteMatrixTagAndName(dataWriter, ArrayClass.Single, true, name, matrix.RowCount, matrix.ColumnCount, 0); - - // write data - dataWriter.Write((int)DataType.Single); - dataWriter.Write(matrix.RowCount*matrix.ColumnCount*4); - - for (var j = 0; j < matrix.ColumnCount; j++) - { - var column = matrix.Column(j); - foreach (var value in column) - { - dataWriter.Write(value.Real); - } - } - - var pad = (matrix.RowCount*matrix.ColumnCount*4)%8; - PadData(dataWriter, pad); - - dataWriter.Write((int)DataType.Single); - dataWriter.Write(matrix.RowCount*matrix.ColumnCount*4); - - for (var j = 0; j < matrix.ColumnCount; j++) - { - var column = matrix.Column(j); - foreach (var value in column) - { - dataWriter.Write(value.Real); - } - } - - PadData(dataWriter, pad); - - data = dataMemoryStream.ToArray(); - } - - return data; - } - - /// - /// Gets the sparse data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetSparseDataArray(LinearAlgebra.Double.SparseMatrix matrix, string name) - { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) - { - var nzmax = matrix.NonZerosCount; - WriteMatrixTagAndName(dataWriter, ArrayClass.Sparse, false, name, matrix.RowCount, matrix.ColumnCount, - nzmax); - - // write ir - dataWriter.Write((int)DataType.Int32); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item1); - } - } - - // add pad if needed - if (nzmax%2 == 1) - { - dataWriter.Write(0); - } - - // write jc - dataWriter.Write((int)DataType.Int32); - dataWriter.Write((matrix.ColumnCount + 1)*4); - dataWriter.Write(0); - var count = 0; - foreach (var column in matrix.EnumerateColumns()) - { - count += ((SparseVectorStorage)column.Storage).ValueCount; - dataWriter.Write(count); - } - - // add pad if needed - if (matrix.ColumnCount%2 == 0) - { - dataWriter.Write(0); - } - - // write data - dataWriter.Write((int)DataType.Double); - dataWriter.Write(nzmax*8); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item2); - } - } - - data = dataMemoryStream.ToArray(); - } - - return data; + Store(stream, new[] { Pack(matrix, matrixName) }); } - /// - /// Gets the sparse data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetSparseDataArray(LinearAlgebra.Single.SparseMatrix matrix, string name) + /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. + public static void Write(string filePath, Matrix matrix, string matrixName) + where TDataType : struct, IEquatable, IFormattable { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) - { - var nzmax = matrix.NonZerosCount; - WriteMatrixTagAndName(dataWriter, ArrayClass.Sparse, false, name, matrix.RowCount, matrix.ColumnCount, - nzmax); - - // write ir - dataWriter.Write((int)DataType.Int32); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item1); - } - } - - // add pad if needed - if (nzmax%2 == 1) - { - dataWriter.Write(0); - } - - // write jc - dataWriter.Write((int)DataType.Int32); - dataWriter.Write((matrix.ColumnCount + 1)*4); - dataWriter.Write(0); - var count = 0; - foreach (var column in matrix.EnumerateColumns()) - { - count += ((SparseVectorStorage)column.Storage).ValueCount; - dataWriter.Write(count); - } - - // add pad if needed - if (matrix.ColumnCount%2 == 0) - { - dataWriter.Write(0); - } - - // write data - dataWriter.Write((int)DataType.Single); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item2); - } - } - - var pad = (nzmax*4)%8; - PadData(dataWriter, pad); - - data = dataMemoryStream.ToArray(); - } - - return data; + Store(filePath, new[] { Pack(matrix, matrixName) }); } - /// - /// Gets the sparse data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetSparseDataArray(LinearAlgebra.Complex.SparseMatrix matrix, string name) + /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. + public static void Write(Stream stream, IList> matrices, IList names) + where TDataType : struct, IEquatable, IFormattable { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) + if (matrices.Count != names.Count) { - var nzmax = matrix.NonZerosCount; - WriteMatrixTagAndName(dataWriter, ArrayClass.Sparse, true, name, matrix.RowCount, matrix.ColumnCount, - nzmax); - - // write ir - dataWriter.Write((int)DataType.Int32); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item1); - } - } - - // add pad if needed - if (nzmax%2 == 1) - { - dataWriter.Write(0); - } - - // write jc - dataWriter.Write((int)DataType.Int32); - dataWriter.Write((matrix.ColumnCount + 1)*4); - dataWriter.Write(0); - var count = 0; - foreach (var column in matrix.EnumerateColumns()) - { - count += ((SparseVectorStorage)column.Storage).ValueCount; - dataWriter.Write(count); - } - - // add pad if needed - if (matrix.ColumnCount%2 == 0) - { - dataWriter.Write(0); - } - - // write data - dataWriter.Write((int)DataType.Double); - dataWriter.Write(nzmax*8); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item2.Real); - } - } - - dataWriter.Write((int)DataType.Double); - dataWriter.Write(nzmax*8); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item2.Real); - } - } - - data = dataMemoryStream.ToArray(); + throw new ArgumentException("Each matrix must have a name. Number of matrices must equal to the number of names."); } - return data; + Store(stream, matrices.Zip(names, Pack)); } - /// - /// Gets the sparse data array. - /// - /// The matrix to get the data from. - /// The name of the matrix. - /// The matrix data as an array. - static byte[] GetSparseDataArray(LinearAlgebra.Complex32.SparseMatrix matrix, string name) + /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. + public static void Write(string filePath, IList> matrices, IList names) + where TDataType : struct, IEquatable, IFormattable { - byte[] data; - using (var dataMemoryStream = new MemoryStream()) - using (var dataWriter = new BinaryWriter(dataMemoryStream)) + if (matrices.Count != names.Count) { - var nzmax = matrix.NonZerosCount; - WriteMatrixTagAndName(dataWriter, ArrayClass.Sparse, true, name, matrix.RowCount, matrix.ColumnCount, - nzmax); - - // write ir - dataWriter.Write((int)DataType.Int32); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item1); - } - } - - // add pad if needed - if (nzmax%2 == 1) - { - dataWriter.Write(0); - } - - // write jc - dataWriter.Write((int)DataType.Int32); - dataWriter.Write((matrix.ColumnCount + 1)*4); - dataWriter.Write(0); - var count = 0; - foreach (var column in matrix.EnumerateColumns()) - { - count += ((SparseVectorStorage)column.Storage).ValueCount; - dataWriter.Write(count); - } - - // add pad if needed - if (matrix.ColumnCount%2 == 0) - { - dataWriter.Write(0); - } - - // write data - dataWriter.Write((int)DataType.Single); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item2.Real); - } - } - - var pad = (nzmax*4)%8; - PadData(dataWriter, pad); - - dataWriter.Write((int)DataType.Single); - dataWriter.Write(nzmax*4); - - foreach (var column in matrix.EnumerateColumns()) - { - foreach (var row in column.EnumerateNonZeroIndexed()) - { - dataWriter.Write(row.Item2.Real); - } - } - - PadData(dataWriter, pad); - - data = dataMemoryStream.ToArray(); + throw new ArgumentException("Each matrix must have a name. Number of matrices must equal to the number of names."); } - return data; + Store(filePath, matrices.Zip(names, Pack)); } - /// - /// Writes the compressed data. - /// - /// The data to write. - void WriteCompressedData(byte[] data) - { - // fill in data size - var size = BitConverter.GetBytes(data.Length); - data[4] = size[0]; - data[5] = size[1]; - data[6] = size[2]; - data[7] = size[3]; - - // compress data - var compressedData = CompressData(data); - - // write compressed data to file - _writer.Write(compressedData.Length); - _writer.Write(compressedData); - } - - /// - /// Writes the file header. - /// - void WriteHeader() + /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. + public static void Write(Stream stream, IEnumerable>> matrices) + where TDataType : struct, IEquatable, IFormattable { - var header = Encoding.ASCII.GetBytes(HeaderText + DateTime.Now.ToString(Resources.MatlabDateHeaderFormat)); - _writer.Write(header); - PadData(_writer, HeaderTextLength - header.Length + 8, 32); - - // write version - _writer.Write((short)0x100); - - // write little endian indicator - _writer.Write((byte)0x49); - _writer.Write((byte)0x4D); + Store(stream, matrices.Select(kv => Pack(kv.Value, kv.Key))); } - /// - /// Pads the data with the given byte. - /// - /// Where to write the pad values. - /// The number of bytes to pad. - /// What value to pad with. - static void PadData(BinaryWriter writer, int bytes, byte pad = (byte)0) + /// The data type of the Matrix. It can be either: double, float, Complex, or Complex32. + public static void Write(string filePath, IEnumerable>> matrices) + where TDataType : struct, IEquatable, IFormattable { - for (var i = 0; i < bytes; i++) - { - writer.Write(pad); - } + Store(filePath, matrices.Select(kv => Pack(kv.Value, kv.Key))); } } } diff --git a/src/Data/Matlab/Parser.cs b/src/Data/Matlab/Parser.cs index a2f297be..b4533cc5 100644 --- a/src/Data/Matlab/Parser.cs +++ b/src/Data/Matlab/Parser.cs @@ -59,9 +59,79 @@ namespace MathNet.Numerics.Data.Matlab const int SmallBlockSize = 4; /// - /// Extracts all matrix blocks in a format we support. + /// Parse a matrix block byte array /// - internal static List ParseAll(Stream stream) + internal static Matrix ParseMatrix(byte[] data) + where T : struct, IEquatable, IFormattable + { + using (var stream = new MemoryStream(data)) + using (var reader = new BinaryReader(stream)) + { + // skip tag - doesn't tell us anything we don't already know + reader.BaseStream.Seek(8, SeekOrigin.Current); + + var arrayClass = (ArrayClass)reader.ReadByte(); + var flags = reader.ReadByte(); + var isComplex = (flags & (byte)ArrayFlags.Complex) == (byte)ArrayFlags.Complex; + + // skip unneeded bytes + reader.BaseStream.Seek(10, SeekOrigin.Current); + + var numDimensions = reader.ReadInt32()/8; + if (numDimensions > 2) + { + throw new NotSupportedException(Resources.MoreThan2D); + } + + var rows = reader.ReadInt32(); + var columns = reader.ReadInt32(); + + // skip name and unneeded bytes + reader.BaseStream.Seek(2, SeekOrigin.Current); + int size = reader.ReadInt16(); + var smallBlock = true; + if (size == 0) + { + size = reader.ReadInt32(); + smallBlock = false; + } + + reader.BaseStream.Seek(size, SeekOrigin.Current); + AlignData(reader.BaseStream, size, smallBlock); + + var type = (DataType)reader.ReadInt16(); + size = reader.ReadInt16(); + if (size == 0) + { + size = reader.ReadInt32(); + } + + Matrix matrix; + switch (arrayClass) + { + case ArrayClass.Sparse: + matrix = SparseArrayReader.PopulateSparseMatrix(reader, isComplex, rows, columns, size); + break; + case ArrayClass.Function: + case ArrayClass.Character: + case ArrayClass.Object: + case ArrayClass.Structure: + case ArrayClass.Cell: + case ArrayClass.Unknown: + throw new NotSupportedException(); + default: + matrix = NumericArrayReader.PopulateDenseMatrix(type, reader, isComplex, rows, columns, size); + break; + } + + return matrix; + } + } + + /// + /// Extracts all matrix blocks in a format we support from a stream. + /// + internal static List ParseFile(Stream stream) { var matrices = new List(); @@ -122,7 +192,7 @@ namespace MathNet.Numerics.Data.Matlab var matrixName = Encoding.ASCII.GetString(matrixReader.ReadBytes(matrixSize)); - matrices.Add(new MatlabMatrix(matrixName, matrixSize, matrixDim, data)); + matrices.Add(new MatlabMatrix(matrixName, data)); } } } @@ -176,72 +246,5 @@ namespace MathNet.Numerics.Data.Matlab return data; } - - internal static Matrix ReadMatrixBlock(byte[] data) - where TDataType : struct, IEquatable, IFormattable - { - using (var stream = new MemoryStream(data)) - using (var reader = new BinaryReader(stream)) - { - // skip tag - doesn't tell us anything we don't already know - reader.BaseStream.Seek(8, SeekOrigin.Current); - - var arrayClass = (ArrayClass)reader.ReadByte(); - var flags = reader.ReadByte(); - var isComplex = (flags & (byte)ArrayFlags.Complex) == (byte)ArrayFlags.Complex; - - // skip unneeded bytes - reader.BaseStream.Seek(10, SeekOrigin.Current); - - var numDimensions = reader.ReadInt32()/8; - if (numDimensions > 2) - { - throw new NotSupportedException(Resources.MoreThan2D); - } - - var rows = reader.ReadInt32(); - var columns = reader.ReadInt32(); - - // skip name and unneeded bytes - reader.BaseStream.Seek(2, SeekOrigin.Current); - int size = reader.ReadInt16(); - var smallBlock = true; - if (size == 0) - { - size = reader.ReadInt32(); - smallBlock = false; - } - - reader.BaseStream.Seek(size, SeekOrigin.Current); - AlignData(reader.BaseStream, size, smallBlock); - - var type = (DataType)reader.ReadInt16(); - size = reader.ReadInt16(); - if (size == 0) - { - size = reader.ReadInt32(); - } - - Matrix matrix; - switch (arrayClass) - { - case ArrayClass.Sparse: - matrix = SparseArrayReader.PopulateSparseMatrix(reader, isComplex, rows, columns, size); - break; - case ArrayClass.Function: - case ArrayClass.Character: - case ArrayClass.Object: - case ArrayClass.Structure: - case ArrayClass.Cell: - case ArrayClass.Unknown: - throw new NotSupportedException(); - default: - matrix = NumericArrayReader.PopulateDenseMatrix(type, reader, isComplex, rows, columns, size); - break; - } - - return matrix; - } - } } } diff --git a/src/DataUnitTests/Matlab/MatlabWriterTests.cs b/src/DataUnitTests/Matlab/MatlabWriterTests.cs index 1e879711..fd50abba 100644 --- a/src/DataUnitTests/Matlab/MatlabWriterTests.cs +++ b/src/DataUnitTests/Matlab/MatlabWriterTests.cs @@ -28,158 +28,120 @@ using System; using System.IO; using System.Numerics; using MathNet.Numerics.Data.Matlab; +using MathNet.Numerics.LinearAlgebra; using NUnit.Framework; namespace MathNet.Numerics.Data.UnitTests.Matlab { /// - /// Matlab matrix writer tests. + /// MATLAB matrix writer tests. /// [TestFixture] public class MatlabWriterTests { - /// - /// Invalid constructor throws ArgumentException. - /// - [Test] - public void InvalidConstructorThrowsArgumentException() - { - Assert.Throws(() => new MatlabWriter(string.Empty)); - Assert.Throws(() => new MatlabWriter(null)); - } - - /// - /// Write bad matrices throws ArgumentException. - /// [Test] public void WriteBadMatricesThrowsArgumentException() { - var matrix = new LinearAlgebra.Single.DenseMatrix(1, 1); - var writer = new MatlabWriter("somefile3"); - Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new[] { string.Empty })); - Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new string[] { null })); - Assert.Throws(() => writer.WriteMatrices(new[] { matrix, matrix }, new[] { "matrix" })); - Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, new[] { "some matrix" })); - writer.Dispose(); - } - - /// - /// Write null matrices throws ArgumentNullException. - /// - [Test] - public void WriteNullMatricesThrowsArgumentNullException() - { - var writer = new MatlabWriter("somefile4"); - Assert.Throws(() => writer.WriteMatrices(new LinearAlgebra.Single.Matrix[] { null }, new[] { "matrix" })); - var matrix = new LinearAlgebra.Single.DenseMatrix(1, 1); - Assert.Throws(() => writer.WriteMatrices(new LinearAlgebra.Single.Matrix[] { matrix }, null)); - writer.Dispose(); + Matrix matrix = Matrix.Build.Dense(1, 1); + Assert.Throws(() => MatlabWriter.Write("somefile3", matrix, string.Empty)); + Assert.Throws(() => MatlabWriter.Write("somefile3", matrix, null)); + Assert.Throws(() => MatlabWriter.Write("somefile3", matrix, "some matrix")); + Assert.Throws(() => MatlabWriter.Write("somefile3", new[] { matrix }, new[] { string.Empty })); + Assert.Throws(() => MatlabWriter.Write("somefile3", new[] { matrix }, new string[] { null })); + Assert.Throws(() => MatlabWriter.Write("somefile3", new[] { matrix, matrix }, new[] { "matrix" })); + Assert.Throws(() => MatlabWriter.Write("somefile3", new[] { matrix }, new[] { "some matrix" })); } - /// - /// Can write double matrices. - /// [Test] public void CanWriteDoubleMatrices() { - var mat1 = new LinearAlgebra.Double.DenseMatrix(5, 3); + Matrix mat1 = Matrix.Build.Dense(5, 5); for (var i = 0; i < mat1.ColumnCount; i++) { mat1[i, i] = i + .1; } - var mat2 = new LinearAlgebra.Double.DenseMatrix(4, 5); + Matrix mat2 = Matrix.Build.Dense(4, 5); for (var i = 0; i < mat2.RowCount; i++) { mat2[i, i] = i + .1; } - var mat3 = new LinearAlgebra.Double.SparseMatrix(5, 4); + Matrix mat3 = Matrix.Build.Sparse(5, 4); mat3[0, 0] = 1.1; mat3[0, 2] = 2.2; mat3[4, 3] = 3.3; - var mat4 = new LinearAlgebra.Double.SparseMatrix(3, 5); + Matrix mat4 = Matrix.Build.Sparse(3, 5); mat4[0, 0] = 1.1; mat4[0, 2] = 2.2; mat4[2, 4] = 3.3; - var write = new LinearAlgebra.Double.Matrix[] { mat1, mat2, mat3, mat4 }; + Matrix[] write = { mat1, mat2, mat3, mat4 }; + string[] names = { "mat1", "dense_matrix_2", "s1", "sparse2" }; - var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" }; if (File.Exists("testd.mat")) { File.Delete("testd.mat"); } - var writer = new MatlabWriter("testd.mat"); - writer.WriteMatrices(write, names); - writer.Dispose(); + MatlabWriter.Write("testd.mat", write, names); var read = MatlabReader.ReadAll("testd.mat", names); - Assert.AreEqual(write.Length, read.Count); for (var i = 0; i < write.Length; i++) { var w = write[i]; var r = read[names[i]]; - Assert.AreEqual(w.RowCount, r.RowCount); Assert.AreEqual(w.ColumnCount, r.ColumnCount); Assert.IsTrue(w.Equals(r)); } } - /// - /// Can write float matrices. - /// [Test] public void CanWriteFloatMatrices() { - var mat1 = new LinearAlgebra.Single.DenseMatrix(5, 3); + Matrix mat1 = Matrix.Build.Dense(5, 3); for (var i = 0; i < mat1.ColumnCount; i++) { mat1[i, i] = i + .1f; } - var mat2 = new LinearAlgebra.Single.DenseMatrix(4, 5); + Matrix mat2 = Matrix.Build.Dense(4, 5); for (var i = 0; i < mat2.RowCount; i++) { mat2[i, i] = i + .1f; } - var mat3 = new LinearAlgebra.Single.SparseMatrix(5, 4); + Matrix mat3 = Matrix.Build.Sparse(5, 4); mat3[0, 0] = 1.1f; mat3[0, 2] = 2.2f; mat3[4, 3] = 3.3f; - var mat4 = new LinearAlgebra.Single.SparseMatrix(3, 5); + Matrix mat4 = Matrix.Build.Sparse(3, 5); mat4[0, 0] = 1.1f; mat4[0, 2] = 2.2f; mat4[2, 4] = 3.3f; - var write = new LinearAlgebra.Single.Matrix[] { mat1, mat2, mat3, mat4 }; + Matrix[] write = { mat1, mat2, mat3, mat4 }; + string[] names = { "mat1", "dense_matrix_2", "s1", "sparse2" }; - var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" }; if (File.Exists("tests.mat")) { File.Delete("tests.mat"); } - var writer = new MatlabWriter("tests.mat"); - writer.WriteMatrices(write, names); - writer.Dispose(); + MatlabWriter.Write("tests.mat", write, names); var read = MatlabReader.ReadAll("tests.mat", names); - Assert.AreEqual(write.Length, read.Count); for (var i = 0; i < write.Length; i++) { var w = write[i]; var r = read[names[i]]; - Assert.AreEqual(w.RowCount, r.RowCount); Assert.AreEqual(w.ColumnCount, r.ColumnCount); Assert.IsTrue(w.Equals(r)); @@ -192,40 +154,37 @@ namespace MathNet.Numerics.Data.UnitTests.Matlab [Test] public void CanWriteComplex32Matrices() { - var mat1 = new LinearAlgebra.Complex32.DenseMatrix(5, 3); + Matrix mat1 = Matrix.Build.Dense(5, 3); for (var i = 0; i < mat1.ColumnCount; i++) { mat1[i, i] = new Complex32(i + .1f, i + .1f); } - var mat2 = new LinearAlgebra.Complex32.DenseMatrix(4, 5); + Matrix mat2 = Matrix.Build.Dense(4, 5); for (var i = 0; i < mat2.RowCount; i++) { mat2[i, i] = new Complex32(i + .1f, i + .1f); } - var mat3 = new LinearAlgebra.Complex32.SparseMatrix(5, 4); + Matrix mat3 = Matrix.Build.Sparse(5, 4); mat3[0, 0] = new Complex32(1.1f, 1.1f); mat3[0, 2] = new Complex32(2.2f, 2.2f); mat3[4, 3] = new Complex32(3.3f, 3.3f); - var mat4 = new LinearAlgebra.Complex32.SparseMatrix(3, 5); + Matrix mat4 = Matrix.Build.Sparse(3, 5); mat4[0, 0] = new Complex32(1.1f, 1.1f); mat4[0, 2] = new Complex32(2.2f, 2.2f); mat4[2, 4] = new Complex32(3.3f, 3.3f); - var write = new LinearAlgebra.Complex32.Matrix[] { mat1, mat2, mat3, mat4 }; + Matrix[] write = { mat1, mat2, mat3, mat4 }; + string[] names = { "mat1", "dense_matrix_2", "s1", "sparse2" }; - var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" }; if (File.Exists("testc.mat")) { File.Delete("testc.mat"); } - var writer = new MatlabWriter("testc.mat"); - writer.WriteMatrices(write, names); - writer.Dispose(); - + MatlabWriter.Write("testc.mat", write, names); var read = MatlabReader.ReadAll("testc.mat", names); Assert.AreEqual(write.Length, read.Count); @@ -234,7 +193,6 @@ namespace MathNet.Numerics.Data.UnitTests.Matlab { var w = write[i]; var r = read[names[i]]; - Assert.AreEqual(w.RowCount, r.RowCount); Assert.AreEqual(w.ColumnCount, r.ColumnCount); Assert.IsTrue(w.Equals(r)); @@ -247,49 +205,45 @@ namespace MathNet.Numerics.Data.UnitTests.Matlab [Test] public void CanWriteComplexMatrices() { - var mat1 = new LinearAlgebra.Complex.DenseMatrix(5, 3); + Matrix mat1 = Matrix.Build.Dense(5, 3); for (var i = 0; i < mat1.ColumnCount; i++) { mat1[i, i] = new Complex(i + .1, i + .1); } - var mat2 = new LinearAlgebra.Complex.DenseMatrix(4, 5); + Matrix mat2 = Matrix.Build.Dense(4, 5); for (var i = 0; i < mat2.RowCount; i++) { mat2[i, i] = new Complex(i + .1, i + .1); } - var mat3 = new LinearAlgebra.Complex.SparseMatrix(5, 4); + Matrix mat3 = Matrix.Build.Sparse(5, 4); mat3[0, 0] = new Complex(1.1, 1.1); mat3[0, 2] = new Complex(2.2, 2.2); mat3[4, 3] = new Complex(3.3, 3.3); - var mat4 = new LinearAlgebra.Complex.SparseMatrix(3, 5); + Matrix mat4 = Matrix.Build.Sparse(3, 5); mat4[0, 0] = new Complex(1.1, 1.1); mat4[0, 2] = new Complex(2.2, 2.2); mat4[2, 4] = new Complex(3.3, 3.3); - var write = new LinearAlgebra.Complex.Matrix[] { mat1, mat2, mat3, mat4 }; + Matrix[] write = { mat1, mat2, mat3, mat4 }; + string[] names = { "mat1", "dense_matrix_2", "s1", "sparse2" }; - var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" }; if (File.Exists("testz.mat")) { File.Delete("testz.mat"); } - var writer = new MatlabWriter("testz.mat"); - writer.WriteMatrices(write, names); - writer.Dispose(); + MatlabWriter.Write("testz.mat", write, names); var read = MatlabReader.ReadAll("testz.mat", names); - Assert.AreEqual(write.Length, read.Count); for (var i = 0; i < write.Length; i++) { var w = write[i]; var r = read[names[i]]; - Assert.AreEqual(w.RowCount, r.RowCount); Assert.AreEqual(w.ColumnCount, r.ColumnCount); Assert.IsTrue(w.Equals(r)); @@ -302,11 +256,9 @@ namespace MathNet.Numerics.Data.UnitTests.Matlab [Test] public void WriteBadMatrixThrowsArgumentException() { - var matrix = new LinearAlgebra.Single.DenseMatrix(1, 1); - var writer = new MatlabWriter("somefile1"); - Assert.Throws(() => writer.WriteMatrix(matrix, string.Empty)); - Assert.Throws(() => writer.WriteMatrix(matrix, null)); - writer.Dispose(); + var matrix = Matrix.Build.Dense(1, 1); + Assert.Throws(() => MatlabWriter.Write("somefile1", matrix, string.Empty)); + Assert.Throws(() => MatlabWriter.Write("somefile1", matrix, null)); } /// @@ -315,9 +267,7 @@ namespace MathNet.Numerics.Data.UnitTests.Matlab [Test] public void WriteNullMatrixThrowsArgumentNullException() { - var writer = new MatlabWriter("somefile2"); - Assert.Throws(() => writer.WriteMatrix(null, "matrix")); - writer.Dispose(); + Assert.Throws(() => MatlabWriter.Write("somefile2", null, "matrix")); } } }