// // 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 // 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 System.Runtime; using System.Runtime.CompilerServices; using IStation.Numerics.LinearAlgebra.Storage; using IStation.Numerics.Threading; namespace IStation.Numerics.LinearAlgebra { /// /// Defines the base class for Matrix classes. /// /// Supported data types are double, single, , and . [Serializable] public abstract partial class Matrix : IFormattable, IEquatable> #if !NETSTANDARD1_3 , ICloneable #endif where T : struct, IEquatable, IFormattable { /// /// Initializes a new instance of the Matrix class. /// protected Matrix(MatrixStorage storage) { Storage = storage; RowCount = storage.RowCount; ColumnCount = storage.ColumnCount; } public static readonly MatrixBuilder Build = BuilderInstance.Matrix; /// /// Gets the raw matrix data storage. /// public MatrixStorage Storage { get; private set; } /// /// Gets the number of columns. /// /// The number of columns. public int ColumnCount { get; private set; } /// /// Gets the number of rows. /// /// The number of rows. public int RowCount { get; private set; } /// /// Gets or sets the value at the given row and column, with range checking. /// /// /// The row of the element. /// /// /// The column of the element. /// /// The value to get or set. /// This method is ranged checked. and /// to get and set values without range checking. public T this[int row, int column] { #if !NET40 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] get { return Storage[row, column]; } #if !NET40 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] set { Storage[row, column] = value; } } /// /// Retrieves the requested element without range checking. /// /// /// The row of the element. /// /// /// The column of the element. /// /// /// The requested element. /// #if !NET40 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public T At(int row, int column) { return Storage.At(row, column); } /// /// Sets the value of the given element without range checking. /// /// /// The row of the element. /// /// /// The column of the element. /// /// /// The value to set the element to. /// #if !NET40 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public void At(int row, int column, T value) { Storage.At(row, column, value); } /// /// Sets all values to zero. /// public void Clear() { Storage.Clear(); } /// /// Sets all values of a row to zero. /// public void ClearRow(int rowIndex) { if ((uint)rowIndex >= (uint)RowCount) { throw new ArgumentOutOfRangeException(nameof(rowIndex)); } Storage.ClearUnchecked(rowIndex, 1, 0, ColumnCount); } /// /// Sets all values of a column to zero. /// public void ClearColumn(int columnIndex) { if ((uint)columnIndex >= (uint)ColumnCount) { throw new ArgumentOutOfRangeException(nameof(columnIndex)); } Storage.ClearUnchecked(0, RowCount, columnIndex, 1); } /// /// Sets all values for all of the chosen rows to zero. /// public void ClearRows(params int[] rowIndices) { Storage.ClearRows(rowIndices); } /// /// Sets all values for all of the chosen columns to zero. /// public void ClearColumns(params int[] columnIndices) { Storage.ClearColumns(columnIndices); } /// /// Sets all values of a sub-matrix to zero. /// public void ClearSubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { Storage.Clear(rowIndex, rowCount, columnIndex, columnCount); } /// /// Set all values whose absolute value is smaller than the threshold to zero, in-place. /// public abstract void CoerceZero(double threshold); /// /// Set all values that meet the predicate to zero, in-place. /// public void CoerceZero(Func zeroPredicate) { MapInplace(x => zeroPredicate(x) ? Zero : x, Zeros.AllowSkip); } /// /// Creates a clone of this instance. /// /// /// A clone of the instance. /// public Matrix Clone() { var result = Build.SameAs(this); Storage.CopyToUnchecked(result.Storage, ExistingData.AssumeZeros); return result; } /// /// Copies the elements of this matrix to the given matrix. /// /// /// The matrix to copy values into. /// /// /// If target is . /// /// /// If this and the target matrix do not have the same dimensions.. /// public void CopyTo(Matrix target) { if (target == null) { throw new ArgumentNullException(nameof(target)); } Storage.CopyTo(target.Storage); } /// /// Copies a row into an Vector. /// /// The row to copy. /// A Vector containing the copied elements. /// If is negative, /// or greater than or equal to the number of rows. public Vector Row(int index) { if ((uint)index >= (uint)RowCount) { throw new ArgumentOutOfRangeException(nameof(index)); } var ret = Vector.Build.SameAs(this, ColumnCount); Storage.CopySubRowToUnchecked(ret.Storage, index, 0, 0, ColumnCount, ExistingData.AssumeZeros); return ret; } /// /// Copies a row into to the given Vector. /// /// The row to copy. /// The Vector to copy the row into. /// If the result vector is . /// If is negative, /// or greater than or equal to the number of rows. /// If this.Columns != result.Count. public void Row(int index, Vector result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } Storage.CopyRowTo(result.Storage, index); } /// /// Copies the requested row elements into a new Vector. /// /// The row to copy elements from. /// The column to start copying from. /// The number of elements to copy. /// A Vector containing the requested elements. /// If: /// is negative, /// or greater than or equal to the number of rows. /// is negative, /// or greater than or equal to the number of columns. /// (columnIndex + length) >= Columns. /// If is not positive. public Vector Row(int rowIndex, int columnIndex, int length) { var ret = Vector.Build.SameAs(this, length); Storage.CopySubRowTo(ret.Storage, rowIndex, columnIndex, 0, length); return ret; } /// /// Copies the requested row elements into a new Vector. /// /// The row to copy elements from. /// The column to start copying from. /// The number of elements to copy. /// The Vector to copy the column into. /// If the result Vector is . /// If is negative, /// or greater than or equal to the number of columns. /// If is negative, /// or greater than or equal to the number of rows. /// If + /// is greater than or equal to the number of rows. /// If is not positive. /// If result.Count < length. public void Row(int rowIndex, int columnIndex, int length, Vector result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } Storage.CopySubRowTo(result.Storage, rowIndex, columnIndex, 0, length); } /// /// Copies a column into a new Vector>. /// /// The column to copy. /// A Vector containing the copied elements. /// If is negative, /// or greater than or equal to the number of columns. public Vector Column(int index) { if ((uint)index >= (uint)ColumnCount) { throw new ArgumentOutOfRangeException(nameof(index)); } var ret = Vector.Build.SameAs(this, RowCount); Storage.CopySubColumnToUnchecked(ret.Storage, index, 0, 0, RowCount, ExistingData.AssumeZeros); return ret; } /// /// Copies a column into to the given Vector. /// /// The column to copy. /// The Vector to copy the column into. /// If the result Vector is . /// If is negative, /// or greater than or equal to the number of columns. /// If this.Rows != result.Count. public void Column(int index, Vector result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } Storage.CopyColumnTo(result.Storage, index); } /// /// Copies the requested column elements into a new Vector. /// /// The column to copy elements from. /// The row to start copying from. /// The number of elements to copy. /// A Vector containing the requested elements. /// If: /// is negative, /// or greater than or equal to the number of columns. /// is negative, /// or greater than or equal to the number of rows. /// (rowIndex + length) >= Rows. /// /// If is not positive. public Vector Column(int columnIndex, int rowIndex, int length) { var ret = Vector.Build.SameAs(this, length); Storage.CopySubColumnTo(ret.Storage, columnIndex, rowIndex, 0, length); return ret; } /// /// Copies the requested column elements into the given vector. /// /// The column to copy elements from. /// The row to start copying from. /// The number of elements to copy. /// The Vector to copy the column into. /// If the result Vector is . /// If is negative, /// or greater than or equal to the number of columns. /// If is negative, /// or greater than or equal to the number of rows. /// If + /// is greater than or equal to the number of rows. /// If is not positive. /// If result.Count < length. public void Column(int columnIndex, int rowIndex, int length, Vector result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } Storage.CopySubColumnTo(result.Storage, columnIndex, rowIndex, 0, length); } /// /// Returns a new matrix containing the upper triangle of this matrix. /// /// The upper triangle of this matrix. public virtual Matrix UpperTriangle() { var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = row; column < ColumnCount; column++) { result.At(row, column, At(row, column)); } } return result; } /// /// Returns a new matrix containing the lower triangle of this matrix. /// /// The lower triangle of this matrix. public virtual Matrix LowerTriangle() { var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = 0; column <= row && column < ColumnCount; column++) { result.At(row, column, At(row, column)); } } return result; } /// /// Puts the lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public virtual void LowerTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } for (var row = 0; row < RowCount; row++) { for (var column = 0; column < ColumnCount; column++) { result.At(row, column, row >= column ? At(row, column) : Zero); } } } /// /// Puts the upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public virtual void UpperTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } for (var row = 0; row < RowCount; row++) { for (var column = 0; column < ColumnCount; column++) { result.At(row, column, row <= column ? At(row, column) : Zero); } } } /// /// Creates a matrix that contains the values from the requested sub-matrix. /// /// The row to start copying from. /// The number of rows to copy. Must be positive. /// The column to start copying from. /// The number of columns to copy. Must be positive. /// The requested sub-matrix. /// If: is /// negative, or greater than or equal to the number of rows. /// is negative, or greater than or equal to the number /// of columns. /// (columnIndex + columnLength) >= Columns /// (rowIndex + rowLength) >= Rows /// If or /// is not positive. public virtual Matrix SubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { var result = Build.SameAs(this, rowCount, columnCount); Storage.CopySubMatrixTo(result.Storage, rowIndex, 0, rowCount, columnIndex, 0, columnCount, ExistingData.AssumeZeros); return result; } /// /// Returns the elements of the diagonal in a Vector. /// /// The elements of the diagonal. /// For non-square matrices, the method returns Min(Rows, Columns) elements where /// i == j (i is the row index, and j is the column index). public virtual Vector Diagonal() { var min = Math.Min(RowCount, ColumnCount); var diagonal = Vector.Build.SameAs(this, min); for (var i = 0; i < min; i++) { diagonal.At(i, At(i, i)); } return diagonal; } /// /// Returns a new matrix containing the lower triangle of this matrix. The new matrix /// does not contain the diagonal elements of this matrix. /// /// The lower triangle of this matrix. public virtual Matrix StrictlyLowerTriangle() { var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { var columns = Math.Min(row, ColumnCount); for (var column = 0; column < columns; column++) { result.At(row, column, At(row, column)); } } return result; } /// /// Puts the strictly lower triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public virtual void StrictlyLowerTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } for (var row = 0; row < RowCount; row++) { for (var column = 0; column < ColumnCount; column++) { result.At(row, column, row > column ? At(row, column) : Zero); } } } /// /// Returns a new matrix containing the upper triangle of this matrix. The new matrix /// does not contain the diagonal elements of this matrix. /// /// The upper triangle of this matrix. public virtual Matrix StrictlyUpperTriangle() { var result = Build.SameAs(this); for (var row = 0; row < RowCount; row++) { for (var column = row + 1; column < ColumnCount; column++) { result.At(row, column, At(row, column)); } } return result; } /// /// Puts the strictly upper triangle of this matrix into the result matrix. /// /// Where to store the lower triangle. /// If is . /// If the result matrix's dimensions are not the same as this matrix. public virtual void StrictlyUpperTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } for (var row = 0; row < RowCount; row++) { for (var column = 0; column < ColumnCount; column++) { result.At(row, column, row < column ? At(row, column) : Zero); } } } /// /// Creates a new matrix and inserts the given column at the given index. /// /// The index of where to insert the column. /// The column to insert. /// A new matrix with the inserted column. /// If is . /// If is < zero or > the number of columns. /// If the size of != the number of rows. public Matrix InsertColumn(int columnIndex, Vector column) { if (column == null) { throw new ArgumentNullException(nameof(column)); } if ((uint)columnIndex > (uint)ColumnCount) { throw new ArgumentOutOfRangeException(nameof(columnIndex)); } if (column.Count != RowCount) { throw new ArgumentException("Matrix row dimensions must agree.", nameof(column)); } var result = Build.SameAs(this, RowCount, ColumnCount + 1, fullyMutable: true); Storage.CopySubMatrixTo(result.Storage, 0, 0, RowCount, 0, 0, columnIndex, ExistingData.AssumeZeros); result.SetColumn(columnIndex, column); Storage.CopySubMatrixTo(result.Storage, 0, 0, RowCount, columnIndex, columnIndex + 1, ColumnCount - columnIndex, ExistingData.AssumeZeros); return result; } /// /// Creates a new matrix with the given column removed. /// /// The index of the column to remove. /// A new matrix without the chosen column. /// If is < zero or >= the number of columns. public Matrix RemoveColumn(int columnIndex) { if ((uint)columnIndex >= (uint)ColumnCount) { throw new ArgumentOutOfRangeException(nameof(columnIndex)); } var result = Build.SameAs(this, RowCount, ColumnCount - 1, fullyMutable: true); Storage.CopySubMatrixTo(result.Storage, 0, 0, RowCount, 0, 0, columnIndex, ExistingData.AssumeZeros); Storage.CopySubMatrixTo(result.Storage, 0, 0, RowCount, columnIndex + 1, columnIndex, ColumnCount - columnIndex - 1, ExistingData.AssumeZeros); return result; } /// /// Copies the values of the given Vector to the specified column. /// /// The column to copy the values to. /// The vector to copy the values from. /// If is . /// If is less than zero, /// or greater than or equal to the number of columns. /// If the size of does not /// equal the number of rows of this Matrix. public void SetColumn(int columnIndex, Vector column) { if (column == null) { throw new ArgumentNullException(nameof(column)); } column.Storage.CopyToColumn(Storage, columnIndex); } /// /// Copies the values of the given Vector to the specified sub-column. /// /// The column to copy the values to. /// The row to start copying to. /// The number of elements to copy. /// The vector to copy the values from. /// If is . /// If is less than zero, /// or greater than or equal to the number of columns. /// If the size of does not /// equal the number of rows of this Matrix. public void SetColumn(int columnIndex, int rowIndex, int length, Vector column) { if (column == null) { throw new ArgumentNullException(nameof(column)); } column.Storage.CopyToSubColumn(Storage, columnIndex, 0, rowIndex, length); } /// /// Copies the values of the given array to the specified column. /// /// The column to copy the values to. /// The array to copy the values from. /// If is . /// If is less than zero, /// or greater than or equal to the number of columns. /// If the size of does not /// equal the number of rows of this Matrix. /// If the size of does not /// equal the number of rows of this Matrix. public void SetColumn(int columnIndex, T[] column) { if (column == null) { throw new ArgumentNullException(nameof(column)); } new DenseVectorStorage(column.Length, column).CopyToColumn(Storage, columnIndex); } /// /// Creates a new matrix and inserts the given row at the given index. /// /// The index of where to insert the row. /// The row to insert. /// A new matrix with the inserted column. /// If is . /// If is < zero or > the number of rows. /// If the size of != the number of columns. public Matrix InsertRow(int rowIndex, Vector row) { if (row == null) { throw new ArgumentNullException(nameof(row)); } if ((uint)rowIndex > (uint)RowCount) { throw new ArgumentOutOfRangeException(nameof(rowIndex)); } if (row.Count != ColumnCount) { throw new ArgumentException("Matrix row dimensions must agree.", nameof(row)); } var result = Build.SameAs(this, RowCount + 1, ColumnCount, fullyMutable: true); Storage.CopySubMatrixTo(result.Storage, 0, 0, rowIndex, 0, 0, ColumnCount, ExistingData.AssumeZeros); result.SetRow(rowIndex, row); Storage.CopySubMatrixTo(result.Storage, rowIndex, rowIndex+1, RowCount - rowIndex, 0, 0, ColumnCount, ExistingData.AssumeZeros); return result; } /// /// Creates a new matrix with the given row removed. /// /// The index of the row to remove. /// A new matrix without the chosen row. /// If is < zero or >= the number of rows. public Matrix RemoveRow(int rowIndex) { if ((uint)rowIndex >= (uint)RowCount) { throw new ArgumentOutOfRangeException(nameof(rowIndex)); } var result = Build.SameAs(this, RowCount - 1, ColumnCount, fullyMutable: true); Storage.CopySubMatrixTo(result.Storage, 0, 0, rowIndex, 0, 0, ColumnCount, ExistingData.AssumeZeros); Storage.CopySubMatrixTo(result.Storage, rowIndex + 1, rowIndex, RowCount - rowIndex - 1, 0, 0, ColumnCount, ExistingData.AssumeZeros); return result; } /// /// Copies the values of the given Vector to the specified row. /// /// The row to copy the values to. /// The vector to copy the values from. /// If is . /// If is less than zero, /// or greater than or equal to the number of rows. /// If the size of does not /// equal the number of columns of this Matrix. public void SetRow(int rowIndex, Vector row) { if (row == null) { throw new ArgumentNullException(nameof(row)); } row.Storage.CopyToRow(Storage, rowIndex); } /// /// Copies the values of the given Vector to the specified sub-row. /// /// The row to copy the values to. /// The column to start copying to. /// The number of elements to copy. /// The vector to copy the values from. /// If is . /// If is less than zero, /// or greater than or equal to the number of rows. /// If the size of does not /// equal the number of columns of this Matrix. public void SetRow(int rowIndex, int columnIndex, int length, Vector row) { if (row == null) { throw new ArgumentNullException(nameof(row)); } row.Storage.CopyToSubRow(Storage, rowIndex, 0, columnIndex, length); } /// /// Copies the values of the given array to the specified row. /// /// The row to copy the values to. /// The array to copy the values from. /// If is . /// If is less than zero, /// or greater than or equal to the number of rows. /// If the size of does not /// equal the number of columns of this Matrix. public void SetRow(int rowIndex, T[] row) { if (row == null) { throw new ArgumentNullException(nameof(row)); } new DenseVectorStorage(row.Length, row).CopyToRow(Storage, rowIndex); } /// /// Copies the values of a given matrix into a region in this matrix. /// /// The row to start copying to. /// The column to start copying to. /// The sub-matrix to copy from. /// If: is /// negative, or greater than or equal to the number of rows. /// is negative, or greater than or equal to the number /// of columns. /// (columnIndex + columnLength) >= Columns /// (rowIndex + rowLength) >= Rows public void SetSubMatrix(int rowIndex, int columnIndex, Matrix subMatrix) { subMatrix.Storage.CopySubMatrixTo(Storage, 0, rowIndex, subMatrix.RowCount, 0, columnIndex, subMatrix.ColumnCount); } /// /// Copies the values of a given matrix into a region in this matrix. /// /// The row to start copying to. /// The number of rows to copy. Must be positive. /// The column to start copying to. /// The number of columns to copy. Must be positive. /// The sub-matrix to copy from. /// If: is /// negative, or greater than or equal to the number of rows. /// is negative, or greater than or equal to the number /// of columns. /// (columnIndex + columnLength) >= Columns /// (rowIndex + rowLength) >= Rows /// the size of is not at least x . /// If or /// is not positive. public void SetSubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount, Matrix subMatrix) { subMatrix.Storage.CopySubMatrixTo(Storage, 0, rowIndex, rowCount, 0, columnIndex, columnCount); } /// /// Copies the values of a given matrix into a region in this matrix. /// /// The row to start copying to. /// The row of the sub-matrix to start copying from. /// The number of rows to copy. Must be positive. /// The column to start copying to. /// The column of the sub-matrix to start copying from. /// The number of columns to copy. Must be positive. /// The sub-matrix to copy from. /// If: is /// negative, or greater than or equal to the number of rows. /// is negative, or greater than or equal to the number /// of columns. /// (columnIndex + columnLength) >= Columns /// (rowIndex + rowLength) >= Rows /// the size of is not at least x . /// If or /// is not positive. public void SetSubMatrix(int rowIndex, int sorceRowIndex, int rowCount, int columnIndex, int sourceColumnIndex, int columnCount, Matrix subMatrix) { subMatrix.Storage.CopySubMatrixTo(Storage, sorceRowIndex, rowIndex, rowCount, sourceColumnIndex, columnIndex, columnCount); } /// /// Copies the values of the given Vector to the diagonal. /// /// The vector to copy the values from. The length of the vector should be /// Min(Rows, Columns). /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public virtual void SetDiagonal(Vector source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } var min = Math.Min(RowCount, ColumnCount); if (source.Count != min) { throw new ArgumentException("All vectors must have the same dimensionality.", nameof(source)); } for (var i = 0; i < min; i++) { At(i, i, source.At(i)); } } /// /// Copies the values of the given array to the diagonal. /// /// The array to copy the values from. The length of the vector should be /// Min(Rows, Columns). /// If is . /// If the length of does not /// equal Min(Rows, Columns). /// For non-square matrices, the elements of are copied to /// this[i,i]. public virtual void SetDiagonal(T[] source) { if (source == null) { throw new ArgumentNullException(nameof(source)); } var min = Math.Min(RowCount, ColumnCount); if (source.Length != min) { throw new ArgumentException("The array arguments must have the same length.", nameof(source)); } for (var i = 0; i < min; i++) { At(i, i, source[i]); } } /// /// Creates a new matrix with the desired size and copies this matrix to it. /// Values which no longer exist in the new matrix are ignored, new values are set to zero. /// /// The number of rows of the new matrix. /// The number of columns of the new matrix. /// A new matrix with the desired rows and columns. public Matrix Resize(int rowCount, int columnCount) { var result = Build.SameAs(this, rowCount, columnCount, fullyMutable: true); Storage.CopySubMatrixTo(result.Storage, 0, 0, Math.Min(RowCount, rowCount), 0, 0, Math.Min(ColumnCount, columnCount), ExistingData.AssumeZeros); return result; } /// /// Returns the transpose of this matrix. /// /// The transpose of this matrix. public Matrix Transpose() { var result = Build.SameAs(this, ColumnCount, RowCount); Storage.TransposeToUnchecked(result.Storage, ExistingData.AssumeZeros); return result; } /// /// Puts the transpose of this matrix into the result matrix. /// public void Transpose(Matrix result) { Storage.TransposeTo(result.Storage, ExistingData.Clear); } /// /// Returns the conjugate transpose of this matrix. /// /// The conjugate transpose of this matrix. public abstract Matrix ConjugateTranspose(); /// /// Puts the conjugate transpose of this matrix into the result matrix. /// public abstract void ConjugateTranspose(Matrix result); /// /// Permute the rows of a matrix according to a permutation. /// /// The row permutation to apply to this matrix. public virtual void PermuteRows(Permutation p) { if (p.Dimension != RowCount) { throw new ArgumentException("The array arguments must have the same length.", nameof(p)); } // Get a sequence of inversions from the permutation. var inv = p.ToInversions(); for (var i = 0; i < inv.Length; i++) { if (inv[i] != i) { var q = inv[i]; for (var j = 0; j < ColumnCount; j++) { var temp = At(q, j); At(q, j, At(i, j)); At(i, j, temp); } } } } /// /// Permute the columns of a matrix according to a permutation. /// /// The column permutation to apply to this matrix. public virtual void PermuteColumns(Permutation p) { if (p.Dimension != ColumnCount) { throw new ArgumentException("The array arguments must have the same length.", nameof(p)); } // Get a sequence of inversions from the permutation. var inv = p.ToInversions(); for (var i = 0; i < inv.Length; i++) { if (inv[i] != i) { var q = inv[i]; for (var j = 0; j < RowCount; j++) { var temp = At(j, q); At(j, q, At(j, i)); At(j, i, temp); } } } } /// /// Concatenates this matrix with the given matrix. /// /// The matrix to concatenate. /// The combined matrix. /// /// public Matrix Append(Matrix right) { if (right == null) { throw new ArgumentNullException(nameof(right)); } if (right.RowCount != RowCount) { throw new ArgumentException("Matrix row dimensions must agree."); } var result = Build.SameAs(this, right, RowCount, ColumnCount + right.ColumnCount, fullyMutable: true); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, ExistingData.AssumeZeros); right.Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, right.RowCount, 0, ColumnCount, right.ColumnCount, ExistingData.AssumeZeros); return result; } /// /// Concatenates this matrix with the given matrix and places the result into the result matrix. /// /// The matrix to concatenate. /// The combined matrix. /// /// public void Append(Matrix right, Matrix result) { if (right == null) { throw new ArgumentNullException(nameof(right)); } if (right.RowCount != RowCount) { throw new ArgumentException("Matrix row dimensions must agree."); } if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.ColumnCount != (ColumnCount + right.ColumnCount) || result.RowCount != RowCount) { throw new ArgumentException("Matrix column dimensions must agree."); } Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, ExistingData.Clear); right.Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, right.RowCount, 0, ColumnCount, right.ColumnCount, ExistingData.Clear); } /// /// Stacks this matrix on top of the given matrix and places the result into the result matrix. /// /// The matrix to stack this matrix upon. /// The combined matrix. /// If lower is . /// If upper.Columns != lower.Columns. /// /// public Matrix Stack(Matrix lower) { if (lower == null) { throw new ArgumentNullException(nameof(lower)); } if (lower.ColumnCount != ColumnCount) { throw new ArgumentException("Matrix column dimensions must agree.", nameof(lower)); } var result = Build.SameAs(this, lower, RowCount + lower.RowCount, ColumnCount, fullyMutable: true); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, ExistingData.AssumeZeros); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, 0, lower.ColumnCount, ExistingData.AssumeZeros); return result; } /// /// Stacks this matrix on top of the given matrix and places the result into the result matrix. /// /// The matrix to stack this matrix upon. /// The combined matrix. /// If lower is . /// If upper.Columns != lower.Columns. /// /// public void Stack(Matrix lower, Matrix result) { if (lower == null) { throw new ArgumentNullException(nameof(lower)); } if (lower.ColumnCount != ColumnCount) { throw new ArgumentException("Matrix column dimensions must agree.", nameof(lower)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != (RowCount + lower.RowCount) || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, ExistingData.Clear); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, 0, lower.ColumnCount, ExistingData.Clear); } /// /// Diagonally stacks his matrix on top of the given matrix. The new matrix is a M-by-N matrix, /// where M = this.Rows + lower.Rows and N = this.Columns + lower.Columns. /// The values of off the off diagonal matrices/blocks are set to zero. /// /// The lower, right matrix. /// If lower is . /// the combined matrix /// /// public Matrix DiagonalStack(Matrix lower) { if (lower == null) { throw new ArgumentNullException(nameof(lower)); } var result = Build.SameAs(this, lower, RowCount + lower.RowCount, ColumnCount + lower.ColumnCount, RowCount != ColumnCount); Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, ExistingData.AssumeZeros); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, ColumnCount, lower.ColumnCount, ExistingData.AssumeZeros); return result; } /// /// Diagonally stacks his matrix on top of the given matrix and places the combined matrix into the result matrix. /// /// The lower, right matrix. /// The combined matrix /// If lower is . /// If the result matrix is . /// If the result matrix's dimensions are not (this.Rows + lower.rows) x (this.Columns + lower.Columns). /// /// public void DiagonalStack(Matrix lower, Matrix result) { if (lower == null) { throw new ArgumentNullException(nameof(lower)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } if (result.RowCount != RowCount + lower.RowCount || result.ColumnCount != ColumnCount + lower.ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } Storage.CopySubMatrixToUnchecked(result.Storage, 0, 0, RowCount, 0, 0, ColumnCount, ExistingData.Clear); lower.Storage.CopySubMatrixToUnchecked(result.Storage, 0, RowCount, lower.RowCount, 0, ColumnCount, lower.ColumnCount, ExistingData.Clear); } /// /// Evaluates whether this matrix is symmetric. /// public virtual bool IsSymmetric() { if (RowCount != ColumnCount) { return false; } for (var row = 0; row < RowCount; row++) { for (var column = row + 1; column < ColumnCount; column++) { if (!At(row, column).Equals(At(column, row))) { return false; } } } return true; } /// /// Evaluates whether this matrix is Hermitian (conjugate symmetric). /// public abstract bool IsHermitian(); /// /// Returns this matrix as a multidimensional array. /// The returned array will be independent from this matrix. /// A new memory block will be allocated for the array. /// /// A multidimensional containing the values of this matrix. public T[,] ToArray() { return Storage.ToArray(); } /// /// Returns the matrix's elements as an array with the data laid out column by column (column major). /// The returned array will be independent from this matrix. /// A new memory block will be allocated for the array. /// ///
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 4, 7, 2, 5, 8, 3, 6, 9
        /// 7, 8, 9
        /// 
