// ------------------- INVERSE MATRIX --------------------------- // TO DO: make without minor matrix copy, but with minor matrix itself. Economy of the memory. /// <summary> /// Calculates the inverse matrix using the LUP-factorization for calculating matrix determinants /// and algebraic supplements of its elements. /// It takes O(n^5) operations to run. /// /// Works only for square, non-singular matrices. /// If these requirements are not met, either a MatrixSizeException /// or a MatrixSingularityException shall be thrown. /// </summary> public Matrix <T, C> inverseMatrix_LUP_Factorization() { this.checkSquare(); Numeric <T, C> determinant = this.Determinant_LUP_Factorization(); if (determinant == Numeric <T, C> .Zero) { throw new MatrixSingularityException("cannot calculate the inverse matrix."); } // to do: make matrix type Matrix <T, C> inverse = new Matrix_SDA <T, C>(this.rows, this.columns); for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { Numeric <T, C> minor = this.getMinorMatrix(j, i).Determinant_LUP_Factorization(); if (((i + j) & 1) == 1) { minor = -minor; } inverse.setItemAt(i, j, minor / determinant); } } return(inverse); }
/// <summary> /// Preforms the LUP-factorization of a matrix (let it be A) /// in the form of: /// /// P*A = L*U. /// /// The P is an identity matrix with a plenty of row inversions. /// For the economy of space it is provided as a single-dimensional array of integers: /// /// (0, 1, 2, 3, ..., n). /// /// Element indices of this array stand for matrix rows, and elements value /// mean the position of '1' in a row. /// /// Requirements: works for any square and nonsingular matrix (having a non-zero determinant). /// If these requirements aren't met, either MatrixSingularException of MatrixSizeException /// would be thrown. /// </summary> /// <param name="L">The lower-triangular matrix with '1' on the main diagonal.</param> /// <param name="U">The upper-triangular matrix.</param> /// <param name="P">The identity matrix with a plenty of row inversions in the form of array.</param> public void LUP_Factorization(out int[] P, out Matrix_SDA <T, C> L, out Matrix_SDA <T, C> U) { Matrix_SDA <T, C> C; this.LUP_Factorization(out P, out C); int n = this.rows; L = new Matrix_SDA <T, C>(n, n); U = new Matrix_SDA <T, C>(n, n); Numeric <T, C> one = (Numeric <T, C>) 1; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i < j) { U.setItemAt(i, j, C.getItemAt(i, j)); } else if (i > j) { L.setItemAt(i, j, C.getItemAt(i, j)); } else { L.setItemAt(i, j, one); // здесь единицы на диагонали U.setItemAt(i, j, C.getItemAt(i, j)); // E возместить не надо. } } } return; }
protected override Matrix <T, C> sum(Matrix <T, C> another) { if (this.rows != another.RowCount || this.columns != another.ColumnCount) { throw new MatrixSizeException("Matrices must be of the same size in order to sum."); } // If the matrix is SDA, we can do it quick'n'lucky. if (this.GetType().IsInstanceOfType(another)) { Matrix_SDA <T, C> temp = (Matrix_SDA <T, C>)another; Matrix_SDA <T, C> newMatrix = new Matrix_SDA <T, C>(this.rows, this.columns); for (int i = 0; i < this.ElementCount; i++) { newMatrix.matrixArray[i] = this.matrixArray[i] + temp.matrixArray[i]; } return(newMatrix); } // Here comes the bad case else { Matrix_SDA <T, C> newMatrix = new Matrix_SDA <T, C>(this.rows, this.columns); for (int i = 0; i < this.ElementCount; i++) { newMatrix.matrixArray[i] = this.matrixArray[i] + another.getItemAt(i / columns, i % columns); } return(newMatrix); } }
/// <summary> /// Provides a deep clone of the current matrix. /// </summary> /// <returns>The cloned matrix.</returns> public override object Clone() { Matrix_SDA <T, C> temp = new Matrix_SDA <T, C>(); temp.matrixArray = (Numeric <T, C>[]) this.matrixArray.Clone(); temp.rows = this.rows; temp.columns = this.columns; return(temp); }
/// <summary> /// Provides a deep clone of the current matrix. /// </summary> /// <returns>The cloned matrix.</returns> public override object Clone() { Matrix_SDA temp = new Matrix_SDA(); temp.matrixArray = (double[])this.matrixArray.Clone(); temp.rows = this.rows; temp.columns = this.columns; return(temp); }
protected override Matrix <double> multiply(Matrix <double> another) { if (this.columns != another.RowCount) { throw new ArgumentException("The column count of the first matrix and the row count of the second matrix must match."); } Matrix_SDA temp = new Matrix_SDA(this.rows, another.ColumnCount); MatrixHelper.multiplySimple(this, another, temp); return(temp); }
// ------------------ OPERATORS /// <summary> /// Negates the matrix so that all the elements change their sign to the opposite. /// </summary> /// <returns></returns> protected override Matrix <T, C> negate() { Matrix_SDA <T, C> temp = new Matrix_SDA <T, C>(rows, columns); temp.matrixArray = (Numeric <T, C>[]) this.matrixArray.Clone(); for (int i = 0; i < this.ElementCount; i++) { temp.matrixArray[i] = -temp.matrixArray[i]; } return(temp); }
/// <summary> /// Negates the matrix so that all the elements change their sign to the opposite. /// </summary> /// <returns></returns> protected override Matrix <double> negate() { Matrix_SDA temp = new Matrix_SDA(rows, columns); temp.matrixArray = (double[])this.matrixArray.Clone(); for (int i = 0; i < this.ElementCount; i++) { temp.matrixArray[i] *= -1; } return(temp); }
/// <summary> /// Preforms the LUP-factorization of a matrix (let it be A) /// in the form of: /// /// P*A = L*U. /// /// The P is an identity matrix with a plenty of row inversions. /// /// Requirements: works for any square and nonsingular matrix (having a non-zero determinant). /// If these requirements aren't met, either MatrixSingularException of MatrixSizeException /// would be thrown. /// </summary> /// <param name="L">The lower-triangular matrix with '1' on the main diagonal.</param> /// <param name="U">The upper-triangular matrix.</param> /// <param name="P">The identity matrix with a plenty of row inversions.</param> public void LUP_Factorization(out Matrix_SDA <T, C> P, out Matrix_SDA <T, C> L, out Matrix_SDA <T, C> U) { int[] arr; int n = this.rows; Numeric <T, C> one = (Numeric <T, C>) 1; LUP_Factorization(out arr, out L, out U); P = new Matrix_SDA <T, C>(n, n); for (int i = 0; i < n; i++) { P.setItemAt(i, arr[i], one); } return; }
// --------------------------------------------- // MATRIX ARITHMETIC // --------------------------------------------- /// <summary> /// Performs the matrix multiplication using the quick Strassen algorithm. /// Consumes more memory than simple iterational method, but much quicker /// on bigger dimensions (128+). /// /// If the dimension of method parameters passed is N, then maximum additional object memory consumption is /// <value>9N + [if N>64: (log2(N)-6)*f] + o(N)</value> /// Where f == (nearly) 4.5*(new Matrix of 2N*2N memory consumption). /// </summary> /// <param name="A">The matrix to multiply.</param> /// <param name="B">The matrix to multiply by.</param> /// <param name="result">The resulting matrix of [A.Rows x B.Columns] size.</param> public static void multiplyStrassen(Matrix <T, C> A, Matrix <T, C> B, Matrix <T, C> result) { int exp = 1; do { exp *= 2; } while (A.ColumnCount > exp || A.RowCount > exp || B.ColumnCount > exp || B.RowCount > exp); Matrix_SDA <T, C> Anew = new Matrix_SDA <T, C>(exp, exp); Matrix_SDA <T, C> Bnew = new Matrix_SDA <T, C>(exp, exp); Matrix_SDA <T, C> ResNew = new Matrix_SDA <T, C>(exp, exp); Anew.layMatrixAt(A, 0, 0); Bnew.layMatrixAt(B, 0, 0); strassenSkeleton(Anew, Bnew, ResNew, exp); result.layMatrixAt(ResNew.getSubMatrixCopyAt(0, 0, result.RowCount, result.ColumnCount), 0, 0); }
/// <summary> /// Overloaded. Gets the submatrix at the specified point and with specified size. /// </summary> /// <param name="i">Row index of the upper-left corner element</param> /// <param name="j">Columnt index of the upper-left corner element</param> /// <param name="rows">Row count of the submatrix</param> /// <param name="columns">Column count of the submatrix</param> /// <returns>The submatrix of specified size</returns> public override Matrix <double> getSubMatrixCopyAt(int i, int j, int rows, int columns) { checkPositive(rows, columns); checkBounds(i + rows, j + columns); double[] ma = new double[rows * columns]; int z = 0; for (int k = i; k < i + rows; k++) { Array.Copy(this.matrixArray, this.columns * k + j, ma, z++ *columns, columns); } Matrix_SDA temp = new Matrix_SDA(); temp.matrixArray = ma; temp.rows = rows; temp.columns = columns; return(temp); }
/// <summary> /// Overloaded. Gets the stand-alone submatrix copy at the specified point and with specified size. /// </summary> /// <param name="i">Row index of the upper-left corner element.</param> /// <param name="j">Columnt index of the upper-left corner element.</param> /// <param name="rows">Row count of the submatrix.</param> /// <param name="columns">Column count of the submatrix.</param> /// <returns>The submatrix of specified size.</returns> public virtual Matrix <T, C> getSubMatrixCopyAt(int i, int j, int rows, int columns) { checkPositive(rows, columns); checkBounds(i + rows, j + columns); Numeric <T, C>[] ma = new Numeric <T, C> [rows * columns]; for (int k = i; k < i + rows; k++) { for (int m = j; m < j + rows; m++) { ma[k * rows + m] = calc.getCopy(this.getItemAt(k, m)); } } Matrix_SDA <T, C> temp = new Matrix_SDA <T, C>(); temp.matrixArray = ma; temp.rows = rows; temp.columns = columns; return(temp); }
// ------------------- FACTORIZATION ---------------------------- /// <summary> /// Preforms the LUP-factorization of a matrix (let it be A) /// in the form of: /// /// P*A = L*U. /// /// The P is an identity matrix with a plenty of row inversions. /// For the economy of space it is provided as a single-dimensional array of integers: /// /// (0, 1, 2, 3, ..., n). /// /// Element indices of this array stand for matrix rows, and elements value /// mean the position of '1' in a row. /// /// Requirements: works for any square and nonsingular matrix (having a non-zero determinant). /// If these requirements aren't met, either a MatrixSingularException or a MatrixSizeException /// would be thrown. /// </summary> /// <param name="C">The matrix C containing L + U - E. It is clear that both L and U can be easily extracted from this matrix.</param> /// <param name="P">The identity matrix with a plenty of row inversions in the form of array.</param> public void LUP_Factorization(out int[] P, out Matrix_SDA <T, C> C) { if (this.rows != this.columns) { throw new MatrixSizeException("The matrix is not square an thus cannot be factorized."); } int n = this.rows; // размер матрицы C = new Matrix_SDA <T, C>(n, n); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { C.setItemAt(i, j, this.getItemAt(i, j)); } } P = new int[n]; P.FillByAssign(delegate(int i) { return(i); }); // ----- пошел ---------- Numeric <T, C> pivot; Numeric <T, C> abs; int pivotIndex; for (int i = 0; i < n; i++) { pivot = Numeric <T, C> .Zero; pivotIndex = -1; for (int row = i; row < n; row++) { abs = WhiteMath <T, C> .Abs(this.getItemAt(row, i)); if (abs > pivot) { pivot = abs; pivotIndex = row; } } if (pivot == Numeric <T, C> .Zero) { throw new MatrixSingularityException("The matrix is singular. It cannot be factorized."); } if (pivotIndex != i) { P.Swap(pivotIndex, i); C.swapRows(pivotIndex, i); } try { for (int j = i + 1; j < n; j++) { C.setItemAt(j, i, C.getItemAt(j, i) / C.getItemAt(i, i)); if (Numeric <T, C> .isInfinity(C.getItemAt(j, i)) || Numeric <T, C> .isNaN(C.getItemAt(j, i))) { throw new DivideByZeroException(); } for (int k = i + 1; k < n; k++) { C.setItemAt(j, k, C.getItemAt(j, k) - C.getItemAt(j, i) * C.getItemAt(i, k)); } } } catch (DivideByZeroException) { throw new MatrixSingularityException("The matrix is singular. It cannot be factorized."); } } return; }
/// <summary> /// Overloaded. Gets the submatrix at the specified point and with specified size. /// </summary> /// <param name="i">Row index of the upper-left corner element</param> /// <param name="j">Columnt index of the upper-left corner element</param> /// <param name="rows">Row count of the submatrix</param> /// <param name="columns">Column count of the submatrix</param> /// <returns>The submatrix of specified size</returns> public override Matrix<double> getSubMatrixCopyAt(int i, int j, int rows, int columns) { checkPositive(rows, columns); checkBounds(i + rows, j + columns); double[] ma = new double[rows * columns]; int z=0; for (int k = i; k < i + rows; k++) { Array.Copy(this.matrixArray, this.columns*k + j, ma, z++*columns, columns); } Matrix_SDA temp = new Matrix_SDA(); temp.matrixArray = ma; temp.rows = rows; temp.columns = columns; return temp; }
/// <summary> /// Private recursive Strassen multiplying method skeleton called implicitly by the wrapper method. /// </summary> /// <param name="A">The matrix to multiply</param> /// <param name="B">The matrix to multiply by</param> /// <param name="result">The resulting matrix</param> /// <param name="curDim">Current matrix dimension</param> private static void strassenSkeleton(Matrix <T, C> A, Matrix <T, C> B, Matrix <T, C> result, int curDim) { if (curDim <= 64) { multiplySimple(A, B, result); return; } int size = curDim / 2; Matrix <T, C> A11 = A.getSubMatrixAt(0, 0, size, size); Matrix <T, C> A12 = A.getSubMatrixAt(0, size, size, size); Matrix <T, C> A21 = A.getSubMatrixAt(size, 0, size, size); Matrix <T, C> A22 = A.getSubMatrixAt(size, size, size, size); Matrix <T, C> B11 = B.getSubMatrixAt(0, 0, size, size); Matrix <T, C> B12 = B.getSubMatrixAt(0, size, size, size); Matrix <T, C> B21 = B.getSubMatrixAt(size, 0, size, size); Matrix <T, C> B22 = B.getSubMatrixAt(size, size, size, size); Matrix <T, C> P1 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> P2 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> P3 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> P4 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> P5 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> P6 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> P7 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> temp1 = new Matrix_SDA <T, C>(size, size); Matrix <T, C> temp2 = new Matrix_SDA <T, C>(size, size); sum(A11, A22, temp1); sum(B11, B22, temp2); strassenSkeleton(temp1, temp2, P1, size); sum(A21, A22, temp1); strassenSkeleton(temp1, B11, P2, size); dif(B12, B22, temp1); strassenSkeleton(A11, temp1, P3, size); dif(B21, B11, temp1); strassenSkeleton(A22, temp1, P4, size); sum(A11, A12, temp1); strassenSkeleton(temp1, B22, P5, size); dif(A21, A11, temp1); sum(B11, B12, temp2); strassenSkeleton(temp1, temp2, P6, size); dif(A12, A22, temp1); sum(B21, B22, temp2); strassenSkeleton(temp1, temp2, P7, size); sum(P1, P4, temp1); dif(temp1, P5, temp1); sum(temp1, P7, temp1); result.layMatrixAt(temp1, 0, 0); sum(P3, P5, temp1); result.layMatrixAt(temp1, 0, size); sum(P2, P4, temp1); result.layMatrixAt(temp1, size, 0); dif(P1, P2, temp1); sum(temp1, P3, temp1); sum(temp1, P6, temp1); result.layMatrixAt(temp1, size, size); }
/// <summary> /// Provides a deep clone of the current matrix. /// </summary> /// <returns>The cloned matrix.</returns> public override object Clone() { Matrix_SDA temp = new Matrix_SDA(); temp.matrixArray = (double[])this.matrixArray.Clone(); temp.rows = this.rows; temp.columns = this.columns; return temp; }
protected override Matrix<double> sum(Matrix<double> another) { if (this.rows != another.RowCount || this.columns != another.ColumnCount) throw new ArgumentException("Matrices must be of the same size in order to sum."); // If the matrix is SDA, we can do it quick'n'lucky. if (this.GetType().IsInstanceOfType(another)) { Matrix_SDA temp = (Matrix_SDA)another; Matrix_SDA newMatrix = new Matrix_SDA(this.rows, this.columns); for (int i = 0; i < this.ElementCount; i++) newMatrix.matrixArray[i] = this.matrixArray[i] + temp.matrixArray[i]; return newMatrix; } // Here comes the bad case else { Matrix_SDA newMatrix = new Matrix_SDA(this.rows, this.columns); for (int i = 0; i < this.ElementCount; i++) newMatrix.matrixArray[i] = this.matrixArray[i] + another.getItemAt(i / columns, i % columns); return newMatrix; } }
protected override Matrix<double> multiply(Matrix<double> another) { if (this.columns != another.RowCount) throw new ArgumentException("The column count of the first matrix and the row count of the second matrix must match."); Matrix_SDA temp = new Matrix_SDA(this.rows, another.ColumnCount); MatrixHelper.multiplySimple(this, another, temp); return temp; }
/// <summary> /// Negates the matrix so that all the elements change their sign to the opposite. /// </summary> /// <returns></returns> protected override Matrix<double> negate() { Matrix_SDA temp = new Matrix_SDA(rows, columns); temp.matrixArray = (double[])this.matrixArray.Clone(); for (int i = 0; i < this.ElementCount; i++) temp.matrixArray[i] *= -1; return temp; }
/// <summary> /// This algorithm uses the LU-Factorization of coefficient matrix in order /// to calculate the solution of the equation system. /// /// As LU-Factorization is not guaranteed to exist for every /// non-singular matrix, this method may sometimes fail. /// </summary> /// <param name="coefficients">A square matrix of unknown terms' coefficients.</param> /// <param name="freeTerm">A vector of free terms.</param> /// <param name="x">The vector containing the solution of the equation system.</param> public static void LU_FactorizationSolving <T, C>(Matrix <T, C> coefficients, Vector <T, C> freeTerm, out Vector <T, C> x) where C : ICalc <T>, new() { if (coefficients.RowCount != coefficients.ColumnCount) { throw new ArgumentException("Only square matrices are supported."); } int dim = coefficients.RowCount; Matrix_SDA <T, C> K = new Matrix_SDA <T, C>(dim, dim); Matrix_SDA <T, C> M = new Matrix_SDA <T, C>(dim, dim); // Заполняем единичную диагональ матрицы K. for (int i = 0; i < dim; i++) { for (int j = 0; j < dim; j++) { K[i, j] = M[i, j] = Numeric <T, C> .Zero; } M[i, i] = (Numeric <T, C>) 1; K[i, 0] = coefficients[i, 0]; M[0, i] = coefficients[0, i] / K[0, 0]; } // вектор промежуточных решений Vector <T, C> y = new Vector <T, C>(dim); y[0] = freeTerm[0] / K[0, 0]; Numeric <T, C> s; // вспомогательная for (int i = 1; i < dim; i++) { int j; for (j = 0; j < dim; j++) { s = (Numeric <T, C>) 0; for (int z = 0; z <= j - 1; z++) { s += K[i, z] * M[z, j]; } K[i, j] = coefficients[i, j] - s; } for (j = i; j < dim; j++) { Numeric <T, C> s1 = (Numeric <T, C>) 0; Numeric <T, C> s2 = (Numeric <T, C>) 0; for (int z = 0; z <= i - 1; z++) { s1 += K[i, z] * M[z, j]; s2 += K[i, z] * y[z]; } M[i, j] = (coefficients[i, j] - s1) / K[i, i]; y[i] = (freeTerm[i] - s2) / K[i, i]; } } x = new Vector <T, C>(dim); x[dim - 1] = y[dim - 1]; for (int i = dim - 2; i >= 0; i--) { s = (Numeric <T, C>) 0; for (int z = i + 1; z < dim; z++) { s += M[i, z] * x[z]; } x[i] = y[i] - s; } return; }