// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // Copyright (c) 2009-2010 Math.NET // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // namespace MathNet.Numerics.LinearAlgebra.Complex { using System; using System.Linq; using System.Numerics; using Generic; using Properties; using Storage; /// /// A matrix type for diagonal matrices. /// /// /// Diagonal matrices can be non-square matrices but the diagonal always starts /// at element 0,0. A diagonal matrix will throw an exception if non diagonal /// entries are set. The exception to this is when the off diagonal elements are /// 0.0 or NaN; these settings will cause no change to the diagonal matrix. /// [Serializable] public class DiagonalMatrix : Matrix { readonly DiagonalMatrixStorage _storage; /// /// Gets the matrix's data. /// /// The matrix's data. readonly Complex[] _data; /// /// Initializes a new instance of the class. /// public DiagonalMatrix(DiagonalMatrixStorage storage) : base(storage) { _storage = storage; _data = _storage.Data; } /// /// Initializes a new instance of the class. This matrix is square with a given size. /// /// the size of the square matrix. /// /// If is less than one. /// public DiagonalMatrix(int order) : this(new DiagonalMatrixStorage(order, order, Complex.Zero)) { } /// /// Initializes a new instance of the class. /// /// /// The number of rows. /// /// /// The number of columns. /// public DiagonalMatrix(int rows, int columns) : this(new DiagonalMatrixStorage(rows, columns, Complex.Zero)) { } /// /// Initializes a new instance of the class with all diagonal entries set to a particular value. /// /// /// The number of rows. /// /// /// The number of columns. /// /// The value which we assign to each diagonal element of the matrix. public DiagonalMatrix(int rows, int columns, Complex value) : this(rows, columns) { for (var i = 0; i < _data.Length; i++) { _data[i] = value; } } /// /// Initializes a new instance of the class from a one dimensional array with diagonal elements. This constructor /// will reference the one dimensional array and not copy it. /// /// The number of rows. /// The number of columns. /// The one dimensional array which contain diagonal elements. public DiagonalMatrix(int rows, int columns, Complex[] diagonalArray) : this(new DiagonalMatrixStorage(rows, columns, Complex.Zero, diagonalArray)) { } /// /// Initializes a new instance of the class from a 2D array. /// /// The 2D array to create this matrix from. /// When contains an off-diagonal element. /// Depending on the implementation, an /// may be thrown if one of the indices is outside the dimensions of the matrix. public DiagonalMatrix(Complex[,] array) : this(array.GetLength(0), array.GetLength(1)) { for (var i = 0; i < RowCount; i++) { for (var j = 0; j < ColumnCount; j++) { if (i == j) { _data[i] = array[i, j]; } else if (((array[i, j].Real != 0.0) && !double.IsNaN(array[i, j].Real)) || ((array[i, j].Imaginary != 0.0) && !double.IsNaN(array[i, j].Imaginary))) { throw new IndexOutOfRangeException("Cannot set an off-diagonal element in a diagonal matrix."); } } } } /// /// Initializes a new instance of the class, copying /// the values from the given matrix. /// /// The matrix to copy. public DiagonalMatrix(Matrix matrix) : this(matrix.RowCount, matrix.ColumnCount) { matrix.Storage.CopyToUnchecked(Storage, skipClearing: true); } /// /// Creates a DiagonalMatrix for the given number of rows and columns. /// /// The number of rows. /// The number of columns. /// True if all fields must be mutable (e.g. not a diagonal matrix). /// /// A DiagonalMatrix with the given dimensions. /// public override Matrix CreateMatrix(int numberOfRows, int numberOfColumns, bool fullyMutable = false) { return fullyMutable ? (Matrix) new SparseMatrix(numberOfRows, numberOfColumns) : new DiagonalMatrix(numberOfRows, numberOfColumns); } /// /// Creates a with a the given dimension. /// /// The size of the vector. /// True if all fields must be mutable. /// /// A with the given dimension. /// public override Vector CreateVector(int size, bool fullyMutable = false) { return new SparseVector(size); } #region Elementary operations /// /// Adds another matrix to this matrix. /// /// The matrix to add to this matrix. /// The result of the addition. /// If the other matrix is . /// If the two matrices don't have the same dimensions. public override Matrix Add(Matrix other) { if (other == null) { throw new ArgumentNullException("other"); } if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other, "other"); } Matrix result; if (other is DiagonalMatrix) { result = new DiagonalMatrix(RowCount, ColumnCount); } else { result = new DenseMatrix(RowCount, ColumnCount); } Add(other, result); return result; } /// /// Adds another matrix to this matrix. /// /// The matrix to add to this matrix. /// The matrix to store the result of the addition. /// If the other matrix is . /// If the two matrices don't have the same dimensions. public override void Add(Matrix other, Matrix result) { if (other == null) { throw new ArgumentNullException("other"); } if (result == null) { throw new ArgumentNullException("result"); } if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other, "other"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; if (diagOther == null || diagResult == null) { base.Add(other, result); } else { Control.LinearAlgebraProvider.AddArrays(_data, diagOther._data, diagResult._data); } } /// /// Subtracts another matrix from this matrix. /// /// The matrix to subtract. /// The result of the subtraction. /// If the other matrix is . /// If the two matrices don't have the same dimensions. public override Matrix Subtract(Matrix other) { if (other == null) { throw new ArgumentNullException("other"); } if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other, "other"); } Matrix result; if (other is DiagonalMatrix) { result = new DenseMatrix(RowCount, ColumnCount); } else { result = new DiagonalMatrix(RowCount, ColumnCount); } Subtract(other, result); return result; } /// /// Subtracts another matrix from this matrix. /// /// The matrix to subtract. /// The matrix to store the result of the subtraction. /// If the other matrix is . /// If the two matrices don't have the same dimensions. public override void Subtract(Matrix other, Matrix result) { if (other == null) { throw new ArgumentNullException("other"); } if (result == null) { throw new ArgumentNullException("result"); } if (other.RowCount != RowCount || other.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other, "other"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, other, "other"); } var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; if (diagOther == null || diagResult == null) { base.Subtract(other, result); } else { Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); } } /// /// 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 override void SetDiagonal(Complex[] source) { if (source == null) { throw new ArgumentNullException("source"); } if (source.Length != _data.Length) { throw new ArgumentException(Resources.ArgumentArraysSameLength, "source"); } Array.Copy(source, _data, source.Length); } /// /// Copies the values of the given 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 override void SetDiagonal(Vector source) { var denseSource = source as DenseVector; if (denseSource == null) { base.SetDiagonal(source); return; } if (_data.Length != denseSource.Values.Length) { throw new ArgumentException(Resources.ArgumentVectorsSameLength, "source"); } Array.Copy(denseSource.Values, _data, denseSource.Values.Length); } /// /// Multiplies each element of the matrix by a scalar and places results into the result matrix. /// /// The scalar to multiply the matrix with. /// The matrix to store the result of the multiplication. /// If the result matrix is . /// If the result matrix's dimensions are not the same as this matrix. protected override void DoMultiply(Complex scalar, Matrix result) { if (scalar == 0.0) { result.Clear(); return; } if (scalar == 1.0) { CopyTo(result); return; } var diagResult = result as DiagonalMatrix; if (diagResult == null) { base.Multiply(scalar, result); } else { if (!ReferenceEquals(this, result)) { CopyTo(diagResult); } Control.LinearAlgebraProvider.ScaleArray(scalar, _data, diagResult._data); } } /// /// Multiplies this matrix with another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. /// If the other matrix is . /// If the result matrix is . /// If this.Columns != other.Rows. /// If the result matrix's dimensions are not the this.Rows x other.Columns. public override void Multiply(Matrix other, Matrix result) { if (other == null) { throw new ArgumentNullException("other"); } if (result == null) { throw new ArgumentNullException("result"); } if (ColumnCount != other.RowCount) { throw DimensionsDontMatch(this, other); } if (result.RowCount != RowCount || result.ColumnCount != other.ColumnCount) { throw DimensionsDontMatch(this, result); } var m = other as DiagonalMatrix; var r = result as DiagonalMatrix; if (m == null || r == null) { base.Multiply(other, result); } else { var thisDataCopy = new Complex[r._data.Length]; var otherDataCopy = new Complex[r._data.Length]; Array.Copy(_data, thisDataCopy, (r._data.Length > _data.Length) ? _data.Length : r._data.Length); Array.Copy(m._data, otherDataCopy, (r._data.Length > m._data.Length) ? m._data.Length : r._data.Length); Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, r._data); } } /// /// Multiplies this matrix with another matrix and returns the result. /// /// The matrix to multiply with. /// If this.Columns != other.Rows. /// If the other matrix is . /// The result of multiplication. public override Matrix Multiply(Matrix other) { if (other == null) { throw new ArgumentNullException("other"); } if (ColumnCount != other.RowCount) { throw DimensionsDontMatch(this, other); } var result = other.CreateMatrix(RowCount, other.ColumnCount); Multiply(other, result); return result; } /// /// Multiplies this matrix with a vector and places the results into the result matrix. /// /// The vector to multiply with. /// The result of the multiplication. /// If is . /// If is . /// If result.Count != this.RowCount. /// If this.ColumnCount != .Count. public override void Multiply(Vector rightSide, Vector result) { if (rightSide == null) { throw new ArgumentNullException("rightSide"); } if (ColumnCount != rightSide.Count) { throw DimensionsDontMatch(this, rightSide, "rightSide"); } if (result == null) { throw new ArgumentNullException("result"); } if (RowCount != result.Count) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(rightSide, result)) { var tmp = result.CreateVector(result.Count); Multiply(rightSide, tmp); tmp.CopyTo(result); } else { // Clear the result vector result.Clear(); // Multiply the elements in the vector with the corresponding diagonal element in this. for (var r = 0; r < _data.Length; r++) { result[r] = _data[r] * rightSide[r]; } } } /// /// Left multiply a matrix with a vector ( = vector * matrix ) and place the result in the result vector. /// /// The vector to multiply with. /// The result of the multiplication. /// If is . /// If the result matrix is . /// If result.Count != this.ColumnCount. /// If this.RowCount != .Count. public override void LeftMultiply(Vector leftSide, Vector result) { if (leftSide == null) { throw new ArgumentNullException("leftSide"); } if (RowCount != leftSide.Count) { throw DimensionsDontMatch(this, leftSide, "leftSide"); } if (result == null) { throw new ArgumentNullException("result"); } if (ColumnCount != result.Count) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(leftSide, result)) { var tmp = result.CreateVector(result.Count); LeftMultiply(leftSide, tmp); tmp.CopyTo(result); } else { // Clear the result vector result.Clear(); // Multiply the elements in the vector with the corresponding diagonal element in this. for (var r = 0; r < _data.Length; r++) { result[r] = _data[r] * leftSide[r]; } } } /// /// Computes the determinant of this matrix. /// /// The determinant of this matrix. public override Complex Determinant() { if (RowCount != ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } return _data.Aggregate(Complex.One, (current, t) => current * t); } /// /// Returns the elements of the diagonal in a . /// /// 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 override Vector Diagonal() { // TODO: Should we return reference to array? In current implementation we return copy of array, so changes in DenseVector will // not influence onto diagonal elements return new DenseVector((Complex[])_data.Clone()); } /// /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// /// The matrix to multiply with. /// The result of the multiplication. /// If the other matrix is . /// If the result matrix is . /// If this.Columns != other.Rows. /// If the result matrix's dimensions are not the this.Rows x other.Columns. public override void TransposeAndMultiply(Matrix other, Matrix result) { var otherDiagonal = other as DiagonalMatrix; var resultDiagonal = result as DiagonalMatrix; if (otherDiagonal == null || resultDiagonal == null) { base.TransposeAndMultiply(other, result); return; } Multiply(otherDiagonal.Transpose(), result); } /// /// Multiplies this matrix with transpose of another matrix and returns the result. /// /// The matrix to multiply with. /// If this.Columns != other.Rows. /// If the other matrix is . /// The result of multiplication. public override Matrix TransposeAndMultiply(Matrix other) { var otherDiagonal = other as DiagonalMatrix; if (otherDiagonal == null) { return base.TransposeAndMultiply(other); } if (ColumnCount != otherDiagonal.ColumnCount) { throw DimensionsDontMatch(this, otherDiagonal); } var result = other.CreateMatrix(RowCount, other.RowCount); TransposeAndMultiply(other, result); return result; } #endregion /// /// Returns the transpose of this matrix. /// /// The transpose of this matrix. public override Matrix Transpose() { var ret = new DiagonalMatrix(ColumnCount, RowCount); Array.Copy(_data, ret._data, _data.Length); return ret; } /// Calculates the L1 norm. /// The L1 norm of the matrix. public override Complex L1Norm() { return _data.Aggregate(double.NegativeInfinity, (current, t) => Math.Max(current, t.Magnitude)); } /// Calculates the L2 norm. /// The L2 norm of the matrix. public override Complex L2Norm() { return _data.Aggregate(double.NegativeInfinity, (current, t) => Math.Max(current, t.Magnitude)); } /// Calculates the Frobenius norm of this matrix. /// The Frobenius norm of this matrix. public override Complex FrobeniusNorm() { var norm = _data.Sum(t => t.Magnitude * t.Magnitude); return Math.Sqrt(norm); } /// Calculates the infinity norm of this matrix. /// The infinity norm of this matrix. public override Complex InfinityNorm() { return L1Norm(); } /// Calculates the condition number of this matrix. /// The condition number of the matrix. public override Complex ConditionNumber() { var maxSv = double.NegativeInfinity; var minSv = double.PositiveInfinity; foreach (var t in _data) { maxSv = Math.Max(maxSv, t.Magnitude); minSv = Math.Min(minSv, t.Magnitude); } return maxSv / minSv; } /// Computes the inverse of this matrix. /// If is not a square matrix. /// If is singular. /// The inverse of this matrix. public override Matrix Inverse() { if (RowCount != ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } var inverse = (DiagonalMatrix)Clone(); for (var i = 0; i < _data.Length; i++) { if (_data[i] != 0.0) { inverse._data[i] = 1.0 / _data[i]; } else { throw new ArgumentException(Resources.ArgumentMatrixNotSingular); } } return inverse; } /// /// Returns a new matrix containing the lower triangle of this matrix. /// /// The lower triangle of this matrix. public override Matrix LowerTriangle() { return Clone(); } /// /// 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 override void LowerTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } if (ReferenceEquals(this, result)) { return; } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, _data[i]); } } /// /// 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 override Matrix StrictlyLowerTriangle() { return new DiagonalMatrix(RowCount, ColumnCount); } /// /// 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 override void StrictlyLowerTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } result.Clear(); } /// /// Returns a new matrix containing the upper triangle of this matrix. /// /// The upper triangle of this matrix. public override Matrix UpperTriangle() { return Clone(); } /// /// 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 override void UpperTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, _data[i]); } } /// /// 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 override Matrix StrictlyUpperTriangle() { return new DiagonalMatrix(RowCount, ColumnCount); } /// /// 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 override void StrictlyUpperTriangle(Matrix result) { if (result == null) { throw new ArgumentNullException("result"); } if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch(this, result, "result"); } result.Clear(); } /// /// 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 override Matrix SubMatrix(int rowIndex, int rowCount, int columnIndex, int columnCount) { var target = rowIndex == columnIndex ? (Matrix)new DiagonalMatrix(rowCount, columnCount) : new SparseMatrix(rowCount, columnCount); Storage.CopySubMatrixTo(target.Storage, rowIndex, 0, rowCount, columnIndex, 0, columnCount, skipClearing: true); return target; } /// /// Creates a new and inserts the given column at the given index. /// /// The index of where to insert the column. /// The column to insert. /// A new with the inserted column. /// If is . /// If is < zero or > the number of columns. /// If the size of != the number of rows. public override Matrix InsertColumn(int columnIndex, Vector column) { if (column == null) { throw new ArgumentNullException("column"); } if (columnIndex < 0 || columnIndex > ColumnCount) { throw new ArgumentOutOfRangeException("columnIndex"); } if (column.Count != RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "column"); } var result = new SparseMatrix(RowCount, ColumnCount + 1); for (var i = 0; i < columnIndex; i++) { result.SetColumn(i, Column(i)); } result.SetColumn(columnIndex, column); for (var i = columnIndex + 1; i < ColumnCount + 1; i++) { result.SetColumn(i, Column(i - 1)); } return result; } /// /// Creates a new and inserts the given row at the given index. /// /// The index of where to insert the row. /// The row to insert. /// A new with the inserted column. /// If is . /// If is < zero or > the number of rows. /// If the size of != the number of columns. public override Matrix InsertRow(int rowIndex, Vector row) { if (row == null) { throw new ArgumentNullException("row"); } if (rowIndex < 0 || rowIndex > RowCount) { throw new ArgumentOutOfRangeException("rowIndex"); } if (row.Count != ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension, "row"); } var result = new SparseMatrix(RowCount + 1, ColumnCount); for (var i = 0; i < rowIndex; i++) { result.At(i, i, At(i, i)); } result.SetRow(rowIndex, row); for (var i = rowIndex + 1; i < result.RowCount; i++) { result.At(i, i - 1, At(i - 1, i - 1)); } return result; } /// /// Permute the columns of a matrix according to a permutation. /// /// The column permutation to apply to this matrix. /// Always thrown /// Permutation in diagonal matrix are senseless, because of matrix nature public override void PermuteColumns(Permutation p) { throw new InvalidOperationException("Permutations in diagonal matrix are not allowed"); } /// /// Permute the rows of a matrix according to a permutation. /// /// The row permutation to apply to this matrix. /// Always thrown /// Permutation in diagonal matrix are senseless, because of matrix nature public override void PermuteRows(Permutation p) { throw new InvalidOperationException("Permutations in diagonal matrix are not allowed"); } /// /// Gets a value indicating whether this matrix is symmetric. /// public override bool IsSymmetric { get { return true; } } #region Static constructors for special matrices. /// /// Initializes a square with all zero's except for ones on the diagonal. /// /// the size of the square matrix. /// A diagonal identity matrix. /// /// If is less than one. /// public static DiagonalMatrix Identity(int order) { var m = new DiagonalMatrix(order); for (var i = 0; i < order; i++) { m._data[i] = 1.0; } return m; } #endregion } }