// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // // Copyright (c) 2009-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 MathNet.Numerics.Properties; namespace MathNet.Numerics.LinearAlgebra.Storage { [Serializable] public abstract partial class MatrixStorage : IEquatable> where T : struct, IEquatable, IFormattable { // [ruegg] public fields are OK here protected static readonly T Zero = BuilderInstance.Instance.Zero; public readonly int RowCount; public readonly int ColumnCount; protected MatrixStorage(int rowCount, int columnCount) { if (rowCount <= 0) { throw new ArgumentOutOfRangeException(Resources.MatrixRowsMustBePositive); } if (columnCount <= 0) { throw new ArgumentOutOfRangeException(Resources.MatrixColumnsMustBePositive); } RowCount = rowCount; ColumnCount = columnCount; } /// /// True if the matrix storage format is dense. /// public abstract bool IsDense { get; } /// /// True if all fields of this matrix can be set to any value. /// False if some fields are fixed, like on a diagonal matrix. /// public abstract bool IsFullyMutable { get; } /// /// True if the specified field can be set to any value. /// False if the field is fixed, like an off-diagonal field on a diagonal matrix. /// public abstract bool IsMutableAt(int row, int column); /// /// 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] { get { ValidateRange(row, column); return At(row, column); } set { ValidateRange(row, column); At(row, column, value); } } /// /// Retrieves the requested element without range checking. /// /// /// The row of the element. /// /// /// The column of the element. /// /// /// The requested element. /// /// Not range-checked. public abstract T At(int row, int column); /// /// Sets the element without range checking. /// /// The row of the element. /// The column of the element. /// The value to set the element to. /// WARNING: This method is not thread safe. Use "lock" with it and be sure to avoid deadlocks. public abstract void At(int row, int column, T value); public virtual void Clear() { for (var i = 0; i < RowCount; i++) { for (var j = 0; j < ColumnCount; j++) { At(i, j, Zero); } } } public virtual void Clear(int rowIndex, int rowCount, int columnIndex, int columnCount) { for (var i = rowIndex; i < rowIndex + rowCount; i++) { for (var j = columnIndex; j < columnIndex + columnCount; j++) { At(i, j, Zero); } } } /// /// Indicates whether the current object is equal to another object of the same type. /// /// /// An object to compare with this object. /// /// /// true if the current object is equal to the parameter; otherwise, false. /// public virtual bool Equals(MatrixStorage other) { // Reject equality when the argument is null or has a different shape. if (other == null) { return false; } if (ColumnCount != other.ColumnCount || RowCount != other.RowCount) { return false; } // Accept if the argument is the same object as this. if (ReferenceEquals(this, other)) { return true; } // If all else fails, perform element wise comparison. for (var row = 0; row < RowCount; row++) { for (var column = 0; column < ColumnCount; column++) { if (!At(row, column).Equals(other.At(row, column))) { return false; } } } return true; } /// /// Determines whether the specified is equal to the current . /// /// /// true if the specified is equal to the current ; otherwise, false. /// /// The to compare with the current . public override sealed bool Equals(object obj) { return Equals(obj as MatrixStorage); } /// /// Serves as a hash function for a particular type. /// /// /// A hash code for the current . /// public override int GetHashCode() { var hashNum = Math.Min(RowCount*ColumnCount, 25); int hash = 17; unchecked { for (var i = 0; i < hashNum; i++) { var col = i%ColumnCount; var row = (i - col)/RowCount; hash = hash*31 + At(row, col).GetHashCode(); } } return hash; } // MATRIX COPY public void CopyTo(MatrixStorage target, bool skipClearing = false) { if (target == null) { throw new ArgumentNullException("target"); } if (ReferenceEquals(this, target)) { return; } if (RowCount != target.RowCount || ColumnCount != target.ColumnCount) { var message = string.Format(Resources.ArgumentMatrixDimensions2, RowCount + "x" + ColumnCount, target.RowCount + "x" + target.ColumnCount); throw new ArgumentException(message, "target"); } CopyToUnchecked(target, skipClearing); } internal virtual void CopyToUnchecked(MatrixStorage target, bool skipClearing = false) { for (int j = 0; j < ColumnCount; j++) { for (int i = 0; i < RowCount; i++) { target.At(i, j, At(i, j)); } } } public void CopySubMatrixTo(MatrixStorage target, int sourceRowIndex, int targetRowIndex, int rowCount, int sourceColumnIndex, int targetColumnIndex, int columnCount, bool skipClearing = false) { if (target == null) { throw new ArgumentNullException("target"); } if (ReferenceEquals(this, target)) { throw new NotSupportedException(); } ValidateSubMatrixRange(target, sourceRowIndex, targetRowIndex, rowCount, sourceColumnIndex, targetColumnIndex, columnCount); CopySubMatrixToUnchecked(target, sourceRowIndex, targetRowIndex, rowCount, sourceColumnIndex, targetColumnIndex, columnCount, skipClearing); } internal virtual void CopySubMatrixToUnchecked(MatrixStorage target, int sourceRowIndex, int targetRowIndex, int rowCount, int sourceColumnIndex, int targetColumnIndex, int columnCount, bool skipClearing = false) { for (int j = sourceColumnIndex, jj = targetColumnIndex; j < sourceColumnIndex + columnCount; j++, jj++) { for (int i = sourceRowIndex, ii = targetRowIndex; i < sourceRowIndex + rowCount; i++, ii++) { target.At(ii, jj, At(i, j)); } } } // ROW COPY public void CopyRowTo(VectorStorage target, int rowIndex, bool skipClearing = false) { if (target == null) { throw new ArgumentNullException("target"); } ValidateRowRange(target, rowIndex); CopySubRowToUnchecked(target, rowIndex, 0, 0, ColumnCount, skipClearing); } public void CopySubRowTo(VectorStorage target, int rowIndex, int sourceColumnIndex, int targetColumnIndex, int columnCount, bool skipClearing = false) { if (target == null) { throw new ArgumentNullException("target"); } ValidateSubRowRange(target, rowIndex, sourceColumnIndex, targetColumnIndex, columnCount); CopySubRowToUnchecked(target, rowIndex, sourceColumnIndex, targetColumnIndex, columnCount, skipClearing); } internal virtual void CopySubRowToUnchecked(VectorStorage target, int rowIndex, int sourceColumnIndex, int targetColumnIndex, int columnCount, bool skipClearing = false) { for (int j = sourceColumnIndex, jj = targetColumnIndex; j < sourceColumnIndex + columnCount; j++, jj++) { target.At(jj, At(rowIndex, j)); } } // COLUMN COPY public void CopyColumnTo(VectorStorage target, int columnIndex, bool skipClearing = false) { if (target == null) { throw new ArgumentNullException("target"); } ValidateColumnRange(target, columnIndex); CopySubColumnToUnchecked(target, columnIndex, 0, 0, RowCount, skipClearing); } public void CopySubColumnTo(VectorStorage target, int columnIndex, int sourceRowIndex, int targetRowIndex, int rowCount, bool skipClearing = false) { if (target == null) { throw new ArgumentNullException("target"); } ValidateSubColumnRange(target, columnIndex, sourceRowIndex, targetRowIndex, rowCount); CopySubColumnToUnchecked(target, columnIndex, sourceRowIndex, targetRowIndex, rowCount, skipClearing); } internal virtual void CopySubColumnToUnchecked(VectorStorage target, int columnIndex, int sourceRowIndex, int targetRowIndex, int rowCount, bool skipClearing = false) { for (int i = sourceRowIndex, ii = targetRowIndex; i < sourceRowIndex + rowCount; i++, ii++) { target.At(ii, At(i, columnIndex)); } } // EXTRACT public virtual T[] ToRowMajorArray() { var ret = new T[RowCount * ColumnCount]; for (int i = 0; i < RowCount; i++) { var offset = i * ColumnCount; for (int j = 0; j < ColumnCount; j++) { ret[offset + j] = At(i, j); } } return ret; } public virtual T[] ToColumnMajorArray() { var ret = new T[RowCount * ColumnCount]; for (int j = 0; j < ColumnCount; j++) { var offset = j * RowCount; for (int i = 0; i < RowCount; i++) { ret[offset + i] = At(i, j); } } return ret; } public virtual T[,] ToArray() { var ret = new T[RowCount,ColumnCount]; for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { ret[i, j] = At(i, j); } } return ret; } // ENUMERATION public virtual IEnumerable Enumerate() { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { yield return At(i, j); } } } public virtual IEnumerable> EnumerateIndexed() { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { yield return new Tuple(i, j, At(i, j)); } } } public virtual IEnumerable EnumerateNonZero() { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { var x = At(i, j); if (!Zero.Equals(x)) { yield return x; } } } } public virtual IEnumerable> EnumerateNonZeroIndexed() { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { var x = At(i, j); if (!Zero.Equals(x)) { yield return new Tuple(i, j, x); } } } } // FUNCTIONAL COMBINATORS public virtual void MapInplace(Func f, bool forceMapZeros = false) { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { At(i, j, f(At(i, j))); } } } public virtual void MapIndexedInplace(Func f, bool forceMapZeros = false) { for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColumnCount; j++) { At(i, j, f(i, j, At(i, j))); } } } } }