diff --git a/.paket/Paket.Restore.targets b/.paket/Paket.Restore.targets index 8d37e28b..f959215a 100644 --- a/.paket/Paket.Restore.targets +++ b/.paket/Paket.Restore.targets @@ -359,7 +359,8 @@ NuspecProperties="$(NuspecProperties)" PackageLicenseFile="$(PackageLicenseFile)" PackageLicenseExpression="$(PackageLicenseExpression)" - PackageLicenseExpressionVersion="$(PackageLicenseExpressionVersion)" /> + PackageLicenseExpressionVersion="$(PackageLicenseExpressionVersion)" + NoDefaultExcludes="$(NoDefaultExcludes)" /> new Tuple(int.Parse(tokens[0]) - 1, int.Parse(tokens[1]) - 1, parse(2, tokens))); + var indexed = ReadTokenLines(reader).Select(tokens => (int.Parse(tokens[0]) - 1, int.Parse(tokens[1]) - 1, parse(2, tokens))); return Matrix.Build.SparseOfIndexed(rows, cols, symmetry == MatrixMarketSymmetry.General ? indexed : ExpandSparse(symmetry, indexed)); } @@ -207,7 +207,7 @@ namespace MathNet.Numerics.Data.Text if (sparse) { - var indexedSeq = ReadTokenLines(reader).Select(tokens => new Tuple(int.Parse(tokens[0]) - 1, parse(1, tokens))); + var indexedSeq = ReadTokenLines(reader).Select(tokens => (int.Parse(tokens[0]) - 1, parse(1, tokens))); return Vector.Build.SparseOfIndexed(length, indexedSeq); } @@ -326,7 +326,7 @@ namespace MathNet.Numerics.Data.Text } } - static IEnumerable> ExpandSparse(MatrixMarketSymmetry symmetry, IEnumerable> indexedValues) + static IEnumerable<(int, int, T)> ExpandSparse(MatrixMarketSymmetry symmetry, IEnumerable<(int, int, T)> indexedValues) { var map = CreateSymmetryMap(symmetry); foreach (var x in indexedValues) @@ -334,7 +334,7 @@ namespace MathNet.Numerics.Data.Text yield return x; if (x.Item1 != x.Item2) { - yield return new Tuple(x.Item2, x.Item1, map(x.Item3)); + yield return (x.Item2, x.Item1, map(x.Item3)); } } } diff --git a/src/FSharp/LinearAlgebra.Matrix.fs b/src/FSharp/LinearAlgebra.Matrix.fs index 300eecac..53f2f11d 100644 --- a/src/FSharp/LinearAlgebra.Matrix.fs +++ b/src/FSharp/LinearAlgebra.Matrix.fs @@ -52,25 +52,25 @@ module Matrix = let inline toSeq (m: #Matrix<_>) = m.Enumerate(Zeros.Include) /// Transform a matrix into an indexed sequence. - let inline toSeqi (m: #Matrix<_>) = m.EnumerateIndexed(Zeros.Include) + let inline toSeqi (m: #Matrix<_>) = m.EnumerateIndexed(Zeros.Include) |> Seq.map (fun t -> t.ToTuple()) /// Transform a matrix into a sequence where zero-values are skipped. Skipping zeros is efficient on sparse data. let inline toSeqSkipZeros (m: #Matrix<_>) = m.Enumerate(Zeros.AllowSkip) /// Transform a matrix into an indexed sequence where zero-values are skipped. Skipping zeros is efficient on sparse data. - let inline toSeqiSkipZeros (m: #Matrix<_>) = m.EnumerateIndexed(Zeros.AllowSkip) + let inline toSeqiSkipZeros (m: #Matrix<_>) = m.EnumerateIndexed(Zeros.AllowSkip) |> Seq.map (fun t -> t.ToTuple()) /// Transform a matrix into a column sequence. let inline toColSeq (m: #Matrix<_>) = m.EnumerateColumns() /// Transform a matrix into an indexed column sequence. - let inline toColSeqi (m: #Matrix<_>) = m.EnumerateColumnsIndexed() + let inline toColSeqi (m: #Matrix<_>) = m.EnumerateColumnsIndexed() |> Seq.map (fun t -> t.ToTuple()) /// Transform a matrix into a row sequence. let inline toRowSeq (m: #Matrix<_>) = m.EnumerateRows() /// Transform a matrix into an indexed row sequence. - let inline toRowSeqi (m: #Matrix<_>) = m.EnumerateRowsIndexed() + let inline toRowSeqi (m: #Matrix<_>) = m.EnumerateRowsIndexed() |> Seq.map (fun t -> t.ToTuple()) /// Applies a function to all elements of the matrix. diff --git a/src/FSharp/LinearAlgebra.Vector.fs b/src/FSharp/LinearAlgebra.Vector.fs index 353884fd..04a8e316 100644 --- a/src/FSharp/LinearAlgebra.Vector.fs +++ b/src/FSharp/LinearAlgebra.Vector.fs @@ -49,13 +49,13 @@ module Vector = let inline toSeq (v: #Vector<_>) = v.Enumerate(Zeros.Include) /// Transform a vector into an indexed sequence. - let inline toSeqi (v: #Vector<_>) = v.EnumerateIndexed(Zeros.Include) + let inline toSeqi (v: #Vector<_>) = v.EnumerateIndexed(Zeros.Include) |> Seq.map (fun t -> t.ToTuple()) /// Transform a vector into a sequence where zero-values are skipped. Skipping zeros is efficient on sparse data. let inline toSeqSkipZeros (v: #Vector<_>) = v.Enumerate(Zeros.AllowSkip) /// Transform a vector into an indexed sequence where zero-values are skipped. Skipping zeros is efficient on sparse data. - let inline toSeqiSkipZeros (v: #Vector<_>) = v.EnumerateIndexed(Zeros.AllowSkip) + let inline toSeqiSkipZeros (v: #Vector<_>) = v.EnumerateIndexed(Zeros.AllowSkip) |> Seq.map (fun t -> t.ToTuple()) /// Applies a function to all elements of the vector. diff --git a/src/Numerics.Tests/GenerateTests.cs b/src/Numerics.Tests/GenerateTests.cs index 5f01b19e..175bb4f2 100644 --- a/src/Numerics.Tests/GenerateTests.cs +++ b/src/Numerics.Tests/GenerateTests.cs @@ -222,8 +222,8 @@ namespace MathNet.Numerics.UnitTests public void UnfoldConsistentWithSequence() { Assert.That( - Generate.UnfoldSequence((s => new Tuple(s + 1, s + 1)), 0).Take(250).ToArray(), - Is.EqualTo(Generate.Unfold(250, (s => new Tuple(s + 1, s + 1)), 0)).AsCollection); + Generate.UnfoldSequence((s => (s + 1, s + 1)), 0).Take(250).ToArray(), + Is.EqualTo(Generate.Unfold(250, (s => (s + 1, s + 1)), 0)).AsCollection); } [Test] @@ -239,11 +239,11 @@ namespace MathNet.Numerics.UnitTests { Assert.That( Generate.FibonacciSequence().Take(250).ToArray(), - Is.EqualTo(new[] { BigInteger.Zero, BigInteger.One }.Concat(Generate.Unfold(248, (s => + Is.EqualTo(new[] { BigInteger.Zero, BigInteger.One }.Concat(Generate.Unfold(248, s => { var z = s.Item1 + s.Item2; - return new Tuple>(z, new Tuple(s.Item2, z)); - }), new Tuple(BigInteger.Zero, BigInteger.One)))).AsCollection); + return (z, (s.Item2, z)); + }, (BigInteger.Zero, BigInteger.One)))).AsCollection); } } } diff --git a/src/Numerics/Generate.cs b/src/Numerics/Generate.cs index 4258f961..570eafd4 100644 --- a/src/Numerics/Generate.cs +++ b/src/Numerics/Generate.cs @@ -774,9 +774,25 @@ namespace MathNet.Numerics var data = new T[length]; for (int i = 0; i < data.Length; i++) { - Tuple next = f(state); - data[i] = next.Item1; - state = next.Item2; + (data[i], state) = f(state); + } + return data; + } + + /// + /// Generate samples generated by the given computation. + /// + public static T[] Unfold(int length, Func f, TState state) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + var data = new T[length]; + for (int i = 0; i < data.Length; i++) + { + (data[i], state) = f(state); } return data; } @@ -788,9 +804,22 @@ namespace MathNet.Numerics { while (true) { - Tuple next = f(state); - state = next.Item2; - yield return next.Item1; + var (item, nextState) = f(state); + state = nextState; + yield return item; + } + } + + /// + /// Generate an infinite sequence generated by the given computation. + /// + public static IEnumerable UnfoldSequence(Func f, TState state) + { + while (true) + { + var (item, nextState) = f(state); + state = nextState; + yield return item; } } diff --git a/src/Numerics/LinearAlgebra/Builder.cs b/src/Numerics/LinearAlgebra/Builder.cs index bdab2ae3..3371f702 100644 --- a/src/Numerics/LinearAlgebra/Builder.cs +++ b/src/Numerics/LinearAlgebra/Builder.cs @@ -565,6 +565,17 @@ namespace MathNet.Numerics.LinearAlgebra return Dense(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new dense matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public Matrix DenseOfIndexed(int rows, int columns, IEnumerable<(int, int, T)> enumerable) + { + return Dense(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in column-major order (column by column). @@ -911,6 +922,17 @@ namespace MathNet.Numerics.LinearAlgebra return Sparse(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new sparse matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public Matrix SparseOfIndexed(int rows, int columns, IEnumerable<(int, int, T)> enumerable) + { + return Sparse(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). @@ -1537,6 +1559,17 @@ namespace MathNet.Numerics.LinearAlgebra return Dense(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new dense vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public Vector DenseOfIndexed(int length, IEnumerable<(int, T)> enumerable) + { + return Dense(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new sparse vector straight from an initialized vector storage instance. /// The storage is used directly without copying. @@ -1611,6 +1644,17 @@ namespace MathNet.Numerics.LinearAlgebra { return Sparse(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + + /// + /// Create a new sparse vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public Vector SparseOfIndexed(int length, IEnumerable<(int, T)> enumerable) + { + return Sparse(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } } internal static class BuilderInstance where T : struct, IEquatable, IFormattable diff --git a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs index baf52cdc..f4684392 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs @@ -145,6 +145,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new dense matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DenseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, Complex)> enumerable) + { + return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in column-major order (column by column). diff --git a/src/Numerics/LinearAlgebra/Complex/DenseVector.cs b/src/Numerics/LinearAlgebra/Complex/DenseVector.cs index 2510345d..16d3270a 100644 --- a/src/Numerics/LinearAlgebra/Complex/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex/DenseVector.cs @@ -131,6 +131,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new dense vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static DenseVector OfIndexedEnumerable(int length, IEnumerable<(int, Complex)> enumerable) + { + return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new dense vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs index 55dd8487..8fe960d5 100644 --- a/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs @@ -148,6 +148,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); } + /// + /// Create a new diagonal matrix and initialize each diagonal value from the provided indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DiagonalMatrix OfIndexedDiagonal(int rows, int columns, IEnumerable<(int, Complex)> diagonal) + { + return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); + } + /// /// Create a new diagonal matrix and initialize each diagonal value from the provided enumerable. /// This new matrix will be independent from the enumerable. diff --git a/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs index 19de6af3..1bb320dd 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseMatrix.cs @@ -118,6 +118,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new sparse matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static SparseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, Complex)> enumerable) + { + return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). diff --git a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs index aaf3d691..d2158364 100644 --- a/src/Numerics/LinearAlgebra/Complex/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex/SparseVector.cs @@ -107,6 +107,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new sparse vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static SparseVector OfIndexedEnumerable(int length, IEnumerable<(int, Complex)> enumerable) + { + return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new sparse vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs index 54943de0..3d402371 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs @@ -145,6 +145,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new dense matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DenseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, Complex32)> enumerable) + { + return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in column-major order (column by column). diff --git a/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs b/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs index 139b7769..8d24bd42 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DenseVector.cs @@ -131,6 +131,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new dense vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static DenseVector OfIndexedEnumerable(int length, IEnumerable<(int, Complex32)> enumerable) + { + return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new dense vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs index 4bdd9a97..9549ae80 100644 --- a/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs @@ -148,6 +148,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); } + /// + /// Create a new diagonal matrix and initialize each diagonal value from the provided indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DiagonalMatrix OfIndexedDiagonal(int rows, int columns, IEnumerable<(int, Complex32)> diagonal) + { + return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); + } + /// /// Create a new diagonal matrix and initialize each diagonal value from the provided enumerable. /// This new matrix will be independent from the enumerable. diff --git a/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs index 2680303c..fd59a954 100644 --- a/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/SparseMatrix.cs @@ -118,6 +118,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new sparse matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static SparseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, Complex32)> enumerable) + { + return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). diff --git a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs index d7c10483..2b815143 100644 --- a/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Complex32/SparseVector.cs @@ -107,6 +107,17 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new sparse vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static SparseVector OfIndexedEnumerable(int length, IEnumerable<(int, Complex32)> enumerable) + { + return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new sparse vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/CreateMatrix.cs b/src/Numerics/LinearAlgebra/CreateMatrix.cs index e820cf16..db269dde 100644 --- a/src/Numerics/LinearAlgebra/CreateMatrix.cs +++ b/src/Numerics/LinearAlgebra/CreateMatrix.cs @@ -281,6 +281,18 @@ namespace MathNet.Numerics.LinearAlgebra return Matrix.Build.DenseOfIndexed(rows, columns, enumerable); } + /// + /// Create a new dense matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static Matrix DenseOfIndexed(int rows, int columns, IEnumerable<(int, int, T)> enumerable) + where T : struct, IEquatable, IFormattable + { + return Matrix.Build.DenseOfIndexed(rows, columns, enumerable); + } + /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in column-major order (column by column). @@ -605,6 +617,18 @@ namespace MathNet.Numerics.LinearAlgebra return Matrix.Build.SparseOfIndexed(rows, columns, enumerable); } + /// + /// Create a new sparse matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static Matrix SparseOfIndexed(int rows, int columns, IEnumerable<(int, int, T)> enumerable) + where T : struct, IEquatable, IFormattable + { + return Matrix.Build.SparseOfIndexed(rows, columns, enumerable); + } + /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). diff --git a/src/Numerics/LinearAlgebra/CreateVector.cs b/src/Numerics/LinearAlgebra/CreateVector.cs index cc585f61..0094af91 100644 --- a/src/Numerics/LinearAlgebra/CreateVector.cs +++ b/src/Numerics/LinearAlgebra/CreateVector.cs @@ -224,6 +224,18 @@ namespace MathNet.Numerics.LinearAlgebra return Vector.Build.DenseOfIndexed(length, enumerable); } + /// + /// Create a new dense vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static Vector DenseOfIndexed(int length, IEnumerable<(int, T)> enumerable) + where T : struct, IEquatable, IFormattable + { + return Vector.Build.DenseOfIndexed(length, enumerable); + } + /// /// Create a new sparse vector straight from an initialized vector storage instance. /// The storage is used directly without copying. @@ -308,5 +320,17 @@ namespace MathNet.Numerics.LinearAlgebra { return Vector.Build.SparseOfIndexed(length, enumerable); } + + /// + /// Create a new sparse vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static Vector SparseOfIndexed(int length, IEnumerable<(int, T)> enumerable) + where T : struct, IEquatable, IFormattable + { + return Vector.Build.SparseOfIndexed(length, enumerable); + } } } diff --git a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs index 63d7555b..0fe6f632 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseMatrix.cs @@ -143,6 +143,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new dense matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DenseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, double)> enumerable) + { + return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in column-major order (column by column). diff --git a/src/Numerics/LinearAlgebra/Double/DenseVector.cs b/src/Numerics/LinearAlgebra/Double/DenseVector.cs index 2bffc342..3fc305c7 100644 --- a/src/Numerics/LinearAlgebra/Double/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/DenseVector.cs @@ -130,6 +130,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new dense vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static DenseVector OfIndexedEnumerable(int length, IEnumerable<(int,double)> enumerable) + { + return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new dense vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs index d3be0e97..4a9f8ac8 100644 --- a/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/DiagonalMatrix.cs @@ -146,6 +146,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); } + /// + /// Create a new diagonal matrix and initialize each diagonal value from the provided indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DiagonalMatrix OfIndexedDiagonal(int rows, int columns, IEnumerable<(int, double)> diagonal) + { + return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); + } + /// /// Create a new diagonal matrix and initialize each diagonal value from the provided enumerable. /// This new matrix will be independent from the enumerable. diff --git a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs index 00b474ca..7e4c270f 100644 --- a/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Double/SparseMatrix.cs @@ -116,6 +116,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new sparse matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static SparseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, double)> enumerable) + { + return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). diff --git a/src/Numerics/LinearAlgebra/Double/SparseVector.cs b/src/Numerics/LinearAlgebra/Double/SparseVector.cs index 6dae29ee..389685cd 100644 --- a/src/Numerics/LinearAlgebra/Double/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Double/SparseVector.cs @@ -107,6 +107,17 @@ namespace MathNet.Numerics.LinearAlgebra.Double return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new sparse vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static SparseVector OfIndexedEnumerable(int length, IEnumerable<(int, double)> enumerable) + { + return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new sparse vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Matrix.cs b/src/Numerics/LinearAlgebra/Matrix.cs index 453ab6d5..3497e2fb 100644 --- a/src/Numerics/LinearAlgebra/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Matrix.cs @@ -1471,7 +1471,7 @@ namespace MathNet.Numerics.LinearAlgebra /// and the third value being the value of the element at that index. /// The enumerator will include all values, even if they are zero. /// - public IEnumerable> EnumerateIndexed() + public IEnumerable<(int, int, T)> EnumerateIndexed() { return Storage.EnumerateIndexed(); } @@ -1484,7 +1484,7 @@ namespace MathNet.Numerics.LinearAlgebra /// and the third value being the value of the element at that index. /// The enumerator will include all values, even if they are zero. /// - public IEnumerable> EnumerateIndexed(Zeros zeros = Zeros.Include) + public IEnumerable<(int, int, T)> EnumerateIndexed(Zeros zeros = Zeros.Include) { switch (zeros) { @@ -1527,11 +1527,11 @@ namespace MathNet.Numerics.LinearAlgebra /// The enumerator returns a Tuple with the first value being the column index /// and the second value being the value of the column at that index. /// - public IEnumerable>> EnumerateColumnsIndexed() + public IEnumerable<(int, Vector)> EnumerateColumnsIndexed() { for (var i = 0; i < ColumnCount; i++) { - yield return new Tuple>(i, Column(i)); + yield return (i, Column(i)); } } @@ -1544,12 +1544,12 @@ namespace MathNet.Numerics.LinearAlgebra /// The enumerator returns a Tuple with the first value being the column index /// and the second value being the value of the column at that index. /// - public IEnumerable>> EnumerateColumnsIndexed(int index, int length) + public IEnumerable<(int, Vector)> EnumerateColumnsIndexed(int index, int length) { var maxIndex = Math.Min(index + length, ColumnCount); for (var i = Math.Max(index, 0); i < maxIndex; i++) { - yield return new Tuple>(i, Column(i)); + yield return (i, Column(i)); } } @@ -1585,11 +1585,11 @@ namespace MathNet.Numerics.LinearAlgebra /// The enumerator returns a Tuple with the first value being the row index /// and the second value being the value of the row at that index. /// - public IEnumerable>> EnumerateRowsIndexed() + public IEnumerable<(int, Vector)> EnumerateRowsIndexed() { for (var i = 0; i < RowCount; i++) { - yield return new Tuple>(i, Row(i)); + yield return (i, Row(i)); } } @@ -1602,12 +1602,12 @@ namespace MathNet.Numerics.LinearAlgebra /// The enumerator returns a Tuple with the first value being the row index /// and the second value being the value of the row at that index. /// - public IEnumerable>> EnumerateRowsIndexed(int index, int length) + public IEnumerable<(int, Vector)> EnumerateRowsIndexed(int index, int length) { var maxIndex = Math.Min(index + length, RowCount); for (var i = Math.Max(index, 0); i < maxIndex; i++) { - yield return new Tuple>(i, Row(i)); + yield return (i, Row(i)); } } diff --git a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs index 29d9244b..94f2a426 100644 --- a/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DenseMatrix.cs @@ -143,6 +143,17 @@ namespace MathNet.Numerics.LinearAlgebra.Single return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new dense matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DenseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, float)> enumerable) + { + return new DenseMatrix(DenseColumnMajorMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in column-major order (column by column). diff --git a/src/Numerics/LinearAlgebra/Single/DenseVector.cs b/src/Numerics/LinearAlgebra/Single/DenseVector.cs index d4bede75..97f952a6 100644 --- a/src/Numerics/LinearAlgebra/Single/DenseVector.cs +++ b/src/Numerics/LinearAlgebra/Single/DenseVector.cs @@ -131,6 +131,17 @@ namespace MathNet.Numerics.LinearAlgebra.Single return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new dense vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static DenseVector OfIndexedEnumerable(int length, IEnumerable<(int, float)> enumerable) + { + return new DenseVector(DenseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new dense vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs index 187ff946..c2a45203 100644 --- a/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/DiagonalMatrix.cs @@ -146,6 +146,17 @@ namespace MathNet.Numerics.LinearAlgebra.Single return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); } + /// + /// Create a new diagonal matrix and initialize each diagonal value from the provided indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static DiagonalMatrix OfIndexedDiagonal(int rows, int columns, IEnumerable<(int, float)> diagonal) + { + return new DiagonalMatrix(DiagonalMatrixStorage.OfIndexedEnumerable(rows, columns, diagonal)); + } + /// /// Create a new diagonal matrix and initialize each diagonal value from the provided enumerable. /// This new matrix will be independent from the enumerable. diff --git a/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs b/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs index 462713cb..1c2784d1 100644 --- a/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs +++ b/src/Numerics/LinearAlgebra/Single/SparseMatrix.cs @@ -117,6 +117,17 @@ namespace MathNet.Numerics.LinearAlgebra.Single return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); } + /// + /// Create a new sparse matrix as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new matrix will be independent from the enumerable. + /// A new memory block will be allocated for storing the matrix. + /// + public static SparseMatrix OfIndexed(int rows, int columns, IEnumerable<(int, int, float)> enumerable) + { + return new SparseMatrix(SparseCompressedRowMatrixStorage.OfIndexedEnumerable(rows, columns, enumerable)); + } + /// /// Create a new sparse matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). diff --git a/src/Numerics/LinearAlgebra/Single/SparseVector.cs b/src/Numerics/LinearAlgebra/Single/SparseVector.cs index 5e5aa4a6..bd72062e 100644 --- a/src/Numerics/LinearAlgebra/Single/SparseVector.cs +++ b/src/Numerics/LinearAlgebra/Single/SparseVector.cs @@ -107,6 +107,17 @@ namespace MathNet.Numerics.LinearAlgebra.Single return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); } + /// + /// Create a new sparse vector as a copy of the given indexed enumerable. + /// Keys must be provided at most once, zero is assumed if a key is omitted. + /// This new vector will be independent from the enumerable. + /// A new memory block will be allocated for storing the vector. + /// + public static SparseVector OfIndexedEnumerable(int length, IEnumerable<(int, float)> enumerable) + { + return new SparseVector(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); + } + /// /// Create a new sparse vector and initialize each value using the provided value. /// diff --git a/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs index 23dfb94d..d2e05414 100644 --- a/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/DenseColumnMajorMatrixStorage.cs @@ -329,9 +329,19 @@ namespace MathNet.Numerics.LinearAlgebra.Storage public static DenseColumnMajorMatrixStorage OfIndexedEnumerable(int rows, int columns, IEnumerable> data) { var array = new T[rows*columns]; - foreach (var item in data) + foreach (var (i,j,x) in data) { - array[(item.Item2*rows) + item.Item1] = item.Item3; + array[j * rows + i] = x; + } + return new DenseColumnMajorMatrixStorage(rows, columns, array); + } + + public static DenseColumnMajorMatrixStorage OfIndexedEnumerable(int rows, int columns, IEnumerable<(int, int, T)> data) + { + var array = new T[rows*columns]; + foreach (var (i,j,x) in data) + { + array[j * rows + i] = x; } return new DenseColumnMajorMatrixStorage(rows, columns, array); } @@ -669,14 +679,14 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Data; } - public override IEnumerable> EnumerateIndexed() + public override IEnumerable<(int, int, T)> EnumerateIndexed() { int index = 0; for (int j = 0; j < ColumnCount; j++) { for (int i = 0; i < RowCount; i++) { - yield return new Tuple(i, j, Data[index]); + yield return (i, j, Data[index]); index++; } } @@ -687,7 +697,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Data.Where(x => !Zero.Equals(x)); } - public override IEnumerable> EnumerateNonZeroIndexed() + public override IEnumerable<(int, int, T)> EnumerateNonZeroIndexed() { int index = 0; for (int j = 0; j < ColumnCount; j++) @@ -697,7 +707,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage var x = Data[index]; if (!Zero.Equals(x)) { - yield return new Tuple(i, j, x); + yield return (i, j, x); } index++; } @@ -712,8 +722,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage { if (predicate(Data[i])) { - int row, column; - RowColumnAtIndex(i, out row, out column); + RowColumnAtIndex(i, out int row, out int column); return new Tuple(row, column, Data[i]); } } @@ -729,8 +738,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage { if (predicate(Data[i], otherData[i])) { - int row, column; - RowColumnAtIndex(i, out row, out column); + RowColumnAtIndex(i, out int row, out int column); return new Tuple(row, column, Data[i], otherData[i]); } diff --git a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs index 6fe53ce8..01bbae22 100644 --- a/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/DenseVectorStorage.cs @@ -171,9 +171,24 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } var array = new T[length]; - foreach (var item in data) + foreach (var (index, value) in data) { - array[item.Item1] = item.Item2; + array[index] = value; + } + return new DenseVectorStorage(array.Length, array); + } + + public static DenseVectorStorage OfIndexedEnumerable(int length, IEnumerable<(int, T)> data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + var array = new T[length]; + foreach (var (index, value) in data) + { + array[index] = value; } return new DenseVectorStorage(array.Length, array); } @@ -338,9 +353,9 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Data; } - public override IEnumerable> EnumerateIndexed() + public override IEnumerable<(int, T)> EnumerateIndexed() { - return Data.Select((t, i) => new Tuple(i, t)); + return Data.Select((t, i) => (i, t)); } public override IEnumerable EnumerateNonZero() @@ -348,13 +363,13 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Data.Where(x => !Zero.Equals(x)); } - public override IEnumerable> EnumerateNonZeroIndexed() + public override IEnumerable<(int, T)> EnumerateNonZeroIndexed() { for (var i = 0; i < Data.Length; i++) { if (!Zero.Equals(Data[i])) { - yield return new Tuple(i, Data[i]); + yield return (i, Data[i]); } } } diff --git a/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs index c907f23b..5bcdecd8 100644 --- a/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs @@ -237,9 +237,24 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } var storage = new DiagonalMatrixStorage(rows, columns); - foreach (var item in data) + foreach (var (i,x) in data) { - storage.Data[item.Item1] = item.Item2; + storage.Data[i] = x; + } + return storage; + } + + public static DiagonalMatrixStorage OfIndexedEnumerable(int rows, int columns, IEnumerable<(int, T)> data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + var storage = new DiagonalMatrixStorage(rows, columns); + foreach (var (i,x) in data) + { + storage.Data[i] = x; } return storage; } @@ -559,16 +574,14 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public override IEnumerable> EnumerateIndexed() + public override IEnumerable<(int, int, T)> EnumerateIndexed() { for (int j = 0; j < ColumnCount; j++) { for (int i = 0; i < RowCount; i++) { // PERF: consider to break up loop to avoid branching - yield return i == j - ? new Tuple(i, i, Data[i]) - : new Tuple(i, j, Zero); + yield return (i, j, i == j ? Data[i] : Zero); } } } @@ -578,13 +591,13 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Data.Where(x => !Zero.Equals(x)); } - public override IEnumerable> EnumerateNonZeroIndexed() + public override IEnumerable<(int, int, T)> EnumerateNonZeroIndexed() { for (int i = 0; i < Data.Length; i++) { if (!Zero.Equals(Data[i])) { - yield return new Tuple(i, i, Data[i]); + yield return (i, i, Data[i]); } } } diff --git a/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs index d173aaf7..bfc5bade 100644 --- a/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/MatrixStorage.cs @@ -623,13 +623,13 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public virtual IEnumerable> EnumerateIndexed() + public virtual IEnumerable<(int, int, T)> EnumerateIndexed() { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { - yield return new Tuple(i, j, At(i, j)); + yield return (i, j, At(i, j)); } } } @@ -649,7 +649,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public virtual IEnumerable> EnumerateNonZeroIndexed() + public virtual IEnumerable<(int, int, T)> EnumerateNonZeroIndexed() { for (int i = 0; i < RowCount; i++) { @@ -658,7 +658,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage var x = At(i, j); if (!Zero.Equals(x)) { - yield return new Tuple(i, j, x); + yield return (i, j, x); } } } diff --git a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs index e0cb9a49..198121db 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/SparseCompressedRowMatrixStorage.cs @@ -765,12 +765,52 @@ namespace MathNet.Numerics.LinearAlgebra.Storage public static SparseCompressedRowMatrixStorage OfIndexedEnumerable(int rows, int columns, IEnumerable> data) { var trows = new List>[rows]; - foreach (var item in data) + foreach (var (i,j,x) in data) { - if (!Zero.Equals(item.Item3)) + if (!Zero.Equals(x)) + { + var row = trows[i] ?? (trows[i] = new List>()); + row.Add(new Tuple(j, x)); + } + } + + var storage = new SparseCompressedRowMatrixStorage(rows, columns); + var rowPointers = storage.RowPointers; + var columnIndices = new List(); + var values = new List(); + + int index = 0; + for (int row = 0; row < rows; row++) + { + rowPointers[row] = index; + var trow = trows[row]; + if (trow != null) + { + trow.Sort(); + foreach (var item in trow) + { + values.Add(item.Item2); + columnIndices.Add(item.Item1); + index++; + } + } + } + + rowPointers[rows] = values.Count; + storage.ColumnIndices = columnIndices.ToArray(); + storage.Values = values.ToArray(); + return storage; + } + + public static SparseCompressedRowMatrixStorage OfIndexedEnumerable(int rows, int columns, IEnumerable<(int, int, T)> data) + { + var trows = new List>[rows]; + foreach (var (i,j,x) in data) + { + if (!Zero.Equals(x)) { - var row = trows[item.Item1] ?? (trows[item.Item1] = new List>()); - row.Add(new Tuple(item.Item2, item.Item3)); + var row = trows[i] ?? (trows[i] = new List>()); + row.Add(new Tuple(j, x)); } } @@ -1653,16 +1693,14 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public override IEnumerable> EnumerateIndexed() + public override IEnumerable<(int, int, T)> EnumerateIndexed() { int k = 0; for (int row = 0; row < RowCount; row++) { for (int col = 0; col < ColumnCount; col++) { - yield return k < RowPointers[row + 1] && ColumnIndices[k] == col - ? new Tuple(row, col, Values[k++]) - : new Tuple(row, col, Zero); + yield return (row, col, k < RowPointers[row + 1] && ColumnIndices[k] == col ? Values[k++] : Zero); } } } @@ -1672,7 +1710,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Values.Take(ValueCount).Where(x => !Zero.Equals(x)); } - public override IEnumerable> EnumerateNonZeroIndexed() + public override IEnumerable<(int, int, T)> EnumerateNonZeroIndexed() { for (int row = 0; row < RowCount; row++) { @@ -1682,7 +1720,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage { if (!Zero.Equals(Values[j])) { - yield return new Tuple(row, ColumnIndices[j], Values[j]); + yield return (row, ColumnIndices[j], Values[j]); } } } diff --git a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs index 9aa1a375..ae4de743 100644 --- a/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/SparseVectorStorage.cs @@ -397,12 +397,42 @@ namespace MathNet.Numerics.LinearAlgebra.Storage var indices = new List(); var values = new List(); - foreach (var item in data) + foreach (var (i, x) in data) { - if (!Zero.Equals(item.Item2)) + if (!Zero.Equals(x)) { - values.Add(item.Item2); - indices.Add(item.Item1); + values.Add(x); + indices.Add(i); + } + } + + var indicesArray = indices.ToArray(); + var valuesArray = values.ToArray(); + Sorting.Sort(indicesArray, valuesArray); + + return new SparseVectorStorage(length) + { + Indices = indicesArray, + Values = valuesArray, + ValueCount = values.Count + }; + } + + public static SparseVectorStorage OfIndexedEnumerable(int length, IEnumerable<(int, T)> data) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + + var indices = new List(); + var values = new List(); + foreach (var (i, x) in data) + { + if (!Zero.Equals(x)) + { + values.Add(x); + indices.Add(i); } } @@ -631,14 +661,12 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public override IEnumerable> EnumerateIndexed() + public override IEnumerable<(int, T)> EnumerateIndexed() { int k = 0; for (int i = 0; i < Length; i++) { - yield return k < ValueCount && Indices[k] == i - ? new Tuple(i, Values[k++]) - : new Tuple(i, Zero); + yield return (i, k < ValueCount && Indices[k] == i ? Values[k++] : Zero); } } @@ -647,13 +675,13 @@ namespace MathNet.Numerics.LinearAlgebra.Storage return Values.Take(ValueCount).Where(x => !Zero.Equals(x)); } - public override IEnumerable> EnumerateNonZeroIndexed() + public override IEnumerable<(int, T)> EnumerateNonZeroIndexed() { for (var i = 0; i < ValueCount; i++) { if (!Zero.Equals(Values[i])) { - yield return new Tuple(Indices[i], Values[i]); + yield return (Indices[i], Values[i]); } } } diff --git a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs index 48c3f81a..7cac68c4 100644 --- a/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/VectorStorage.cs @@ -400,11 +400,11 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public virtual IEnumerable> EnumerateIndexed() + public virtual IEnumerable<(int, T)> EnumerateIndexed() { for (var i = 0; i < Length; i++) { - yield return new Tuple(i, At(i)); + yield return (i, At(i)); } } @@ -420,14 +420,14 @@ namespace MathNet.Numerics.LinearAlgebra.Storage } } - public virtual IEnumerable> EnumerateNonZeroIndexed() + public virtual IEnumerable<(int, T)> EnumerateNonZeroIndexed() { for (var i = 0; i < Length; i++) { var x = At(i); if (!Zero.Equals(x)) { - yield return new Tuple(i, x); + yield return (i, x); } } } diff --git a/src/Numerics/LinearAlgebra/Vector.cs b/src/Numerics/LinearAlgebra/Vector.cs index 7114b2d7..08c7d952 100644 --- a/src/Numerics/LinearAlgebra/Vector.cs +++ b/src/Numerics/LinearAlgebra/Vector.cs @@ -323,7 +323,7 @@ namespace MathNet.Numerics.LinearAlgebra /// and the second value being the value of the element at that index. /// The enumerator will include all values, even if they are zero. /// - public IEnumerable> EnumerateIndexed() + public IEnumerable<(int, T)> EnumerateIndexed() { return Storage.EnumerateIndexed(); } @@ -336,7 +336,7 @@ namespace MathNet.Numerics.LinearAlgebra /// and the second value being the value of the element at that index. /// The enumerator will include all values, even if they are zero. /// - public IEnumerable> EnumerateIndexed(Zeros zeros = Zeros.Include) + public IEnumerable<(int, T)> EnumerateIndexed(Zeros zeros = Zeros.Include) { switch (zeros) { diff --git a/src/Numerics/LinearRegression/MultipleRegression.cs b/src/Numerics/LinearRegression/MultipleRegression.cs index 98a0d304..cf702aad 100644 --- a/src/Numerics/LinearRegression/MultipleRegression.cs +++ b/src/Numerics/LinearRegression/MultipleRegression.cs @@ -208,8 +208,21 @@ namespace MathNet.Numerics.LinearRegression /// Best fitting list of model parameters β for each element in the predictor-arrays. public static T[] NormalEquations(IEnumerable> samples, bool intercept = false) where T : struct, IEquatable, IFormattable { - var xy = samples.UnpackSinglePass(); - return NormalEquations(xy.Item1, xy.Item2, intercept); + var (u, v) = samples.UnpackSinglePass(); + return NormalEquations(u, v, intercept); + } + + /// + /// Find the model parameters β such that their linear combination with all predictor-arrays in X become as close to their response in Y as possible, with least squares residuals. + /// Uses the cholesky decomposition of the normal equations. + /// + /// Sequence of predictor-arrays and their response. + /// True if an intercept should be added as first artificial predictor value. Default = false. + /// Best fitting list of model parameters β for each element in the predictor-arrays. + public static T[] NormalEquations(IEnumerable<(T[], T)> samples, bool intercept = false) where T : struct, IEquatable, IFormattable + { + var (u, v) = samples.UnpackSinglePass(); + return NormalEquations(u, v, intercept); } /// @@ -294,8 +307,21 @@ namespace MathNet.Numerics.LinearRegression /// Best fitting list of model parameters β for each element in the predictor-arrays. public static T[] QR(IEnumerable> samples, bool intercept = false) where T : struct, IEquatable, IFormattable { - var xy = samples.UnpackSinglePass(); - return QR(xy.Item1, xy.Item2, intercept); + var (u, v) = samples.UnpackSinglePass(); + return QR(u, v, intercept); + } + + /// + /// Find the model parameters β such that their linear combination with all predictor-arrays in X become as close to their response in Y as possible, with least squares residuals. + /// Uses an orthogonal decomposition and is therefore more numerically stable than the normal equations but also slower. + /// + /// Sequence of predictor-arrays and their response. + /// True if an intercept should be added as first artificial predictor value. Default = false. + /// Best fitting list of model parameters β for each element in the predictor-arrays. + public static T[] QR(IEnumerable<(T[], T)> samples, bool intercept = false) where T : struct, IEquatable, IFormattable + { + var (u, v) = samples.UnpackSinglePass(); + return QR(u, v, intercept); } /// @@ -380,8 +406,21 @@ namespace MathNet.Numerics.LinearRegression /// Best fitting list of model parameters β for each element in the predictor-arrays. public static T[] Svd(IEnumerable> samples, bool intercept = false) where T : struct, IEquatable, IFormattable { - var xy = samples.UnpackSinglePass(); - return Svd(xy.Item1, xy.Item2, intercept); + var (u, v) = samples.UnpackSinglePass(); + return Svd(u, v, intercept); + } + + /// + /// Find the model parameters β such that their linear combination with all predictor-arrays in X become as close to their response in Y as possible, with least squares residuals. + /// Uses a singular value decomposition and is therefore more numerically stable (especially if ill-conditioned) than the normal equations or QR but also slower. + /// + /// Sequence of predictor-arrays and their response. + /// True if an intercept should be added as first artificial predictor value. Default = false. + /// Best fitting list of model parameters β for each element in the predictor-arrays. + public static T[] Svd(IEnumerable<(T[], T)> samples, bool intercept = false) where T : struct, IEquatable, IFormattable + { + var (u, v) = samples.UnpackSinglePass(); + return Svd(u, v, intercept); } } } diff --git a/src/Numerics/LinearRegression/SimpleRegression.cs b/src/Numerics/LinearRegression/SimpleRegression.cs index a33ee77b..5a3897fe 100644 --- a/src/Numerics/LinearRegression/SimpleRegression.cs +++ b/src/Numerics/LinearRegression/SimpleRegression.cs @@ -87,8 +87,20 @@ namespace MathNet.Numerics.LinearRegression /// Predictor-Response samples as tuples public static (double A, double B) Fit(IEnumerable> samples) { - var xy = samples.UnpackSinglePass(); - return Fit(xy.Item1, xy.Item2); + var (u, v) = samples.UnpackSinglePass(); + return Fit(u, v); + } + + /// + /// Least-Squares fitting the points (x,y) to a line y : x -> a+b*x, + /// returning its best fitting parameters as (a, b) tuple, + /// where a is the intercept and b the slope. + /// + /// Predictor-Response samples as tuples + public static (double A, double B) Fit(IEnumerable<(double, double)> samples) + { + var (u, v) = samples.UnpackSinglePass(); + return Fit(u, v); } /// diff --git a/src/Numerics/LinearRegression/Util.cs b/src/Numerics/LinearRegression/Util.cs index 8576f368..b2d2d692 100644 --- a/src/Numerics/LinearRegression/Util.cs +++ b/src/Numerics/LinearRegression/Util.cs @@ -2,9 +2,9 @@ // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics -// +// // Copyright (c) 2009-2013 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 @@ -13,10 +13,10 @@ // 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 @@ -34,18 +34,32 @@ namespace MathNet.Numerics.LinearRegression { internal static class Util { - public static Tuple UnpackSinglePass(this IEnumerable> samples) + public static (TU[] U, TV[] V) UnpackSinglePass(this IEnumerable> samples) { - var u = new List(); - var v = new List(); + var ux = new List(); + var vx = new List(); foreach (var tuple in samples) { - u.Add(tuple.Item1); - v.Add(tuple.Item2); + ux.Add(tuple.Item1); + vx.Add(tuple.Item2); } - return new Tuple(u.ToArray(), v.ToArray()); + return (ux.ToArray(), vx.ToArray()); + } + + public static (TU[] U, TV[] V) UnpackSinglePass(this IEnumerable<(TU, TV)> samples) + { + var ux = new List(); + var vx = new List(); + + foreach (var (u, v) in samples) + { + ux.Add(u); + vx.Add(v); + } + + return (ux.ToArray(), vx.ToArray()); } } } diff --git a/src/Numerics/LinearRegression/WeightedRegression.cs b/src/Numerics/LinearRegression/WeightedRegression.cs index 2d8a1847..e7fa7f11 100644 --- a/src/Numerics/LinearRegression/WeightedRegression.cs +++ b/src/Numerics/LinearRegression/WeightedRegression.cs @@ -2,9 +2,9 @@ // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics -// +// // Copyright (c) 2009-2013 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 @@ -13,10 +13,10 @@ // 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 @@ -85,8 +85,20 @@ namespace MathNet.Numerics.LinearRegression /// True if an intercept should be added as first artificial predictor value. Default = false. public static T[] Weighted(IEnumerable> samples, T[] weights, bool intercept = false) where T : struct, IEquatable, IFormattable { - var xy = samples.UnpackSinglePass(); - return Weighted(xy.Item1, xy.Item2, weights, intercept); + var (u, v) = samples.UnpackSinglePass(); + return Weighted(u, v, weights, intercept); + } + + /// + /// Weighted Linear Regression using normal equations. + /// + /// List of sample vectors (predictor) together with their response. + /// List of weights, one for each sample. + /// True if an intercept should be added as first artificial predictor value. Default = false. + public static T[] Weighted(IEnumerable<(T[], T)> samples, T[] weights, bool intercept = false) where T : struct, IEquatable, IFormattable + { + var (u, v) = samples.UnpackSinglePass(); + return Weighted(u, v, weights, intercept); } /// diff --git a/src/Numerics/Optimization/LevenbergMarquardtMinimizer.cs b/src/Numerics/Optimization/LevenbergMarquardtMinimizer.cs index 4994b228..4bbad04c 100644 --- a/src/Numerics/Optimization/LevenbergMarquardtMinimizer.cs +++ b/src/Numerics/Optimization/LevenbergMarquardtMinimizer.cs @@ -129,9 +129,7 @@ namespace MathNet.Numerics.Optimization } // Evaluate gradient and Hessian - var jac = EvaluateJacobian(objective, P); - var Gradient = jac.Item1; // objective.Gradient; - var Hessian = jac.Item2; // objective.Hessian; + var (Gradient, Hessian) = EvaluateJacobian(objective, P); var diagonalOfHessian = Hessian.Diagonal(); // diag(H) // if ||g||oo <= gtol, found and stop @@ -190,9 +188,7 @@ namespace MathNet.Numerics.Optimization RSS = RSSnew; // update gradient and Hessian - jac = EvaluateJacobian(objective, P); - Gradient = jac.Item1; // objective.Gradient; - Hessian = jac.Item2; // objective.Hessian; + (Gradient, Hessian) = EvaluateJacobian(objective, P); diagonalOfHessian = Hessian.Diagonal(); // if ||g||_oo <= gtol, found and stop diff --git a/src/Numerics/Optimization/NonlinearMinimizerBase.cs b/src/Numerics/Optimization/NonlinearMinimizerBase.cs index 863b0956..1e06410a 100644 --- a/src/Numerics/Optimization/NonlinearMinimizerBase.cs +++ b/src/Numerics/Optimization/NonlinearMinimizerBase.cs @@ -100,7 +100,7 @@ namespace MathNet.Numerics.Optimization return objective.Value; } - protected Tuple, Matrix> EvaluateJacobian(IObjectiveModel objective, Vector Pint) + protected (Vector Gradient, Matrix Hessian) EvaluateJacobian(IObjectiveModel objective, Vector Pint) { var gradient = objective.Gradient; var hessian = objective.Hessian; @@ -123,7 +123,7 @@ namespace MathNet.Numerics.Optimization } } - return new Tuple, Matrix>(gradient, hessian); + return (gradient, hessian); } #region Projection of Parameters diff --git a/src/Numerics/Optimization/TrustRegion/TrustRegionMinimizerBase.cs b/src/Numerics/Optimization/TrustRegion/TrustRegionMinimizerBase.cs index c895d008..e38b3534 100644 --- a/src/Numerics/Optimization/TrustRegion/TrustRegionMinimizerBase.cs +++ b/src/Numerics/Optimization/TrustRegion/TrustRegionMinimizerBase.cs @@ -137,9 +137,7 @@ namespace MathNet.Numerics.Optimization.TrustRegion } // evaluate projected gradient and Hessian - var jac = EvaluateJacobian(objective, P); - var Gradient = jac.Item1; // objective.Gradient; - var Hessian = jac.Item2; // objective.Hessian; + var (Gradient, Hessian) = EvaluateJacobian(objective, P); // if ||g||_oo <= gtol, found and stop if (Gradient.InfinityNorm() <= gradientTolerance) @@ -213,9 +211,7 @@ namespace MathNet.Numerics.Optimization.TrustRegion RSS = RSSnew; // evaluate projected gradient and Hessian - jac = EvaluateJacobian(objective, P); - Gradient = jac.Item1; // objective.Gradient; - Hessian = jac.Item2; // objective.Hessian; + (Gradient, Hessian) = EvaluateJacobian(objective, P); // if ||g||_oo <= gtol, found and stop if (Gradient.InfinityNorm() <= gradientTolerance)