// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // Copyright (c) 2009-2014 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Collections.Generic; using System.Linq; using IStation.Numerics.Distributions; using IStation.Numerics.LinearAlgebra.Solvers; using IStation.Numerics.LinearAlgebra.Storage; using IStation.Numerics.Random; namespace IStation.Numerics.LinearAlgebra.Double { internal class MatrixBuilder : MatrixBuilder { public override double Zero => 0d; public override double One => 1d; public override Matrix Dense(DenseColumnMajorMatrixStorage storage) { return new DenseMatrix(storage); } public override Matrix Sparse(SparseCompressedRowMatrixStorage storage) { return new SparseMatrix(storage); } public override Matrix Diagonal(DiagonalMatrixStorage storage) { return new DiagonalMatrix(storage); } public override Matrix Random(int rows, int columns, IContinuousDistribution distribution) { return Dense(rows, columns, Generate.Random(rows*columns, distribution)); } public override IIterationStopCriterion[] IterativeSolverStopCriteria(int maxIterations = 1000) { return new IIterationStopCriterion[] { new FailureStopCriterion(), new DivergenceStopCriterion(), new IterationCountStopCriterion(maxIterations), new ResidualStopCriterion(1e-12) }; } internal override double Add(double x, double y) { return x + y; } } internal class VectorBuilder : VectorBuilder { public override double Zero => 0d; public override double One => 1d; public override Vector Dense(DenseVectorStorage storage) { return new DenseVector(storage); } public override Vector Sparse(SparseVectorStorage storage) { return new SparseVector(storage); } public override Vector Random(int length, IContinuousDistribution distribution) { return Dense(Generate.Random(length, distribution)); } } } namespace IStation.Numerics.LinearAlgebra.Single { internal class MatrixBuilder : MatrixBuilder { public override float Zero => 0f; public override float One => 1f; public override Matrix Dense(DenseColumnMajorMatrixStorage storage) { return new DenseMatrix(storage); } public override Matrix Sparse(SparseCompressedRowMatrixStorage storage) { return new SparseMatrix(storage); } public override Matrix Diagonal(DiagonalMatrixStorage storage) { return new DiagonalMatrix(storage); } public override Matrix Random(int rows, int columns, IContinuousDistribution distribution) { return Dense(rows, columns, Generate.RandomSingle(rows*columns, distribution)); } public override IIterationStopCriterion[] IterativeSolverStopCriteria(int maxIterations = 1000) { return new IIterationStopCriterion[] { new FailureStopCriterion(), new DivergenceStopCriterion(), new IterationCountStopCriterion(maxIterations), new ResidualStopCriterion(1e-6) }; } internal override float Add(float x, float y) { return x + y; } } internal class VectorBuilder : VectorBuilder { public override float Zero => 0f; public override float One => 1f; public override Vector Dense(DenseVectorStorage storage) { return new DenseVector(storage); } public override Vector Sparse(SparseVectorStorage storage) { return new SparseVector(storage); } public override Vector Random(int length, IContinuousDistribution distribution) { return Dense(Generate.RandomSingle(length, distribution)); } } } namespace IStation.Numerics.LinearAlgebra.Complex { using Complex = System.Numerics.Complex; internal class MatrixBuilder : MatrixBuilder { public override Complex Zero => Complex.Zero; public override Complex One => Complex.One; public override Matrix Dense(DenseColumnMajorMatrixStorage storage) { return new DenseMatrix(storage); } public override Matrix Sparse(SparseCompressedRowMatrixStorage storage) { return new SparseMatrix(storage); } public override Matrix Diagonal(DiagonalMatrixStorage storage) { return new DiagonalMatrix(storage); } public override Matrix Random(int rows, int columns, IContinuousDistribution distribution) { return Dense(rows, columns, Generate.RandomComplex(rows*columns, distribution)); } public override IIterationStopCriterion[] IterativeSolverStopCriteria(int maxIterations = 1000) { return new IIterationStopCriterion[] { new FailureStopCriterion(), new DivergenceStopCriterion(), new IterationCountStopCriterion(maxIterations), new ResidualStopCriterion(1e-12) }; } internal override Complex Add(Complex x, Complex y) { return x + y; } } internal class VectorBuilder : VectorBuilder { public override Complex Zero => Complex.Zero; public override Complex One => Complex.One; public override Vector Dense(DenseVectorStorage storage) { return new DenseVector(storage); } public override Vector Sparse(SparseVectorStorage storage) { return new SparseVector(storage); } public override Vector Random(int length, IContinuousDistribution distribution) { return Dense(Generate.RandomComplex(length, distribution)); } } } namespace IStation.Numerics.LinearAlgebra.Complex32 { internal class MatrixBuilder : MatrixBuilder { public override Numerics.Complex32 Zero => Numerics.Complex32.Zero; public override Numerics.Complex32 One => Numerics.Complex32.One; public override Matrix Dense(DenseColumnMajorMatrixStorage storage) { return new DenseMatrix(storage); } public override Matrix Sparse(SparseCompressedRowMatrixStorage storage) { return new SparseMatrix(storage); } public override Matrix Diagonal(DiagonalMatrixStorage storage) { return new DiagonalMatrix(storage); } public override Matrix Random(int rows, int columns, IContinuousDistribution distribution) { return Dense(rows, columns, Generate.RandomComplex32(rows*columns, distribution)); } public override IIterationStopCriterion[] IterativeSolverStopCriteria(int maxIterations = 1000) { return new IIterationStopCriterion[] { new FailureStopCriterion(), new DivergenceStopCriterion(), new IterationCountStopCriterion(maxIterations), new ResidualStopCriterion(1e-6) }; } internal override Numerics.Complex32 Add(Numerics.Complex32 x, Numerics.Complex32 y) { return x + y; } } internal class VectorBuilder : VectorBuilder { public override Numerics.Complex32 Zero => Numerics.Complex32.Zero; public override Numerics.Complex32 One => Numerics.Complex32.One; public override Vector Dense(DenseVectorStorage storage) { return new DenseVector(storage); } public override Vector Sparse(SparseVectorStorage storage) { return new SparseVector(storage); } public override Vector Random(int length, IContinuousDistribution distribution) { return Dense(Generate.RandomComplex32(length, distribution)); } } } namespace IStation.Numerics.LinearAlgebra { /// /// Generic linear algebra type builder, for situations where a matrix or vector /// must be created in a generic way. Usage of generic builders should not be /// required in normal user code. /// public abstract class MatrixBuilder where T : struct, IEquatable, IFormattable { /// /// Gets the value of 0.0 for type T. /// public abstract T Zero { get; } /// /// Gets the value of 1.0 for type T. /// public abstract T One { get; } internal abstract T Add(T x, T y); /// /// Create a new matrix straight from an initialized matrix storage instance. /// If you have an instance of a discrete storage type instead, use their direct methods instead. /// public Matrix OfStorage(MatrixStorage storage) { if (storage == null) throw new ArgumentNullException(nameof(storage)); if (storage is DenseColumnMajorMatrixStorage dense) return Dense(dense); if (storage is SparseCompressedRowMatrixStorage sparse) return Sparse(sparse); if (storage is DiagonalMatrixStorage diagonal) return Diagonal(diagonal); throw new NotSupportedException(FormattableString.Invariant($"Matrix storage type '{storage.GetType().Name}' is not supported. Only DenseColumnMajorMatrixStorage, SparseCompressedRowMatrixStorage and DiagonalMatrixStorage are supported as this point.")); } /// /// Create a new matrix with the same kind of the provided example. /// public Matrix SameAs(Matrix example, int rows, int columns, bool fullyMutable = false) where TU : struct, IEquatable, IFormattable { var storage = example.Storage; if (storage is DenseColumnMajorMatrixStorage) return Dense(rows, columns); if (storage is DiagonalMatrixStorage) return fullyMutable ? Sparse(rows, columns) : Diagonal(rows, columns); if (storage is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); return Dense(rows, columns); } /// /// Create a new matrix with the same kind and dimensions of the provided example. /// public Matrix SameAs(Matrix example) where TU : struct, IEquatable, IFormattable { return SameAs(example, example.RowCount, example.ColumnCount); } /// /// Create a new matrix with the same kind of the provided example. /// public Matrix SameAs(Vector example, int rows, int columns) { return example.Storage.IsDense ? Dense(rows, columns) : Sparse(rows, columns); } /// /// Create a new matrix with a type that can represent and is closest to both provided samples. /// public Matrix SameAs(Matrix example, Matrix otherExample, int rows, int columns, bool fullyMutable = false) { var storage1 = example.Storage; var storage2 = otherExample.Storage; if (storage1 is DenseColumnMajorMatrixStorage || storage2 is DenseColumnMajorMatrixStorage) return Dense(rows, columns); if (storage1 is DiagonalMatrixStorage && storage2 is DiagonalMatrixStorage) return fullyMutable ? Sparse(rows, columns) : Diagonal(rows, columns); if (storage1 is SparseCompressedRowMatrixStorage || storage2 is SparseCompressedRowMatrixStorage) return Sparse(rows, columns); return Dense(rows, columns); } /// /// Create a new matrix with a type that can represent and is closest to both provided samples and the dimensions of example. /// public Matrix SameAs(Matrix example, Matrix otherExample) { return SameAs(example, otherExample, example.RowCount, example.ColumnCount); } /// /// Create a new dense matrix with values sampled from the provided random distribution. /// public abstract Matrix Random(int rows, int columns, IContinuousDistribution distribution); /// /// Create a new dense matrix with values sampled from the standard distribution with a system random source. /// public Matrix Random(int rows, int columns) { return Random(rows, columns, new Normal(SystemRandomSource.Default)); } /// /// Create a new dense matrix with values sampled from the standard distribution with a system random source. /// public Matrix Random(int rows, int columns, int seed) { return Random(rows, columns, new Normal(new SystemRandomSource(seed, true))); } /// /// Create a new positive definite dense matrix where each value is the product /// of two samples from the provided random distribution. /// public Matrix RandomPositiveDefinite(int order, IContinuousDistribution distribution) { var a = Random(order, order, distribution); return a.ConjugateTransposeThisAndMultiply(a); } /// /// Create a new positive definite dense matrix where each value is the product /// of two samples from the standard distribution. /// public Matrix RandomPositiveDefinite(int order) { var a = Random(order, order, new Normal(SystemRandomSource.Default)); return a.ConjugateTransposeThisAndMultiply(a); } /// /// Create a new positive definite dense matrix where each value is the product /// of two samples from the provided random distribution. /// public Matrix RandomPositiveDefinite(int order, int seed) { var a = Random(order, order, new Normal(new SystemRandomSource(seed, true))); return a.ConjugateTransposeThisAndMultiply(a); } /// /// Create a new dense matrix straight from an initialized matrix storage instance. /// The storage is used directly without copying. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// public abstract Matrix Dense(DenseColumnMajorMatrixStorage storage); /// /// Create a new dense matrix with the given number of rows and columns. /// All cells of the matrix will be initialized to zero. /// public Matrix Dense(int rows, int columns) { return Dense(new DenseColumnMajorMatrixStorage(rows, columns)); } /// /// Create a new dense matrix with the given number of rows and columns directly binding to a raw array. /// The array is assumed to be in column-major order (column by column) and is used directly without copying. /// Very efficient, but changes to the array and the matrix will affect each other. /// /// public Matrix Dense(int rows, int columns, T[] storage) { return Dense(new DenseColumnMajorMatrixStorage(rows, columns, storage)); } /// /// Create a new dense matrix and initialize each value to the same provided value. /// public Matrix Dense(int rows, int columns, T value) { if (Zero.Equals(value)) return Dense(rows, columns); return Dense(DenseColumnMajorMatrixStorage.OfValue(rows, columns, value)); } /// /// Create a new dense matrix and initialize each value using the provided init function. /// public Matrix Dense(int rows, int columns, Func init) { return Dense(DenseColumnMajorMatrixStorage.OfInit(rows, columns, init)); } /// /// Create a new diagonal dense matrix and initialize each diagonal value to the same provided value. /// public Matrix DenseDiagonal(int rows, int columns, T value) { if (Zero.Equals(value)) return Dense(rows, columns); return Dense(DenseColumnMajorMatrixStorage.OfDiagonalInit(rows, columns, i => value)); } /// /// Create a new diagonal dense matrix and initialize each diagonal value to the same provided value. /// public Matrix DenseDiagonal(int order, T value) { if (Zero.Equals(value)) return Dense(order, order); return Dense(DenseColumnMajorMatrixStorage.OfDiagonalInit(order, order, i => value)); } /// /// Create a new diagonal dense matrix and initialize each diagonal value using the provided init function. /// public Matrix DenseDiagonal(int rows, int columns, Func init) { return Dense(DenseColumnMajorMatrixStorage.OfDiagonalInit(rows, columns, init)); } /// /// Create a new diagonal dense identity matrix with a one-diagonal. /// public Matrix DenseIdentity(int rows, int columns) { return Dense(DenseColumnMajorMatrixStorage.OfDiagonalInit(rows, columns, i => One)); } /// /// Create a new diagonal dense identity matrix with a one-diagonal. /// public Matrix DenseIdentity(int order) { return Dense(DenseColumnMajorMatrixStorage.OfDiagonalInit(order, order, i => One)); } /// /// Create a new dense matrix as a copy of the given other matrix. /// This new matrix will be independent from the other matrix. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfMatrix(Matrix matrix) { return Dense(DenseColumnMajorMatrixStorage.OfMatrix(matrix.Storage)); } /// /// Create a new dense matrix as a copy of the given two-dimensional array. /// This new matrix will be independent from the provided array. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfArray(T[,] array) { return Dense(DenseColumnMajorMatrixStorage.OfArray(array)); } /// /// 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> 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). /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumnMajor(int rows, int columns, IEnumerable columnMajor) { return Dense(DenseColumnMajorMatrixStorage.OfColumnMajorEnumerable(rows, columns, columnMajor)); } /// /// Create a new dense matrix as a copy of the given enumerable of enumerable columns. /// Each enumerable in the master enumerable specifies a column. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumns(IEnumerable> data) { return Dense(DenseColumnMajorMatrixStorage.OfColumnArrays(data.Select(v => (v as T[]) ?? v.ToArray()).ToArray())); } /// /// Create a new dense matrix as a copy of the given enumerable of enumerable columns. /// Each enumerable in the master enumerable specifies a column. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumns(int rows, int columns, IEnumerable> data) { return Dense(DenseColumnMajorMatrixStorage.OfColumnEnumerables(rows, columns, data)); } /// /// Create a new dense matrix of T as a copy of the given column arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumnArrays(params T[][] columns) { return Dense(DenseColumnMajorMatrixStorage.OfColumnArrays(columns)); } /// /// Create a new dense matrix of T as a copy of the given column arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumnArrays(IEnumerable columns) { return Dense(DenseColumnMajorMatrixStorage.OfColumnArrays((columns as T[][]) ?? columns.ToArray())); } /// /// Create a new dense matrix as a copy of the given column vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumnVectors(params Vector[] columns) { var storage = new VectorStorage[columns.Length]; for (int i = 0; i < columns.Length; i++) { storage[i] = columns[i].Storage; } return Dense(DenseColumnMajorMatrixStorage.OfColumnVectors(storage)); } /// /// Create a new dense matrix as a copy of the given column vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfColumnVectors(IEnumerable> columns) { return Dense(DenseColumnMajorMatrixStorage.OfColumnVectors(columns.Select(c => c.Storage).ToArray())); } /// /// Create a new dense matrix as a copy of the given enumerable. /// The enumerable is assumed to be in row-major order (row by row). /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRowMajor(int rows, int columns, IEnumerable columnMajor) { return Dense(DenseColumnMajorMatrixStorage.OfRowMajorEnumerable(rows, columns, columnMajor)); } /// /// Create a new dense matrix as a copy of the given enumerable of enumerable rows. /// Each enumerable in the master enumerable specifies a row. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRows(IEnumerable> data) { return Dense(DenseColumnMajorMatrixStorage.OfRowArrays(data.Select(v => (v as T[]) ?? v.ToArray()).ToArray())); } /// /// Create a new dense matrix as a copy of the given enumerable of enumerable rows. /// Each enumerable in the master enumerable specifies a row. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRows(int rows, int columns, IEnumerable> data) { return Dense(DenseColumnMajorMatrixStorage.OfRowEnumerables(rows, columns, data)); } /// /// Create a new dense matrix of T as a copy of the given row arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRowArrays(params T[][] rows) { return Dense(DenseColumnMajorMatrixStorage.OfRowArrays(rows)); } /// /// Create a new dense matrix of T as a copy of the given row arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRowArrays(IEnumerable rows) { return Dense(DenseColumnMajorMatrixStorage.OfRowArrays((rows as T[][]) ?? rows.ToArray())); } /// /// Create a new dense matrix as a copy of the given row vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRowVectors(params Vector[] rows) { var storage = new VectorStorage[rows.Length]; for (int i = 0; i < rows.Length; i++) { storage[i] = rows[i].Storage; } return Dense(DenseColumnMajorMatrixStorage.OfRowVectors(storage)); } /// /// Create a new dense matrix as a copy of the given row vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfRowVectors(IEnumerable> rows) { return Dense(DenseColumnMajorMatrixStorage.OfRowVectors(rows.Select(r => r.Storage).ToArray())); } /// /// Create a new dense matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfDiagonalVector(Vector diagonal) { var m = Dense(diagonal.Count, diagonal.Count); m.SetDiagonal(diagonal); return m; } /// /// Create a new dense matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfDiagonalVector(int rows, int columns, Vector diagonal) { var m = Dense(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new dense matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfDiagonalArray(T[] diagonal) { var m = Dense(diagonal.Length, diagonal.Length); m.SetDiagonal(diagonal); return m; } /// /// Create a new dense matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public Matrix DenseOfDiagonalArray(int rows, int columns, T[] diagonal) { var m = Dense(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new dense matrix from a 2D array of existing matrices. /// The matrices in the array are not required to be dense already. /// If the matrices do not align properly, they are placed on the top left /// corner of their cell with the remaining fields left zero. /// public Matrix DenseOfMatrixArray(Matrix[,] matrices) { var rowspans = new int[matrices.GetLength(0)]; var colspans = new int[matrices.GetLength(1)]; for (int i = 0; i < rowspans.Length; i++) { for (int j = 0; j < colspans.Length; j++) { rowspans[i] = Math.Max(rowspans[i], matrices[i, j].RowCount); colspans[j] = Math.Max(colspans[j], matrices[i, j].ColumnCount); } } var m = Dense(rowspans.Sum(), colspans.Sum()); int rowoffset = 0; for (int i = 0; i < rowspans.Length; i++) { int coloffset = 0; for (int j = 0; j < colspans.Length; j++) { m.SetSubMatrix(rowoffset, coloffset, matrices[i, j]); coloffset += colspans[j]; } rowoffset += rowspans[i]; } return m; } /// /// Create a new sparse matrix straight from an initialized matrix storage instance. /// The storage is used directly without copying. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// /// The SparseCompressedRowMatrixStorage public abstract Matrix Sparse(SparseCompressedRowMatrixStorage storage); /// /// Create a sparse matrix of T with the given number of rows and columns. /// /// The number of rows. /// The number of columns. public Matrix Sparse(int rows, int columns) { return Sparse(new SparseCompressedRowMatrixStorage(rows, columns)); } /// /// Create a new sparse matrix and initialize each value to the same provided value. /// public Matrix Sparse(int rows, int columns, T value) { if (Zero.Equals(value)) return Sparse(rows, columns); return Sparse(SparseCompressedRowMatrixStorage.OfValue(rows, columns, value)); } /// /// Create a new sparse matrix and initialize each value using the provided init function. /// public Matrix Sparse(int rows, int columns, Func init) { return Sparse(SparseCompressedRowMatrixStorage.OfInit(rows, columns, init)); } /// /// Create a new diagonal sparse matrix and initialize each diagonal value to the same provided value. /// public Matrix SparseDiagonal(int rows, int columns, T value) { if (Zero.Equals(value)) return Sparse(rows, columns); return Sparse(SparseCompressedRowMatrixStorage.OfDiagonalInit(rows, columns, i => value)); } /// /// Create a new diagonal sparse matrix and initialize each diagonal value to the same provided value. /// public Matrix SparseDiagonal(int order, T value) { if (Zero.Equals(value)) return Sparse(order, order); return Sparse(SparseCompressedRowMatrixStorage.OfDiagonalInit(order, order, i => value)); } /// /// Create a new diagonal sparse matrix and initialize each diagonal value using the provided init function. /// public Matrix SparseDiagonal(int rows, int columns, Func init) { return Sparse(SparseCompressedRowMatrixStorage.OfDiagonalInit(rows, columns, init)); } /// /// Create a new diagonal dense identity matrix with a one-diagonal. /// public Matrix SparseIdentity(int rows, int columns) { return Sparse(SparseCompressedRowMatrixStorage.OfDiagonalInit(rows, columns, i => One)); } /// /// Create a new diagonal dense identity matrix with a one-diagonal. /// public Matrix SparseIdentity(int order) { return Sparse(SparseCompressedRowMatrixStorage.OfDiagonalInit(order, order, i => One)); } /// /// Create a new sparse matrix as a copy of the given other matrix. /// This new matrix will be independent from the other matrix. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfMatrix(Matrix matrix) { return Sparse(SparseCompressedRowMatrixStorage.OfMatrix(matrix.Storage)); } /// /// Create a new sparse matrix as a copy of the given two-dimensional array. /// This new matrix will be independent from the provided array. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfArray(T[,] array) { return Sparse(SparseCompressedRowMatrixStorage.OfArray(array)); } /// /// 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> 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). /// This new matrix will be independent from the enumerable. /// A new memory block will be allocated for storing the vector. /// /// public Matrix SparseOfRowMajor(int rows, int columns, IEnumerable rowMajor) { return Sparse(SparseCompressedRowMatrixStorage.OfRowMajorEnumerable(rows, columns, rowMajor)); } /// /// Create a new sparse matrix with the given number of rows and columns as a copy of the given array. /// The array is assumed to be in column-major order (column by column). /// This new matrix will be independent from the provided array. /// A new memory block will be allocated for storing the matrix. /// /// public Matrix SparseOfColumnMajor(int rows, int columns, IList columnMajor) { return Sparse(SparseCompressedRowMatrixStorage.OfColumnMajorList(rows, columns, columnMajor)); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable columns. /// Each enumerable in the master enumerable specifies a column. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfColumns(IEnumerable> data) { return Sparse(SparseCompressedRowMatrixStorage.OfColumnArrays(data.Select(v => (v as T[]) ?? v.ToArray()).ToArray())); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable columns. /// Each enumerable in the master enumerable specifies a column. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfColumns(int rows, int columns, IEnumerable> data) { return Sparse(SparseCompressedRowMatrixStorage.OfColumnEnumerables(rows, columns, data)); } /// /// Create a new sparse matrix as a copy of the given column arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfColumnArrays(params T[][] columns) { return Sparse(SparseCompressedRowMatrixStorage.OfColumnArrays(columns)); } /// /// Create a new sparse matrix as a copy of the given column arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfColumnArrays(IEnumerable columns) { return Sparse(SparseCompressedRowMatrixStorage.OfColumnArrays((columns as T[][]) ?? columns.ToArray())); } /// /// Create a new sparse matrix as a copy of the given column vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfColumnVectors(params Vector[] columns) { var storage = new VectorStorage[columns.Length]; for (int i = 0; i < columns.Length; i++) { storage[i] = columns[i].Storage; } return Sparse(SparseCompressedRowMatrixStorage.OfColumnVectors(storage)); } /// /// Create a new sparse matrix as a copy of the given column vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfColumnVectors(IEnumerable> columns) { return Sparse(SparseCompressedRowMatrixStorage.OfColumnVectors(columns.Select(c => c.Storage).ToArray())); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable rows. /// Each enumerable in the master enumerable specifies a row. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfRows(IEnumerable> data) { return Sparse(SparseCompressedRowMatrixStorage.OfRowArrays(data.Select(v => (v as T[]) ?? v.ToArray()).ToArray())); } /// /// Create a new sparse matrix as a copy of the given enumerable of enumerable rows. /// Each enumerable in the master enumerable specifies a row. /// This new matrix will be independent from the enumerables. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfRows(int rows, int columns, IEnumerable> data) { return Sparse(SparseCompressedRowMatrixStorage.OfRowEnumerables(rows, columns, data)); } /// /// Create a new sparse matrix as a copy of the given row arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfRowArrays(params T[][] rows) { return Sparse(SparseCompressedRowMatrixStorage.OfRowArrays(rows)); } /// /// Create a new sparse matrix as a copy of the given row arrays. /// This new matrix will be independent from the arrays. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfRowArrays(IEnumerable rows) { return Sparse(SparseCompressedRowMatrixStorage.OfRowArrays((rows as T[][]) ?? rows.ToArray())); } /// /// Create a new sparse matrix as a copy of the given row vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfRowVectors(params Vector[] rows) { var storage = new VectorStorage[rows.Length]; for (int i = 0; i < rows.Length; i++) { storage[i] = rows[i].Storage; } return Sparse(SparseCompressedRowMatrixStorage.OfRowVectors(storage)); } /// /// Create a new sparse matrix as a copy of the given row vectors. /// This new matrix will be independent from the vectors. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfRowVectors(IEnumerable> rows) { return Sparse(SparseCompressedRowMatrixStorage.OfRowVectors(rows.Select(r => r.Storage).ToArray())); } /// /// Create a new sparse matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfDiagonalVector(Vector diagonal) { var m = Sparse(diagonal.Count, diagonal.Count); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfDiagonalVector(int rows, int columns, Vector diagonal) { var m = Sparse(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfDiagonalArray(T[] diagonal) { var m = Sparse(diagonal.Length, diagonal.Length); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public Matrix SparseOfDiagonalArray(int rows, int columns, T[] diagonal) { var m = Sparse(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new sparse matrix from a 2D array of existing matrices. /// The matrices in the array are not required to be sparse already. /// If the matrices do not align properly, they are placed on the top left /// corner of their cell with the remaining fields left zero. /// public Matrix SparseOfMatrixArray(Matrix[,] matrices) { var rowspans = new int[matrices.GetLength(0)]; var colspans = new int[matrices.GetLength(1)]; for (int i = 0; i < rowspans.Length; i++) { for (int j = 0; j < colspans.Length; j++) { rowspans[i] = Math.Max(rowspans[i], matrices[i, j].RowCount); colspans[j] = Math.Max(colspans[j], matrices[i, j].ColumnCount); } } var m = Sparse(rowspans.Sum(), colspans.Sum()); int rowoffset = 0; for (int i = 0; i < rowspans.Length; i++) { int coloffset = 0; for (int j = 0; j < colspans.Length; j++) { m.SetSubMatrix(rowoffset, coloffset, matrices[i, j]); coloffset += colspans[j]; } rowoffset += rowspans[i]; } return m; } // Representation of Sparse Matrix // // Matrix A = [ 0 b 0 h 0 0 ] // [ a c e i 0 0 ] // [ 0 0 f j l n ] // [ 0 d g k m 0 ] // // rows = 4, columns = 6, valueCount = 14 // // (1) COO, Coordinate, ijv, or triplet format: // cooRowIndices = { 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 } // cooColumnIndices = { 1, 3, 0, 1, 2, 3, 2, 3, 4, 5, 1, 2, 3, 4 } // cooValues = { b, h, a, c, e, i, f, j, l, n, d, g, k, m } // // (2) CSR, Compressed Sparse Row or Compressed Row Storage(CRS) or Yale format: // csrRowPointers = { 0, 2, 6, 10, 14 } // csrColumnIndices = { 1, 3, 0, 1, 2, 3, 2, 3, 4, 5, 1, 2, 3, 4 } // csrValues = { b, h, a, c, e, i, f, j, l, n, d, g, k, m } // // (3) CSC, Compressed Sparse Column or Compressed Column Storage(CCS) format: // cscColumnPointers = { 0, 1, 4, 7, 11, 13, 14 } // cscRowIndices = { 1, 0, 1, 3, 1, 2, 3, 0, 1, 2, 3, 2, 3, 2 } // cscValues = { a, b, c, d, e, f, g, h, i, j, k, l, m, n } /// /// Create a new sparse matrix from a coordinate format. /// This new matrix will be independent from the given arrays. /// A new memory block will be allocated for storing the matrix. /// /// The number of rows. /// The number of columns. /// The number of stored values including explicit zeros. /// The row index array of the coordinate format. /// The column index array of the coordinate format. /// The data array of the coordinate format. /// The sparse matrix from the coordinate format. /// Duplicate entries will be summed together and explicit zeros will be not intentionally removed. public Matrix SparseFromCoordinateFormat(int rows, int columns, int valueCount, int[] rowIndices, int[] columnIndices, T[] values) { return Sparse(SparseCompressedRowMatrixStorage.OfCoordinateFormat(rows, columns, valueCount, rowIndices, columnIndices, values)); } /// /// Create a new sparse matrix from a compressed sparse row format. /// This new matrix will be independent from the given arrays. /// A new memory block will be allocated for storing the matrix. /// /// The number of rows. /// The number of columns. /// The number of stored values including explicit zeros. /// The row pointer array of the compressed sparse row format. /// The column index array of the compressed sparse row format. /// The data array of the compressed sparse row format. /// The sparse matrix from the compressed sparse row format. /// Duplicate entries will be summed together and explicit zeros will be not intentionally removed. public Matrix SparseFromCompressedSparseRowFormat(int rows, int columns, int valueCount, int[] rowPointers, int[] columnIndices, T[] values) { return Sparse(SparseCompressedRowMatrixStorage.OfCompressedSparseRowFormat(rows, columns, valueCount, rowPointers, columnIndices, values)); } /// /// Create a new sparse matrix from a compressed sparse column format. /// This new matrix will be independent from the given arrays. /// A new memory block will be allocated for storing the matrix. /// /// The number of rows. /// The number of columns. /// The number of stored values including explicit zeros. /// The row index array of the compressed sparse column format. /// The column pointer array of the compressed sparse column format. /// The data array of the compressed sparse column format. /// The sparse matrix from the compressed sparse column format. /// Duplicate entries will be summed together and explicit zeros will be not intentionally removed. public Matrix SparseFromCompressedSparseColumnFormat(int rows, int columns, int valueCount, int[] rowIndices, int[] columnPointers, T[] values) { return Sparse(SparseCompressedRowMatrixStorage.OfCompressedSparseColumnFormat(rows, columns, valueCount, rowIndices, columnPointers, values)); } /// /// Create a new diagonal matrix straight from an initialized matrix storage instance. /// The storage is used directly without copying. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// public abstract Matrix Diagonal(DiagonalMatrixStorage storage); /// /// Create a new diagonal matrix with the given number of rows and columns. /// All cells of the matrix will be initialized to zero. /// public Matrix Diagonal(int rows, int columns) { return Diagonal(new DiagonalMatrixStorage(rows, columns)); } /// /// Create a new diagonal matrix with the given number of rows and columns directly binding to a raw array. /// The array is assumed to represent the diagonal values and is used directly without copying. /// Very efficient, but changes to the array and the matrix will affect each other. /// public Matrix Diagonal(int rows, int columns, T[] storage) { return Diagonal(new DiagonalMatrixStorage(rows, columns, storage)); } /// /// Create a new square diagonal matrix directly binding to a raw array. /// The array is assumed to represent the diagonal values and is used directly without copying. /// Very efficient, but changes to the array and the matrix will affect each other. /// public Matrix Diagonal(T[] storage) { return Diagonal(new DiagonalMatrixStorage(storage.Length, storage.Length, storage)); } /// /// Create a new diagonal matrix and initialize each diagonal value to the same provided value. /// public Matrix Diagonal(int rows, int columns, T value) { if (Zero.Equals(value)) return Diagonal(rows, columns); return Diagonal(DiagonalMatrixStorage.OfValue(rows, columns, value)); } /// /// Create a new diagonal matrix and initialize each diagonal value using the provided init function. /// public Matrix Diagonal(int rows, int columns, Func init) { return Diagonal(DiagonalMatrixStorage.OfInit(rows, columns, init)); } /// /// Create a new diagonal identity matrix with a one-diagonal. /// public Matrix DiagonalIdentity(int rows, int columns) { return Diagonal(DiagonalMatrixStorage.OfValue(rows, columns, One)); } /// /// Create a new diagonal identity matrix with a one-diagonal. /// public Matrix DiagonalIdentity(int order) { return Diagonal(DiagonalMatrixStorage.OfValue(order, order, One)); } /// /// Create a new diagonal matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public Matrix DiagonalOfDiagonalVector(Vector diagonal) { var m = Diagonal(diagonal.Count, diagonal.Count); m.SetDiagonal(diagonal); return m; } /// /// Create a new diagonal matrix with the diagonal as a copy of the given vector. /// This new matrix will be independent from the vector. /// A new memory block will be allocated for storing the matrix. /// public Matrix DiagonalOfDiagonalVector(int rows, int columns, Vector diagonal) { var m = Diagonal(rows, columns); m.SetDiagonal(diagonal); return m; } /// /// Create a new diagonal matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public Matrix DiagonalOfDiagonalArray(T[] diagonal) { var m = Diagonal(diagonal.Length, diagonal.Length); m.SetDiagonal(diagonal); return m; } /// /// Create a new diagonal matrix with the diagonal as a copy of the given array. /// This new matrix will be independent from the array. /// A new memory block will be allocated for storing the matrix. /// public Matrix DiagonalOfDiagonalArray(int rows, int columns, T[] diagonal) { var m = Diagonal(rows, columns); m.SetDiagonal(diagonal); return m; } public abstract IIterationStopCriterion[] IterativeSolverStopCriteria(int maxIterations = 1000); } /// /// Generic linear algebra type builder, for situations where a matrix or vector /// must be created in a generic way. Usage of generic builders should not be /// required in normal user code. /// public abstract class VectorBuilder where T : struct, IEquatable, IFormattable { /// /// Gets the value of 0.0 for type T. /// public abstract T Zero { get; } /// /// Gets the value of 1.0 for type T. /// public abstract T One { get; } /// /// Create a new vector straight from an initialized matrix storage instance. /// If you have an instance of a discrete storage type instead, use their direct methods instead. /// public Vector OfStorage(VectorStorage storage) { if (storage == null) throw new ArgumentNullException(nameof(storage)); if (storage is DenseVectorStorage dense) return Dense(dense); if (storage is SparseVectorStorage sparse) return Sparse(sparse); throw new NotSupportedException(FormattableString.Invariant($"Vector storage type '{storage.GetType().Name}' is not supported. Only DenseVectorStorage and SparseVectorStorage are supported as this point.")); } /// /// Create a new vector with the same kind of the provided example. /// public Vector SameAs(Vector example, int length) where TU : struct, IEquatable, IFormattable { return example.Storage.IsDense ? Dense(length) : Sparse(length); } /// /// Create a new vector with the same kind and dimension of the provided example. /// public Vector SameAs(Vector example) where TU : struct, IEquatable, IFormattable { return example.Storage.IsDense ? Dense(example.Count) : Sparse(example.Count); } /// /// Create a new vector with the same kind of the provided example. /// public Vector SameAs(Matrix example, int length) where TU : struct, IEquatable, IFormattable { return example.Storage.IsDense ? Dense(length) : Sparse(length); } /// /// Create a new vector with a type that can represent and is closest to both provided samples. /// public Vector SameAs(Vector example, Vector otherExample, int length) { return example.Storage.IsDense || otherExample.Storage.IsDense ? Dense(length) : Sparse(length); } /// /// Create a new vector with a type that can represent and is closest to both provided samples and the dimensions of example. /// public Vector SameAs(Vector example, Vector otherExample) { return example.Storage.IsDense || otherExample.Storage.IsDense ? Dense(example.Count) : Sparse(example.Count); } /// /// Create a new vector with a type that can represent and is closest to both provided samples. /// public Vector SameAs(Matrix matrix, Vector vector, int length) { return matrix.Storage.IsDense || vector.Storage.IsDense ? Dense(length) : Sparse(length); } /// /// Create a new dense vector with values sampled from the provided random distribution. /// public abstract Vector Random(int length, IContinuousDistribution distribution); /// /// Create a new dense vector with values sampled from the standard distribution with a system random source. /// public Vector Random(int length) { return Random(length, new Normal(SystemRandomSource.Default)); } /// /// Create a new dense vector with values sampled from the standard distribution with a system random source. /// public Vector Random(int length, int seed) { return Random(length, new Normal(new SystemRandomSource(seed, true))); } /// /// Create a new dense vector straight from an initialized vector storage instance. /// The storage is used directly without copying. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// public abstract Vector Dense(DenseVectorStorage storage); /// /// Create a dense vector of T with the given size. /// /// The size of the vector. public Vector Dense(int size) { return Dense(new DenseVectorStorage(size)); } /// /// Create a dense vector of T that is directly bound to the specified array. /// public Vector Dense(T[] array) { return Dense(new DenseVectorStorage(array.Length, array)); } /// /// Create a new dense vector and initialize each value using the provided value. /// public Vector Dense(int length, T value) { if (Zero.Equals(value)) return Dense(length); return Dense(DenseVectorStorage.OfValue(length, value)); } /// /// Create a new dense vector and initialize each value using the provided init function. /// public Vector Dense(int length, Func init) { return Dense(DenseVectorStorage.OfInit(length, init)); } /// /// Create a new dense vector as a copy of the given other vector. /// This new vector will be independent from the other vector. /// A new memory block will be allocated for storing the vector. /// public Vector DenseOfVector(Vector vector) { return Dense(DenseVectorStorage.OfVector(vector.Storage)); } /// /// Create a new dense vector as a copy of the given array. /// This new vector will be independent from the array. /// A new memory block will be allocated for storing the vector. /// public Vector DenseOfArray(T[] array) { return Dense(DenseVectorStorage.OfVector(new DenseVectorStorage(array.Length, array))); } /// /// Create a new dense vector as a copy of the given enumerable. /// This new vector will be independent from the enumerable. /// A new memory block will be allocated for storing the vector. /// public Vector DenseOfEnumerable(IEnumerable enumerable) { return Dense(DenseVectorStorage.OfEnumerable(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> 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. /// Intended for advanced scenarios where you're working directly with /// storage for performance or interop reasons. /// public abstract Vector Sparse(SparseVectorStorage storage); /// /// Create a sparse vector of T with the given size. /// /// The size of the vector. public Vector Sparse(int size) { return Sparse(new SparseVectorStorage(size)); } /// /// Create a new sparse vector and initialize each value using the provided value. /// public Vector Sparse(int length, T value) { if (Zero.Equals(value)) return Sparse(length); return Sparse(SparseVectorStorage.OfValue(length, value)); } /// /// Create a new sparse vector and initialize each value using the provided init function. /// public Vector Sparse(int length, Func init) { return Sparse(SparseVectorStorage.OfInit(length, init)); } /// /// Create a new sparse vector as a copy of the given other vector. /// This new vector will be independent from the other vector. /// A new memory block will be allocated for storing the vector. /// public Vector SparseOfVector(Vector vector) { return Sparse(SparseVectorStorage.OfVector(vector.Storage)); } /// /// Create a new sparse vector as a copy of the given array. /// This new vector will be independent from the array. /// A new memory block will be allocated for storing the vector. /// public Vector SparseOfArray(T[] array) { return Sparse(SparseVectorStorage.OfEnumerable(array)); } /// /// Create a new sparse vector as a copy of the given enumerable. /// This new vector will be independent from the enumerable. /// A new memory block will be allocated for storing the vector. /// public Vector SparseOfEnumerable(IEnumerable enumerable) { return Sparse(SparseVectorStorage.OfEnumerable(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> enumerable) { return Sparse(SparseVectorStorage.OfIndexedEnumerable(length, enumerable)); } } internal static class BuilderInstance where T : struct, IEquatable, IFormattable { static Lazy, VectorBuilder>> _singleton = new Lazy, VectorBuilder>>(Create); static Tuple, VectorBuilder> Create() { if (typeof (T) == typeof (System.Numerics.Complex)) { return new Tuple, VectorBuilder>( (MatrixBuilder)(object)new LinearAlgebra.Complex.MatrixBuilder(), (VectorBuilder)(object)new LinearAlgebra.Complex.VectorBuilder()); } if (typeof (T) == typeof (Numerics.Complex32)) { return new Tuple, VectorBuilder>( (MatrixBuilder)(object)new Complex32.MatrixBuilder(), (VectorBuilder)(object)new Complex32.VectorBuilder()); } if (typeof (T) == typeof (double)) { return new Tuple, VectorBuilder>( (MatrixBuilder)(object)new Double.MatrixBuilder(), (VectorBuilder)(object)new Double.VectorBuilder()); } if (typeof (T) == typeof (float)) { return new Tuple, VectorBuilder>( (MatrixBuilder)(object)new Single.MatrixBuilder(), (VectorBuilder)(object)new Single.VectorBuilder()); } throw new NotSupportedException(FormattableString.Invariant($"Matrices and vectors of type '{typeof(T).Name}' are not supported. Only Double, Single, Complex or Complex32 are supported at this point.")); } public static void Register(MatrixBuilder matrixBuilder, VectorBuilder vectorBuilder) { _singleton = new Lazy, VectorBuilder>>(() => new Tuple, VectorBuilder>(matrixBuilder, vectorBuilder)); } public static MatrixBuilder Matrix => _singleton.Value.Item1; public static VectorBuilder Vector => _singleton.Value.Item2; } }