/// An array containing the matrix's elements. /// /// public T[] ToColumnMajorArray() { return Storage.ToColumnMajorArray(); } /// /// Returns the matrix's elements as an array with the data laid row by row (row major). /// The returned array will be independent from this matrix. /// A new memory block will be allocated for the array. /// ///
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 2, 3, 4, 5, 6, 7, 8, 9
        /// 7, 8, 9
        /// 
/// An array containing the matrix's elements. /// /// public T[] ToRowMajorArray() { return Storage.ToRowMajorArray(); } /// /// Returns this matrix as array of row arrays. /// The returned arrays will be independent from this matrix. /// A new memory block will be allocated for the arrays. /// public T[][] ToRowArrays() { return Storage.ToRowArrays(); } /// /// Returns this matrix as array of column arrays. /// The returned arrays will be independent from this matrix. /// A new memory block will be allocated for the arrays. /// public T[][] ToColumnArrays() { return Storage.ToColumnArrays(); } /// /// Returns the internal multidimensional array of this matrix if, and only if, this matrix is stored by such an array internally. /// Otherwise returns null. Changes to the returned array and the matrix will affect each other. /// Use ToArray instead if you always need an independent array. /// public T[,] AsArray() { return Storage.AsArray(); } /// /// Returns the internal column by column (column major) array of this matrix if, and only if, this matrix is stored by such arrays internally. /// Otherwise returns null. Changes to the returned arrays and the matrix will affect each other. /// Use ToColumnMajorArray instead if you always need an independent array. /// ///
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 4, 7, 2, 5, 8, 3, 6, 9
        /// 7, 8, 9
        /// 
