diff --git a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs index cb4fe7ca..cf1c0d94 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs @@ -1161,7 +1161,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex /// /// Matrix M[i,j] = this[i] * v[j]. /// - /// public Matrix OuterProduct(SparseVector v) { return OuterProduct(this, v); @@ -1532,5 +1531,27 @@ namespace MathNet.Numerics.LinearAlgebra.Complex } #endregion + + /// + /// Returns an that contains the position and value of the element. + /// + /// + /// An over this vector that contains the position and value of each + /// element. + /// + /// + /// The enumerator returns a + /// + /// with the key being the element index and the value + /// being the value of the element at that index. For sparse vectors, the enumerator will exclude all elements + /// with a zero value. + /// + public override IEnumerable> GetIndexedEnumerator() + { + for (var i = 0; i < NonZerosCount; i++) + { + yield return new KeyValuePair(_nonZeroIndices[i], _nonZeroValues[i]); + } + } } } diff --git a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs index bc361a6e..46c677b2 100644 --- a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs @@ -1161,7 +1161,6 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// Matrix M[i,j] = this[i] * v[j]. /// - /// public Matrix OuterProduct(SparseVector v) { return OuterProduct(this, v); @@ -1532,5 +1531,27 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 } #endregion + + /// + /// Returns an that contains the position and value of the element. + /// + /// + /// An over this vector that contains the position and value of each + /// element. + /// + /// + /// The enumerator returns a + /// + /// with the key being the element index and the value + /// being the value of the element at that index. For sparse vectors, the enumerator will exclude all elements + /// with a zero value. + /// + public override IEnumerable> GetIndexedEnumerator() + { + for (var i = 0; i < NonZerosCount; i++) + { + yield return new KeyValuePair(_nonZeroIndices[i], _nonZeroValues[i]); + } + } } } diff --git a/src/Numerics/LinearAlgebra/Double/SparseVector.cs b/src/Numerics/LinearAlgebra/Double/SparseVector.cs index f5a44801..244a2eca 100644 --- a/src/Numerics/LinearAlgebra/Double/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/SparseVector.cs @@ -1160,7 +1160,6 @@ namespace MathNet.Numerics.LinearAlgebra.Double /// /// Matrix M[i,j] = this[i] * v[j]. /// - /// public Matrix OuterProduct(SparseVector v) { return OuterProduct(this, v); @@ -1545,5 +1544,27 @@ namespace MathNet.Numerics.LinearAlgebra.Double return true; } + + /// + /// Returns an that contains the position and value of the element. + /// + /// + /// An over this vector that contains the position and value of each + /// element. + /// + /// + /// The enumerator returns a + /// + /// with the key being the element index and the value + /// being the value of the element at that index. For sparse vectors, the enumerator will exclude all elements + /// with a zero value. + /// + public override IEnumerable> GetIndexedEnumerator() + { + for (var i = 0; i < NonZerosCount; i++) + { + yield return new KeyValuePair(_nonZeroIndices[i], _nonZeroValues[i]); + } + } } } diff --git a/src/Numerics/LinearAlgebra/Generic/Vector.cs b/src/Numerics/LinearAlgebra/Generic/Vector.cs index bf4ffc4a..a1c72a5b 100644 --- a/src/Numerics/LinearAlgebra/Generic/Vector.cs +++ b/src/Numerics/LinearAlgebra/Generic/Vector.cs @@ -1369,7 +1369,7 @@ namespace MathNet.Numerics.LinearAlgebra.Generic /// /// /// An over this vector that contains the position and value of each - /// non-zero element. + /// element. /// /// /// The enumerator returns a diff --git a/src/Numerics/LinearAlgebra/IO/MatlabReader.cs b/src/Numerics/LinearAlgebra/IO/MatlabReader.cs index 138e38c8..41aeb7e1 100644 --- a/src/Numerics/LinearAlgebra/IO/MatlabReader.cs +++ b/src/Numerics/LinearAlgebra/IO/MatlabReader.cs @@ -31,6 +31,7 @@ namespace MathNet.Numerics.LinearAlgebra.IO using System; using System.Collections.Generic; using System.IO; + using System.Linq; using Generic; using Matlab; using Properties; @@ -144,8 +145,9 @@ namespace MathNet.Numerics.LinearAlgebra.IO /// /// Reads all matrices from the file or stream. /// - /// All matrices from the file or stream. - public Matrix[] ReadMatrices() + /// All matrices from the file or stream. The key to the + /// is the matrix's name. + public IDictionary> ReadMatrices() { return ReadMatrices(new string[] { }); } @@ -155,9 +157,9 @@ namespace MathNet.Numerics.LinearAlgebra.IO /// /// The names of the matrices to retrieve. /// - /// The named matrices from the file or stream. - /// - public Matrix[] ReadMatrices(IEnumerable names) + /// The named matrices from the file or stream. The key to the + /// is the matrix's name. + public IDictionary> ReadMatrices(IEnumerable names) { Stream stream; if (_filename == null) @@ -173,20 +175,13 @@ namespace MathNet.Numerics.LinearAlgebra.IO var parser = new MatlabParser(stream, names); var file = parser.Parse(); - var matrices = new Matrix[file.Matrices.Count]; - var i = 0; - foreach (var matrix in file.Matrices.Values) - { - matrices[i++] = matrix; - } - if (_filename != null) { stream.Close(); stream.Dispose(); } - return matrices; + return file.Matrices.ToDictionary(matrix => matrix.Key, matrix => matrix.Value); } } } diff --git a/src/Numerics/LinearAlgebra/IO/MatlabWriter.cs b/src/Numerics/LinearAlgebra/IO/MatlabWriter.cs new file mode 100644 index 00000000..c1232f09 --- /dev/null +++ b/src/Numerics/LinearAlgebra/IO/MatlabWriter.cs @@ -0,0 +1,752 @@ +// +// 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-2010 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.Text; +using zlib; + +namespace MathNet.Numerics.LinearAlgebra.IO +{ + using Generic; + using Matlab; + using Properties; + + /// + /// Writes matrices to a Matlab file. + /// + public class MatlabMatrixWriter : IDisposable + { + /// + /// The file header value + /// + private const string HeaderText = "MATLAB 5.0 MAT-file, Platform: .NET 4 - Math.NET Numerics, Created on: "; + + /// + /// The length of the header text. + /// + private const int HeaderTextLength = 116; + + /// + /// Have we written the header yet. + /// + private bool _headerWritten; + + /// + /// The binary writer to write to. + /// + private BinaryWriter _writer; + + /// + /// Initializes a new instance of the class. + /// + /// The name of the Matlab file to save the matrices to. + public MatlabMatrixWriter(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + throw new ArgumentException(Resources.StringNullOrEmpty, "filename"); + } + + _writer = new BinaryWriter(new BufferedStream(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + if (_writer != null) + { + _writer.Flush(); + _writer.Close(); + _writer = null; + } + } + + /// + /// 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) 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 datatype + _writer.Write((int)DataType.Compressed); + + byte[] data = null; + + if (typeof(TDataType) == typeof(double)) + { + if (matrix is Double.SparseMatrix) + { + data = GetSparseDataArray((Double.SparseMatrix)(object)matrix, name); + } + else + { + data = GetDenseDataArray((Double.Matrix)(object)matrix, name); + } + } + else if (typeof(TDataType) == typeof(float)) + { + if (matrix is Single.SparseMatrix) + { + data = GetSparseDataArray((Single.SparseMatrix)(object)matrix, name); + } + else + { + data = GetDenseDataArray((Single.Matrix)(object)matrix, name); + } + } + else if (typeof(TDataType) == typeof(System.Numerics.Complex)) + { + if (matrix is Complex.SparseMatrix) + { + data = GetSparseDataArray((Complex.SparseMatrix)(object)matrix, name); + } + else + { + data = GetDenseDataArray((Complex.Matrix)(object)matrix, name); + } + } + else if (typeof(TDataType) == typeof(Numerics.Complex32)) + { + if (matrix is Complex32.SparseMatrix) + { + data = GetSparseDataArray((Complex32.SparseMatrix)(object)matrix, name); + } + else + { + data = GetDenseDataArray((Complex32.Matrix)(object)matrix, name); + } + } + else + { + throw new NotSupportedException(); + } + + WriteCompressedData(data); + } + + /// + /// 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) 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 (int 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. + /// The name name of the matrix. + /// The number of rows. + /// The columns of columns. + /// The maximum number of non-zero elements. + private static void WriteMatrixTagAndName(BinaryWriter writer, ArrayClass arrayClass, 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); + 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); + + byte[] nameBytes = Encoding.ASCII.GetBytes(name); + + // write name + if (nameBytes.Length > 4) + { + writer.Write((int)DataType.Int8); + writer.Write(nameBytes.Length); + writer.Write(nameBytes); + int 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. + private static byte[] CompressData(byte[] data) + { + using (var compressedStream = new MemoryStream()) + { + using (var outputStream = new ZOutputStream(compressedStream, zlibConst.Z_DEFAULT_COMPRESSION)) + { + outputStream.Write(data, 0, data.Length); + } + + 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. + private static byte[] GetDenseDataArray(Matrix matrix, string name) + { + byte[] data; + using (var dataMemoryStream = new MemoryStream()) + using (var dataWriter = new BinaryWriter(dataMemoryStream)) + { + WriteMatrixTagAndName(dataWriter, ArrayClass.Double, 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. + private static byte[] GetDenseDataArray(Matrix matrix, string name) + { + byte[] data; + using (var dataMemoryStream = new MemoryStream()) + using (var dataWriter = new BinaryWriter(dataMemoryStream)) + { + WriteMatrixTagAndName(dataWriter, ArrayClass.Single, 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); + } + } + + 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. + private static byte[] GetDenseDataArray(Matrix matrix, string name) + { + byte[] data; + using (var dataMemoryStream = new MemoryStream()) + using (var dataWriter = new BinaryWriter(dataMemoryStream)) + { + WriteMatrixTagAndName(dataWriter, ArrayClass.Double, 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); + } + } + + throw new NotImplementedException(); + + 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. + private static byte[] GetDenseDataArray(Matrix matrix, string name) + { + byte[] data; + using (var dataMemoryStream = new MemoryStream()) + using (var dataWriter = new BinaryWriter(dataMemoryStream)) + { + WriteMatrixTagAndName(dataWriter, ArrayClass.Single, 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); + } + } + + throw new NotImplementedException(); + + 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. + private static byte[] GetSparseDataArray(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, name, matrix.RowCount, matrix.ColumnCount, nzmax); + + // write ir + dataWriter.Write((int)DataType.Int32); + dataWriter.Write(nzmax * 4); + + foreach (var column in matrix.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Key); + } + } + + // 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); + int count = 0; + foreach (var column in matrix.ColumnEnumerator()) + { + count += ((Double.SparseVector)column.Value).NonZerosCount; + 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.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Value); + } + } + + 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. + private static byte[] GetSparseDataArray(Single.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, name, matrix.RowCount, matrix.ColumnCount, nzmax); + + // write ir + dataWriter.Write((int)DataType.Int32); + dataWriter.Write(nzmax * 4); + + foreach (var column in matrix.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Key); + } + } + + // 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); + int count = 0; + foreach (var column in matrix.ColumnEnumerator()) + { + count += ((Single.SparseVector)column.Value).NonZerosCount; + 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.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Value); + } + } + + 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. + private static byte[] GetSparseDataArray(Complex.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, name, matrix.RowCount, matrix.ColumnCount, nzmax); + + // write ir + dataWriter.Write((int)DataType.Int32); + dataWriter.Write(nzmax * 4); + + foreach (var column in matrix.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Key); + } + } + + // 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); + int count = 0; + foreach (var column in matrix.ColumnEnumerator()) + { + count += ((Complex.SparseVector)column.Value).NonZerosCount; + 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.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Value.Real); + } + } + + throw new NotImplementedException(); + 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. + private static byte[] GetSparseDataArray(Complex32.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, name, matrix.RowCount, matrix.ColumnCount, nzmax); + + // write ir + dataWriter.Write((int)DataType.Int32); + dataWriter.Write(nzmax * 4); + + foreach (var column in matrix.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Key); + } + } + + // 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); + int count = 0; + foreach (var column in matrix.ColumnEnumerator()) + { + count += ((Complex32.SparseVector)column.Value).NonZerosCount; + 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.ColumnEnumerator()) + { + foreach (var row in column.Value.GetIndexedEnumerator()) + { + dataWriter.Write(row.Value.Real); + } + } + + throw new NotImplementedException(); + data = dataMemoryStream.ToArray(); + } + + return data; + } + + /// + /// Writes the compressed data. + /// + /// The data to write. + private 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. + /// + private void WriteHeader() + { + 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. + private static void PadData(BinaryWriter writer, int bytes, byte pad = (byte)0) + { + for (int i = 0; i < bytes; i++) + { + writer.Write(pad); + } + } + } +} \ No newline at end of file diff --git a/src/Numerics/LinearAlgebra/Single/SparseVector.cs b/src/Numerics/LinearAlgebra/Single/SparseVector.cs index 8b0942d3..3f73868a 100644 --- a/src/Numerics/LinearAlgebra/Single/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Single/SparseVector.cs @@ -1160,7 +1160,6 @@ namespace MathNet.Numerics.LinearAlgebra.Single /// /// Matrix M[i,j] = this[i] * v[j]. /// - /// public Matrix OuterProduct(SparseVector v) { return OuterProduct(this, v); @@ -1545,5 +1544,27 @@ namespace MathNet.Numerics.LinearAlgebra.Single return true; } + + /// + /// Returns an that contains the position and value of the element. + /// + /// + /// An over this vector that contains the position and value of each + /// element. + /// + /// + /// The enumerator returns a + /// + /// with the key being the element index and the value + /// being the value of the element at that index. For sparse vectors, the enumerator will exclude all elements + /// with a zero value. + /// + public override IEnumerable> GetIndexedEnumerator() + { + for (var i = 0; i < NonZerosCount; i++) + { + yield return new KeyValuePair(_nonZeroIndices[i], _nonZeroValues[i]); + } + } } } diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 5f56e271..69b512d3 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -142,6 +142,7 @@ + diff --git a/src/Numerics/Properties/Resources.Designer.cs b/src/Numerics/Properties/Resources.Designer.cs index 4907d9a0..e457ff6b 100644 --- a/src/Numerics/Properties/Resources.Designer.cs +++ b/src/Numerics/Properties/Resources.Designer.cs @@ -582,6 +582,15 @@ namespace MathNet.Numerics.Properties { } } + /// + /// Looks up a localized string similar to ddd MMM dd HH:mm:ss yyyy. + /// + internal static string MatlabDateHeaderFormat { + get { + return ResourceManager.GetString("MatlabDateHeaderFormat", resourceCulture); + } + } + /// /// Looks up a localized string similar to The number of columns of a matrix must be positive.. /// @@ -618,6 +627,15 @@ namespace MathNet.Numerics.Properties { } } + /// + /// Looks up a localized string similar to Name cannot contain a space. name: {0}. + /// + internal static string NameCannotContainASpace { + get { + return ResourceManager.GetString("NameCannotContainASpace", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} is not a supported type.. /// diff --git a/src/Numerics/Properties/Resources.resx b/src/Numerics/Properties/Resources.resx index f231e0e1..8dfc7518 100644 --- a/src/Numerics/Properties/Resources.resx +++ b/src/Numerics/Properties/Resources.resx @@ -339,4 +339,10 @@ There is no stop criterium in the collection. + + Name cannot contain a space. name: {0} + + + ddd MMM dd HH:mm:ss yyyy + \ No newline at end of file diff --git a/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabReaderTests.cs b/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabReaderTests.cs index 5b03e108..2fdebe09 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabReaderTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex/IO/MatlabReaderTests.cs @@ -16,13 +16,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.IO { var dmr = new MatlabMatrixReader("./data/Matlab/complex.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(3, matrices.Length); + Assert.AreEqual(3, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); } - var a = matrices[0]; + var a = matrices["a"]; Assert.AreEqual(100, a.RowCount); Assert.AreEqual(100, a.ColumnCount); @@ -34,13 +34,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.IO { var dmr = new MatlabMatrixReader("./data/Matlab/sparse_complex.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(3, matrices.Length); + Assert.AreEqual(3, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(SparseMatrix), matrix.GetType()); } - var a = matrices[0]; + var a = matrices["sa"]; Assert.AreEqual(100, a.RowCount); Assert.AreEqual(100, a.ColumnCount); @@ -52,7 +52,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(30, matrices.Length); + Assert.AreEqual(30, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -76,7 +76,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad", "Au64" }); - Assert.AreEqual(2, matrices.Length); + Assert.AreEqual(2, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -88,11 +88,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad" }); - Assert.AreEqual(1, matrices.Length); - Assert.AreEqual(100, matrices[0].RowCount); - Assert.AreEqual(100, matrices[0].ColumnCount); - AssertHelpers.AlmostEqual(100.431635988639, matrices[0].FrobeniusNorm(), 13); - Assert.AreEqual(typeof(DenseMatrix), matrices[0].GetType()); + Assert.AreEqual(1, matrices.Count); + var ad = matrices["Ad"]; + Assert.AreEqual(100, ad.RowCount); + Assert.AreEqual(100, ad.ColumnCount); + AssertHelpers.AlmostEqual(100.431635988639, ad.FrobeniusNorm(), 13); + Assert.AreEqual(typeof(DenseMatrix), ad.GetType()); } [Test] diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabReaderTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabReaderTests.cs index 6415ec4d..08b15e31 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabReaderTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/IO/MatlabReaderTests.cs @@ -14,13 +14,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.IO { var dmr = new MatlabMatrixReader("./data/Matlab/complex.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(3, matrices.Length); + Assert.AreEqual(3, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); } - var a = matrices[0]; + var a = matrices["a"]; Assert.AreEqual(100, a.RowCount); Assert.AreEqual(100, a.ColumnCount); @@ -32,13 +32,13 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.IO { var dmr = new MatlabMatrixReader("./data/Matlab/sparse_complex.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(3, matrices.Length); + Assert.AreEqual(3, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(SparseMatrix), matrix.GetType()); } - var a = matrices[0]; + var a = matrices["sa"]; Assert.AreEqual(100, a.RowCount); Assert.AreEqual(100, a.ColumnCount); @@ -50,7 +50,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(30, matrices.Length); + Assert.AreEqual(30, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -74,7 +74,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad", "Au64" }); - Assert.AreEqual(2, matrices.Length); + Assert.AreEqual(2, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -86,11 +86,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad" }); - Assert.AreEqual(1, matrices.Length); - Assert.AreEqual(100, matrices[0].RowCount); - Assert.AreEqual(100, matrices[0].ColumnCount); - AssertHelpers.AlmostEqual(100.431635988639, matrices[0].FrobeniusNorm().Real, 6); - Assert.AreEqual(typeof(DenseMatrix), matrices[0].GetType()); + Assert.AreEqual(1, matrices.Count); + var ad = matrices["Ad"]; + Assert.AreEqual(100, ad.RowCount); + Assert.AreEqual(100, ad.ColumnCount); + AssertHelpers.AlmostEqual(100.431635988639, ad.FrobeniusNorm().Real, 6); + Assert.AreEqual(typeof(DenseMatrix), ad.GetType()); } [Test] diff --git a/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabReaderTests.cs b/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabReaderTests.cs index dcfb860d..ce4bf2b3 100644 --- a/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabReaderTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabReaderTests.cs @@ -13,7 +13,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(30, matrices.Length); + Assert.AreEqual(30, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -37,7 +37,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad", "Au64" }); - Assert.AreEqual(2, matrices.Length); + Assert.AreEqual(2, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -49,11 +49,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad" }); - Assert.AreEqual(1, matrices.Length); - Assert.AreEqual(100, matrices[0].RowCount); - Assert.AreEqual(100, matrices[0].ColumnCount); - AssertHelpers.AlmostEqual(100.431635988639, matrices[0].FrobeniusNorm(), 13); - Assert.AreEqual(typeof(DenseMatrix), matrices[0].GetType()); + Assert.AreEqual(1, matrices.Count); + var ad = matrices["Ad"]; + Assert.AreEqual(100, ad.RowCount); + Assert.AreEqual(100, ad.ColumnCount); + AssertHelpers.AlmostEqual(100.431635988639, ad.FrobeniusNorm(), 13); + Assert.AreEqual(typeof(DenseMatrix), ad.GetType()); } [Test] diff --git a/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabWriterTests.cs b/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabWriterTests.cs new file mode 100644 index 00000000..17e96758 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Double/IO/MatlabWriterTests.cs @@ -0,0 +1,115 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Double.IO +{ + using System; + using System.IO; + using LinearAlgebra.Double; + using LinearAlgebra.Double.IO; + using LinearAlgebra.IO; + using MbUnit.Framework; + + [TestFixture] + public class MatlabMatrixWriterTests + { + [Test] + public void Constructor_ThrowsArgumentException() + { + Assert.Throws(() => new MatlabMatrixWriter(string.Empty)); + Assert.Throws(() => new MatlabMatrixWriter(null)); + } + + [Test] + public void WriteMatrices_ThrowsArgumentException() + { + Matrix matrix = new DenseMatrix(1, 1); + var writer = new MatlabMatrixWriter("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(); + } + + [Test] + public void WriteMatrices_ThrowsArgumentNullException() + { + var writer = new MatlabMatrixWriter("somefile4"); + Assert.Throws(() => writer.WriteMatrices(new Matrix[] { null }, new[] { "matrix" })); + Matrix matrix = new DenseMatrix(1, 1); + Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, null)); + writer.Dispose(); + } + + [Test] + public void WriteMatricesTest() + { + Matrix mat1 = new DenseMatrix(5, 4); + for (var i = 0; i < mat1.ColumnCount; i++) + { + mat1[i, i] = i + 1; + } + + Matrix mat2 = new DenseMatrix(4, 5); + for (var i = 0; i < mat2.RowCount; i++) + { + mat2[i, i] = i + 1; + } + + Matrix mat3 = new SparseMatrix(5, 4); + for (var i = 0; i < mat3.ColumnCount; i++) + { + mat3[i, i] = i + 1; + } + + Matrix mat4 = new SparseMatrix(4, 5); + for (var i = 0; i < mat4.RowCount; i++) + { + mat4[i, i] = i + 1; + } + + var write = new[] { mat1, mat2, mat3, mat4 }; + + var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" }; + if (File.Exists("test.mat")) + { + File.Delete("test.mat"); + } + + var writer = new MatlabMatrixWriter("test.mat"); + writer.WriteMatrices(write, names); + writer.Dispose(); + + var reader = new MatlabMatrixReader("test.mat"); + var read = reader.ReadMatrices(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)); + } + } + + [Test] + public void WriteMatrix_ThrowsArgumentException() + { + Matrix matrix = new DenseMatrix(1, 1); + var writer = new MatlabMatrixWriter("somefile1"); + Assert.Throws(() => writer.WriteMatrix(matrix, string.Empty)); + Assert.Throws(() => writer.WriteMatrix(matrix, null)); + writer.Dispose(); + } + + [Test] + public void WriteMatrix_ThrowsArgumentNullException() + { + var writer = new MatlabMatrixWriter("somefile2"); + Assert.Throws(() => writer.WriteMatrix(null, "matrix")); + writer.Dispose(); + } + } +} diff --git a/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs b/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs index 9920d8c9..e306bea8 100644 --- a/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabReaderTests.cs @@ -13,7 +13,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(); - Assert.AreEqual(30, matrices.Length); + Assert.AreEqual(30, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -37,7 +37,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad", "Au64" }); - Assert.AreEqual(2, matrices.Length); + Assert.AreEqual(2, matrices.Count); foreach (var matrix in matrices) { Assert.AreEqual(typeof(DenseMatrix), matrix.GetType()); @@ -49,11 +49,12 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO { var dmr = new MatlabMatrixReader("./data/Matlab/collection.mat"); var matrices = dmr.ReadMatrices(new[] { "Ad" }); - Assert.AreEqual(1, matrices.Length); - Assert.AreEqual(100, matrices[0].RowCount); - Assert.AreEqual(100, matrices[0].ColumnCount); - AssertHelpers.AlmostEqual(100.431635988639f, matrices[0].FrobeniusNorm(), 6); - Assert.AreEqual(typeof(DenseMatrix), matrices[0].GetType()); + Assert.AreEqual(1, matrices.Count); + var ad = matrices["Ad"]; + Assert.AreEqual(100, ad.RowCount); + Assert.AreEqual(100, ad.ColumnCount); + AssertHelpers.AlmostEqual(100.431635988639f, ad.FrobeniusNorm(), 6); + Assert.AreEqual(typeof(DenseMatrix), ad.GetType()); } [Test] diff --git a/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabWriterTests.cs b/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabWriterTests.cs new file mode 100644 index 00000000..db355e21 --- /dev/null +++ b/src/UnitTests/LinearAlgebraTests/Single/IO/MatlabWriterTests.cs @@ -0,0 +1,115 @@ +namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Single.IO +{ + using System; + using System.IO; + using LinearAlgebra.IO; + using LinearAlgebra.Single; + using LinearAlgebra.Single.IO; + using MbUnit.Framework; + + [TestFixture] + public class MatlabMatrixWriterTests + { + [Test] + public void Constructor_ThrowsArgumentException() + { + Assert.Throws(() => new MatlabMatrixWriter(string.Empty)); + Assert.Throws(() => new MatlabMatrixWriter(null)); + } + + [Test] + public void WriteMatrices_ThrowsArgumentException() + { + Matrix matrix = new DenseMatrix(1, 1); + var writer = new MatlabMatrixWriter("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(); + } + + [Test] + public void WriteMatrices_ThrowsArgumentNullException() + { + var writer = new MatlabMatrixWriter("somefile4"); + Assert.Throws(() => writer.WriteMatrices(new Matrix[] { null }, new[] { "matrix" })); + Matrix matrix = new DenseMatrix(1, 1); + Assert.Throws(() => writer.WriteMatrices(new[] { matrix }, null)); + writer.Dispose(); + } + + [Test] + public void WriteMatricesTest() + { + Matrix mat1 = new DenseMatrix(5, 4); + for (var i = 0; i < mat1.ColumnCount; i++) + { + mat1[i, i] = i + 1; + } + + Matrix mat2 = new DenseMatrix(4, 5); + for (var i = 0; i < mat2.RowCount; i++) + { + mat2[i, i] = i + 1; + } + + Matrix mat3 = new SparseMatrix(5, 4); + for (var i = 0; i < mat3.ColumnCount; i++) + { + mat3[i, i] = i + 1; + } + + Matrix mat4 = new SparseMatrix(4, 5); + for (var i = 0; i < mat4.RowCount; i++) + { + mat4[i, i] = i + 1; + } + + var write = new[] { mat1, mat2, mat3, mat4 }; + + var names = new[] { "mat1", "dense_matrix_2", "s1", "sparse2" }; + if (File.Exists("test.mat")) + { + File.Delete("test.mat"); + } + + var writer = new MatlabMatrixWriter("test.mat"); + writer.WriteMatrices(write, names); + writer.Dispose(); + + var reader = new MatlabMatrixReader("test.mat"); + var read = reader.ReadMatrices(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)); + } + } + + [Test] + public void WriteMatrix_ThrowsArgumentException() + { + Matrix matrix = new DenseMatrix(1, 1); + var writer = new MatlabMatrixWriter("somefile1"); + Assert.Throws(() => writer.WriteMatrix(matrix, string.Empty)); + Assert.Throws(() => writer.WriteMatrix(matrix, null)); + writer.Dispose(); + } + + [Test] + public void WriteMatrix_ThrowsArgumentNullException() + { + var writer = new MatlabMatrixWriter("somefile2"); + Assert.Throws(() => writer.WriteMatrix(null, "matrix")); + writer.Dispose(); + } + } +} diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index fb8e9e98..58cfe4e1 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -224,6 +224,7 @@ + @@ -259,6 +260,7 @@ +