/// An array containing the matrix's elements. /// /// public T[] AsColumnMajorArray() { return Storage.AsColumnMajorArray(); } /// /// Returns the internal row by row (row major) array of this matrix if, and only if, this matrix is stored by such arrays internally. /// Otherwise returns null. Changes to the returned arrays and the matrix will affect each other. /// Use ToRowMajorArray instead if you always need an independent array. /// ///
        /// 1, 2, 3
        /// 4, 5, 6  will be returned as  1, 2, 3, 4, 5, 6, 7, 8, 9
        /// 7, 8, 9
        /// 
/// An array containing the matrix's elements. /// /// public T[] AsRowMajorArray() { return Storage.AsRowMajorArray(); } /// /// Returns the internal row arrays of this matrix if, and only if, this matrix is stored by such arrays internally. /// Otherwise returns null. Changes to the returned arrays and the matrix will affect each other. /// Use ToRowArrays instead if you always need an independent array. /// public T[][] AsRowArrays() { return Storage.AsRowArrays(); } /// /// Returns the internal column arrays of this matrix if, and only if, this matrix is stored by such arrays internally. /// Otherwise returns null. Changes to the returned arrays and the matrix will affect each other. /// Use ToColumnArrays instead if you always need an independent array. /// public T[][] AsColumnArrays() { return Storage.AsColumnArrays(); } /// /// Returns an IEnumerable that can be used to iterate through all values of the matrix. /// /// /// The enumerator will include all values, even if they are zero. /// The ordering of the values is unspecified (not necessarily column-wise or row-wise). /// public IEnumerable Enumerate() { return Storage.Enumerate(); } /// /// Returns an IEnumerable that can be used to iterate through all values of the matrix. /// /// /// The enumerator will include all values, even if they are zero. /// The ordering of the values is unspecified (not necessarily column-wise or row-wise). /// public IEnumerable Enumerate(Zeros zeros = Zeros.Include) { switch (zeros) { case Zeros.AllowSkip: return Storage.EnumerateNonZero(); default: return Storage.Enumerate(); } } /// /// Returns an IEnumerable that can be used to iterate through all values of the matrix and their index. /// /// /// The enumerator returns a Tuple with the first two values being the row and column index /// 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() { return Storage.EnumerateIndexed(); } /// /// Returns an IEnumerable that can be used to iterate through all values of the matrix and their index. /// /// /// The enumerator returns a Tuple with the first two values being the row and column index /// 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) { switch (zeros) { case Zeros.AllowSkip: return Storage.EnumerateNonZeroIndexed(); default: return Storage.EnumerateIndexed(); } } /// /// Returns an IEnumerable that can be used to iterate through all columns of the matrix. /// public IEnumerable> EnumerateColumns() { for (var i = 0; i < ColumnCount; i++) { yield return Column(i); } } /// /// Returns an IEnumerable that can be used to iterate through a subset of all columns of the matrix. /// /// The column to start enumerating over. /// The number of columns to enumerating over. public IEnumerable> EnumerateColumns(int index, int length) { var maxIndex = Math.Min(index + length, ColumnCount); for (var i = Math.Max(index, 0); i < maxIndex; i++) { yield return Column(i); } } /// /// Returns an IEnumerable that can be used to iterate through all columns of the matrix and their index. /// /// /// 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() { for (var i = 0; i < ColumnCount; i++) { yield return new Tuple>(i, Column(i)); } } /// /// Returns an IEnumerable that can be used to iterate through a subset of all columns of the matrix and their index. /// /// The column to start enumerating over. /// The number of columns to enumerating over. /// /// 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) { var maxIndex = Math.Min(index + length, ColumnCount); for (var i = Math.Max(index, 0); i < maxIndex; i++) { yield return new Tuple>(i, Column(i)); } } /// /// Returns an IEnumerable that can be used to iterate through all rows of the matrix. /// public IEnumerable> EnumerateRows() { for (var i = 0; i < RowCount; i++) { yield return Row(i); } } /// /// Returns an IEnumerable that can be used to iterate through a subset of all rows of the matrix. /// /// The row to start enumerating over. /// The number of rows to enumerating over. public IEnumerable> EnumerateRows(int index, int length) { var maxIndex = Math.Min(index + length, RowCount); for (var i = Math.Max(index, 0); i < maxIndex; i++) { yield return Row(i); } } /// /// Returns an IEnumerable that can be used to iterate through all rows of the matrix and their index. /// /// /// 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() { for (var i = 0; i < RowCount; i++) { yield return new Tuple>(i, Row(i)); } } /// /// Returns an IEnumerable that can be used to iterate through a subset of all rows of the matrix and their index. /// /// The row to start enumerating over. /// The number of rows to enumerating over. /// /// 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) { var maxIndex = Math.Min(index + length, RowCount); for (var i = Math.Max(index, 0); i < maxIndex; i++) { yield return new Tuple>(i, Row(i)); } } /// /// Applies a function to each value of this matrix and replaces the value with its result. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public void MapInplace(Func f, Zeros zeros = Zeros.AllowSkip) { Storage.MapInplace(f, zeros); } /// /// Applies a function to each value of this matrix and replaces the value with its result. /// The row and column indices of each value (zero-based) are passed as first arguments to the function. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public void MapIndexedInplace(Func f, Zeros zeros = Zeros.AllowSkip) { Storage.MapIndexedInplace(f, zeros); } /// /// Applies a function to each value of this matrix and replaces the value in the result matrix. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public void Map(Func f, Matrix result, Zeros zeros = Zeros.AllowSkip) { if (ReferenceEquals(this, result)) { Storage.MapInplace(f, zeros); } else { Storage.MapTo(result.Storage, f, zeros, zeros == Zeros.Include ? ExistingData.AssumeZeros : ExistingData.Clear); } } /// /// Applies a function to each value of this matrix and replaces the value in the result matrix. /// The index of each value (zero-based) is passed as first argument to the function. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public void MapIndexed(Func f, Matrix result, Zeros zeros = Zeros.AllowSkip) { if (ReferenceEquals(this, result)) { Storage.MapIndexedInplace(f, zeros); } else { Storage.MapIndexedTo(result.Storage, f, zeros, zeros == Zeros.Include ? ExistingData.AssumeZeros : ExistingData.Clear); } } /// /// Applies a function to each value of this matrix and replaces the value in the result matrix. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public void MapConvert(Func f, Matrix result, Zeros zeros = Zeros.AllowSkip) where TU : struct, IEquatable, IFormattable { Storage.MapTo(result.Storage, f, zeros, zeros == Zeros.Include ? ExistingData.AssumeZeros : ExistingData.Clear); } /// /// Applies a function to each value of this matrix and replaces the value in the result matrix. /// The index of each value (zero-based) is passed as first argument to the function. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public void MapIndexedConvert(Func f, Matrix result, Zeros zeros = Zeros.AllowSkip) where TU : struct, IEquatable, IFormattable { Storage.MapIndexedTo(result.Storage, f, zeros, zeros == Zeros.Include ? ExistingData.AssumeZeros : ExistingData.Clear); } /// /// Applies a function to each value of this matrix and returns the results as a new matrix. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public Matrix Map(Func f, Zeros zeros = Zeros.AllowSkip) where TU : struct, IEquatable, IFormattable { var result = Matrix.Build.SameAs(this, RowCount, ColumnCount, fullyMutable: zeros == Zeros.Include); Storage.MapToUnchecked(result.Storage, f, zeros, ExistingData.AssumeZeros); return result; } /// /// Applies a function to each value of this matrix and returns the results as a new matrix. /// The index of each value (zero-based) is passed as first argument to the function. /// If forceMapZero is not set to true, zero values may or may not be skipped depending /// on the actual data storage implementation (relevant mostly for sparse matrices). /// public Matrix MapIndexed(Func f, Zeros zeros = Zeros.AllowSkip) where TU : struct, IEquatable, IFormattable { var result = Matrix.Build.SameAs(this, RowCount, ColumnCount, fullyMutable: zeros == Zeros.Include); Storage.MapIndexedToUnchecked(result.Storage, f, zeros, ExistingData.AssumeZeros); return result; } /// /// For each row, applies a function f to each element of the row, threading an accumulator argument through the computation. /// Returns an array with the resulting accumulator states for each row. /// public TU[] FoldByRow(Func f, TU state, Zeros zeros = Zeros.AllowSkip) { var result = new TU[RowCount]; if (!EqualityComparer.Default.Equals(state, default(TU))) { CommonParallel.For(0, result.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { result[i] = state; } }); } Storage.FoldByRowUnchecked(result, f, (x, c) => x, result, zeros); return result; } /// /// For each column, applies a function f to each element of the column, threading an accumulator argument through the computation. /// Returns an array with the resulting accumulator states for each column. /// public TU[] FoldByColumn(Func f, TU state, Zeros zeros = Zeros.AllowSkip) { var result = new TU[ColumnCount]; if (!EqualityComparer.Default.Equals(state, default(TU))) { CommonParallel.For(0, result.Length, 4096, (a, b) => { for (int i = a; i < b; i++) { result[i] = state; } }); } Storage.FoldByColumnUnchecked(result, f, (x, c) => x, result, zeros); return result; } /// /// Applies a function f to each row vector, threading an accumulator vector argument through the computation. /// Returns the resulting accumulator vector. /// public Vector FoldRows(Func, Vector, Vector> f, Vector state) where TU : struct, IEquatable, IFormattable { foreach (var vector in EnumerateRows()) { state = f(state, vector); } return state; } /// /// Applies a function f to each column vector, threading an accumulator vector argument through the computation. /// Returns the resulting accumulator vector. /// public Vector FoldColumns(Func, Vector, Vector> f, Vector state) where TU : struct, IEquatable, IFormattable { foreach (var vector in EnumerateColumns()) { state = f(state, vector); } return state; } /// /// Reduces all row vectors by applying a function between two of them, until only a single vector is left. /// public Vector ReduceRows(Func, Vector, Vector> f) { return EnumerateRows().Aggregate(f); } /// /// Reduces all column vectors by applying a function between two of them, until only a single vector is left. /// public Vector ReduceColumns(Func, Vector, Vector> f) { return EnumerateColumns().Aggregate(f); } /// /// Applies a function to each value pair of two matrices and replaces the value in the result vector. /// public void Map2(Func f, Matrix other, Matrix result, Zeros zeros = Zeros.AllowSkip) { Storage.Map2To(result.Storage, other.Storage, f, zeros, ExistingData.Clear); } /// /// Applies a function to each value pair of two matrices and returns the results as a new vector. /// public Matrix Map2(Func f, Matrix other, Zeros zeros = Zeros.AllowSkip) { var result = Build.SameAs(this); Storage.Map2To(result.Storage, other.Storage, f, zeros, ExistingData.AssumeZeros); return result; } /// /// Applies a function to update the status with each value pair of two matrices and returns the resulting status. /// public TState Fold2(Func f, TState state, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { return Storage.Fold2(other.Storage, f, state, zeros); } /// /// Returns a tuple with the index and value of the first element satisfying a predicate, or null if none is found. /// Zero elements may be skipped on sparse data structures if allowed (default). /// public Tuple Find(Func predicate, Zeros zeros = Zeros.AllowSkip) { return Storage.Find(predicate, zeros); } /// /// Returns a tuple with the index and values of the first element pair of two matrices of the same size satisfying a predicate, or null if none is found. /// Zero elements may be skipped on sparse data structures if allowed (default). /// public Tuple Find2(Func predicate, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { return Storage.Find2(other.Storage, predicate, zeros); } /// /// Returns true if at least one element satisfies a predicate. /// Zero elements may be skipped on sparse data structures if allowed (default). /// public bool Exists(Func predicate, Zeros zeros = Zeros.AllowSkip) { return Storage.Find(predicate, zeros) != null; } /// /// Returns true if at least one element pairs of two matrices of the same size satisfies a predicate. /// Zero elements may be skipped on sparse data structures if allowed (default). /// public bool Exists2(Func predicate, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { return Storage.Find2(other.Storage, predicate, zeros) != null; } /// /// Returns true if all elements satisfy a predicate. /// Zero elements may be skipped on sparse data structures if allowed (default). /// public bool ForAll(Func predicate, Zeros zeros = Zeros.AllowSkip) { return Storage.Find(x => !predicate(x), zeros) == null; } /// /// Returns true if all element pairs of two matrices of the same size satisfy a predicate. /// Zero elements may be skipped on sparse data structures if allowed (default). /// public bool ForAll2(Func predicate, Matrix other, Zeros zeros = Zeros.AllowSkip) where TOther : struct, IEquatable, IFormattable { return Storage.Find2(other.Storage, (x, y) => !predicate(x, y), zeros) == null; } } }