/// <summary> /// Performs forward insertion for regular lower triangular matrix /// and right side b, such that the solution is saved right within b. /// The matrix is not changed. /// </summary> /// <param name="b">Vector of height n, if matrix is n by n.</param> public void ForwardInsertion(ArrayMatrix b) { if (!IsLowerTriangular()) throw new InvalidOperationException("Cannot perform forward insertion for matrix not being lower triangular."); if ( /*this.Determinant*/DiagProd() == 0) throw new InvalidOperationException("Warning: Matrix is nearly singular."); var n = RowCount; if (b.VectorLength() != n) throw new ArgumentException("Parameter must vector of the same height as matrix."); for (var j = 1; j <= n - 1; j++) { b[j] /= this[j, j]; for (var i = 1; i <= n - j; i++) b[j + i] -= b[j]*this[j + i, j]; } b[n] /= this[n, n]; }
public static ArrayMatrix HorizontalConcat(ArrayMatrix[] A) { if (A == null) throw new ArgumentNullException(); else if (A.Length == 1) return A[0]; else { var C = HorizontalConcat(A[0], A[1]); for (var i = 2; i < A.Length; i++) C = HorizontalConcat(C, A[i]); return C; } }
/// <summary> /// Creates n by n matrix filled with random values in [0,1]. /// </summary> /// <param name="m"></param> /// <param name="n"></param> /// <returns></returns> public static ArrayMatrix Random(int n) { var M = new ArrayMatrix(n); var r = new Random(); for (var i = 1; i <= n; i++) for (var j = 1; j <= n; j++) M[i, j] = new Complex(r.NextDouble()); return M; }
/// <summary> /// Implements the dot product of two vectors. /// </summary> /// <param name="v">Row or column vector.</param> /// <param name="w">Row or column vector.</param> /// <returns>Dot product.</returns> public static Complex Dot(ArrayMatrix v, ArrayMatrix w) { var m = v.VectorLength(); var n = w.VectorLength(); if (m == 0 || n == 0) throw new ArgumentException("Arguments need to be vectors."); else if (m != n) throw new ArgumentException("Vectors must be of the same length."); var buf = Complex.Zero; for (var i = 1; i <= m; i++) buf += v[i]*w[i]; return buf; }
/// <summary> /// Returns the shortest path between two given vertices i and j as /// int array. /// </summary> /// <param name="P">Path matrix as returned from Floyd().</param> /// <param name="i">One-based index of start vertex.</param> /// <param name="j">One-based index of end vertex.</param> /// <returns></returns> public static ArrayList FloydPath(ArrayMatrix P, int i, int j) { if (!P.IsSquare()) throw new ArgumentException("Path matrix must be square."); else if (!P.IsReal()) throw new ArgumentException("Adjacence matrices are expected to be real."); var path = new ArrayList(); path.Add(i); //int borderliner = 0; //int n = P.Size()[0] + 1; // shortest path cannot have more than n vertices! while (P[i, j] != 0) { i = Convert.ToInt32(P[i, j]); path.Add(i); //borderliner++; //if (borderliner == n) // throw new FormatException("P was not a Floyd path matrix."); } path.Add(j); return path; }
/// <summary> /// Creates m by n chessboard matrix with interchangíng ones and zeros. /// /// </summary> /// <param name="n">Number of columns.</param> /// <param name="even">Indicates, if matrix entry (1,1) equals zero.</param> /// <returns></returns> public static ArrayMatrix ChessboardMatrix(int n, bool even) { var M = new ArrayMatrix(n); if (even) for (var i = 1; i <= n; i++) for (var j = 1; j <= n; j++) M[i, j] = KroneckerDelta((i + j)%2, 0); else for (var i = 1; i <= n; i++) for (var j = 1; j <= n; j++) M[i, j] = KroneckerDelta((i + j)%2, 1); return M; }
/// <summary> /// Generates diagonal matrix /// </summary> /// <param name="diag_vector">column vector containing the diag elements</param> /// <returns></returns> public static ArrayMatrix Diag(ArrayMatrix diag_vector) { var dim = diag_vector.VectorLength(); if (dim == 0) throw new ArgumentException("diag_vector must be 1xN or Nx1"); var M = new ArrayMatrix(dim, dim); for (var i = 1; i <= dim; i++) M[i, i] = diag_vector[i]; return M; }
/// <summary> /// Executes the QR iteration. /// </summary> /// <param name="max_iterations"></param> /// <returns></returns> public ArrayMatrix QRIterationBasic(int max_iterations) { if (!IsReal()) throw new InvalidOperationException("Basic QR iteration is possible only for real matrices."); var T = Clone(); var QR = new ArrayMatrix[2]; for (var i = 0; i < max_iterations; i++) { QR = T.QRGramSchmidt(); T = QR[1]*QR[0]; } return T; }
/// <summary> /// Returns the matrix of the real parts of the entries of this matrix. /// </summary> /// <returns></returns> public ArrayMatrix Re() { var M = new ArrayMatrix(RowCount, ColumnCount); for (var i = 1; i <= RowCount; i++) for (var j = 1; j <= ColumnCount; j++) M[i, j] = new Complex(this[i, j].Real); return M; }
/// <summary> /// Inserts row at specified index. /// </summary> /// <param name="row">Vector to insert</param> /// <param name="i">One-based index at which to insert</param> public void InsertRow(ArrayMatrix row, int i) { var size = row.VectorLength(); if (size == 0) throw new InvalidOperationException("Row must be a vector of length > 0."); if (i <= 0) throw new ArgumentException("Row index must be positive."); if (i > RowCount) this[i, size] = Complex.Zero; else if (size > ColumnCount) { this[i, size] = Complex.Zero; RowCount++; } else RowCount++; Values.Insert(--i, new ArrayList(size)); //Debug.WriteLine(Values.Count.ToString()); for (var k = 1; k <= size; k++) ((ArrayList) Values[i]).Add(row[k]); // fill w/ zeros if vector row is too short for (var k = size; k < ColumnCount; k++) ((ArrayList) Values[i]).Add(Complex.Zero); }
/// <summary> /// Gram-Schmidtian orthogonalization of an m by n matrix A, such that /// {Q, R} is returned, where A = QR, Q is m by n and orthogonal, R is /// n by n and upper triangular matrix. /// </summary> /// <returns></returns> public ArrayMatrix[] QRGramSchmidt() { var m = RowCount; var n = ColumnCount; var A = Clone(); var Q = new ArrayMatrix(m, n); var R = new ArrayMatrix(n, n); // the first column of Q equals the first column of this matrix for (var i = 1; i <= m; i++) Q[i, 1] = A[i, 1]; R[1, 1] = Complex.One; for (var k = 1; k <= n; k++) { R[k, k] = new Complex(A.Column(k).Norm()); for (var i = 1; i <= m; i++) Q[i, k] = A[i, k]/R[k, k]; for (var j = k + 1; j <= n; j++) { R[k, j] = Dot(Q.Column(k), A.Column(j)); for (var i = 1; i <= m; i++) A[i, j] = A[i, j] - Q[i, k]*R[k, j]; } } return new ArrayMatrix[] {Q, R}; }
/// <summary> /// Inserts column at specified index. /// </summary> /// <param name="col">Vector to insert</param> /// <param name="j">One-based index at which to insert</param> public void InsertColumn(ArrayMatrix col, int j) { var size = col.VectorLength(); if (size == 0) throw new InvalidOperationException("Row must be a vector of length > 0."); if (j <= 0) throw new ArgumentException("Row index must be positive."); if (j > ColumnCount) this[size, j] = Complex.Zero; else ColumnCount++; if (size > RowCount) this[size, j] = Complex.Zero; j--; for (var k = 0; k < size; k++) ((ArrayList) Values[k]).Insert(j, col[k + 1]); // fill w/ zeros if vector col too short for (var k = size; k < RowCount; k++) ((ArrayList) Values[k]).Insert(j, 0); }
/// <summary> /// Inserts a sub matrix M at row i and column j. /// </summary> /// <param name="i">One-based row number to insert.</param> /// <param name="j">One-based column number to insert.</param> /// <param name="M">Sub matrix to insert.</param> public void Insert(int i, int j, ArrayMatrix M) { for (var m = 1; m <= M.RowCount; m++) for (var n = 1; n <= M.ColumnCount; n++) this[i + m - 1, j + n - 1] = M[m, n]; }
/// <summary> /// Performs Hessenberg-Householder reduction, where {H, Q} /// is returned, with H Hessenbergian, Q orthogonal and H = Q'AQ. /// </summary> /// <returns></returns> public ArrayMatrix[] HessenbergHouseholder() { //throw new NotImplementedException("Still buggy!"); if (!IsSquare()) throw new InvalidOperationException( "Cannot perform Hessenberg Householder decomposition of non-square matrix."); var n = RowCount; var Q = Identity(n); var H = Clone(); ArrayMatrix I, N, R, P; var vbeta = new ArrayMatrix[2]; int m; // don't try to understand from the code alone. // this is pure magic to me - mathematics, reborn as code. for (var k = 1; k <= n - 2; k++) { vbeta = HouseholderVector(H.Extract(k + 1, n, k, k)); I = Identity(k); N = Zeros(k, n - k); m = vbeta[0].VectorLength(); R = Identity(m) - vbeta[1][1, 1]*vbeta[0]*vbeta[0].Transpose(); H.Insert(k + 1, k, R*H.Extract(k + 1, n, k, n)); H.Insert(1, k + 1, H.Extract(1, n, k + 1, n)*R); P = BlockMatrix(I, N, N.Transpose(), R); Q = Q*P; } return new ArrayMatrix[] {H, Q}; }
public static ArrayMatrix operator *(Complex x, ArrayMatrix A) { var B = new ArrayMatrix(A.RowCount, A.ColumnCount); for (var i = 1; i <= A.RowCount; i++) for (var j = 1; j <= A.ColumnCount; j++) B[i, j] = A[i, j]*x; return B; }
/// <summary> /// Retrieves row with one-based index i. /// </summary> /// <param name="i"></param> /// <returns>i-th row...</returns> public ArrayMatrix Row(int i) { if (i <= 0 || i > RowCount) throw new ArgumentException("Index exceed matrix dimension."); //return (new Matrix((Complex[])((ArrayList)Values[i - 1]).ToArray(typeof(Complex)))).Transpose(); var buf = new ArrayMatrix(ColumnCount, 1); for (var j = 1; j <= ColumnCount; j++) buf[j] = this[i, j]; return buf; }
/// <summary> /// Constructs block matrix [A, B; C, D]. /// </summary> /// <param name="A">Upper left sub matrix.</param> /// <param name="B">Upper right sub matrix.</param> /// <param name="C">Lower left sub matrix.</param> /// <param name="D">Lower right sub matrix.</param> /// <returns></returns> public static ArrayMatrix BlockMatrix(ArrayMatrix A, ArrayMatrix B, ArrayMatrix C, ArrayMatrix D) { if (A.RowCount != B.RowCount || C.RowCount != D.RowCount || A.ColumnCount != C.ColumnCount || B.ColumnCount != D.ColumnCount) throw new ArgumentException("Matrix dimensions must agree."); var R = new ArrayMatrix(A.RowCount + C.RowCount, A.ColumnCount + B.ColumnCount); for (var i = 1; i <= R.RowCount; i++) for (var j = 1; j <= R.ColumnCount; j++) if (i <= A.RowCount) if (j <= A.ColumnCount) R[i, j] = A[i, j]; else R[i, j] = B[i, j - A.ColumnCount]; else if (j <= C.ColumnCount) R[i, j] = C[i - A.RowCount, j]; else R[i, j] = D[i - A.RowCount, j - C.ColumnCount]; return R; }
/// <summary> /// Splits matrix into its row vectors. /// </summary> /// <returns>Array of row vectors.</returns> public ArrayMatrix[] RowVectorize() { var buf = new ArrayMatrix[RowCount]; for (var i = 1; i <= buf.Length; i++) buf[i] = Row(i); return buf; }
/// <summary> /// Performs depth-first search for a graph given by its adjacence matrix. /// </summary> /// <param name="adjacence_matrix">A[i,j] = 0 or +infty, if there is no edge from i to j; any non-zero value otherwise.</param> /// <param name="root">The vertex to begin the search.</param> /// <returns>Adjacence matrix of the computed spanning tree.</returns> public static ArrayMatrix DFS(ArrayMatrix adjacence_matrix, int root) { if (!adjacence_matrix.IsSquare()) throw new ArgumentException("Adjacence matrices are expected to be square."); else if (!adjacence_matrix.IsReal()) throw new ArgumentException("Adjacence matrices are expected to be real."); var n = adjacence_matrix.RowCount; if (root < 1 || root > n) throw new ArgumentException("Root must be a vertex of the graph, e.i. in {1, ..., n}."); var spanTree = new ArrayMatrix(n); var marked = new bool[n + 1]; var todo = new Stack(); todo.Push(root); marked[root] = true; // adajacence lists for each vertex var A = new ArrayList[n + 1]; for (var i = 1; i <= n; i++) { A[i] = new ArrayList(); for (var j = 1; j <= n; j++) if (adjacence_matrix[i, j].Real != 0 && adjacence_matrix[i, j].Imag != double.PositiveInfinity) A[i].Add(j); } int v, w; while (todo.Count > 0) { v = (int) todo.Peek(); if (A[v].Count > 0) { w = (int) A[v][0]; if (!marked[w]) { marked[w] = true; // mark w spanTree[v, w].Real = 1; // mark vw todo.Push(w); // one more to search } A[v].RemoveAt(0); } else todo.Pop(); } return spanTree; }
/// <summary> /// Solves equation this*x = b via conjugate gradient method. /// </summary> /// <param name="b"></param> /// <returns></returns> public ArrayMatrix SolveCG(ArrayMatrix b) { //throw new NotImplementedException("Still buggy!"); if (!IsSymmetricPositiveDefinite()) throw new InvalidOperationException("CG method only works for spd matrices."); else if (!IsReal()) throw new InvalidOperationException("CG method only works for real matrices."); var n = RowCount; var max_iterations = 150; var tolerance = 1e-6; var x = Ones(n, 1); // x will contain the solution var r = b - this*x; // residual approaches zero as x converges to the solution var d = r; // dir = direction of descence var delta = r.Norm(); // delta denotes the current error delta *= delta; tolerance *= tolerance; var h = Zeros(n, 1); double alpha, gamma; double old_delta; if (delta <= tolerance) return x; else { for (var i = 0; i < max_iterations; i++) { h = this*d; gamma = Dot(h, d).Real; if (Math.Abs(gamma) <= tolerance) return x; alpha = delta/gamma; x += alpha*d; // compute new approximation of solution r -= alpha*h; // compute new residual old_delta = delta; // buffer delta delta = r.Norm(); delta *= delta; if (delta <= tolerance) return x; d = r + delta/old_delta*d; // compute new direction of descence } return x; } }
/// <summary> /// Generates diagonal matrix /// </summary> /// <param name="diag_vector">column vector containing the diag elements</param> /// <returns></returns> public static ArrayMatrix Diag(ArrayMatrix diag_vector, int offset) { var dim = diag_vector.VectorLength(); if (dim == 0) throw new ArgumentException("diag_vector must be 1xN or Nx1."); //if (Math.Abs(offset) >= dim) // throw new ArgumentException("Absolute value of offset must be less than length of diag_vector."); var M = new ArrayMatrix(dim + Math.Abs(offset), dim + Math.Abs(offset)); dim = M.RowCount; if (offset >= 0) for (var i = 1; i <= dim - offset; i++) M[i, i + offset] = diag_vector[i]; else for (var i = 1; i <= dim + offset; i++) M[i - offset, i] = diag_vector[i]; return M; }
/// <summary> /// Swaps each matrix entry A[i, j] with A[j, i]. /// </summary> /// <returns>A transposed matrix.</returns> public ArrayMatrix Transpose() { var M = new ArrayMatrix(ColumnCount, RowCount); for (var i = 1; i <= ColumnCount; i++) for (var j = 1; j <= RowCount; j++) M[i, j] = this[j, i]; return M; }
/// <summary> /// Computes all shortest distance between any vertices in a given graph. /// </summary> /// <param name="adjacence_matrix">Square adjacence matrix. The main diagonal /// is expected to consist of zeros, any non-existing edges should be marked /// positive infinity.</param> /// <returns>Two matrices D and P, where D[u,v] holds the distance of the shortest /// path between u and v, and P[u,v] holds the shortcut vertex on the way from /// u to v.</returns> public static ArrayMatrix[] Floyd(ArrayMatrix adjacence_matrix) { if (!adjacence_matrix.IsSquare()) throw new ArgumentException("Expected square matrix."); else if (!adjacence_matrix.IsReal()) throw new ArgumentException("Adjacence matrices are expected to be real."); var n = adjacence_matrix.RowCount; var D = adjacence_matrix.Clone(); // distance matrix var P = new ArrayMatrix(n); double buf; for (var k = 1; k <= n; k++) for (var i = 1; i <= n; i++) for (var j = 1; j <= n; j++) { buf = D[i, k].Real + D[k, j].Real; if (buf < D[i, j].Real) { D[i, j].Real = buf; P[i, j].Real = k; } } return new ArrayMatrix[] {D, P}; }
/// <summary> /// Computes the Householder vector. /// </summary> /// <param name="x"></param> /// <returns></returns> static ArrayMatrix[] HouseholderVector(ArrayMatrix x) { //throw new NotImplementedException("Supposingly buggy!"); //if (!x.IsReal()) // throw new ArgumentException("Cannot compute housholder vector of non-real vector."); var n = x.VectorLength(); if (n == 0) throw new InvalidOperationException("Expected vector as argument."); var y = x/x.Norm(); var buf = y.Extract(2, n, 1, 1); var s = Dot(buf, buf); var v = Zeros(n, 1); v[1] = Complex.One; v.Insert(2, 1, buf); double beta = 0; if (s != 0) { var mu = Complex.Sqrt(y[1]*y[1] + s); if (y[1].Real <= 0) v[1] = y[1] - mu; else v[1] = -s/(y[1] + mu); beta = 2*v[1].Real*v[1].Real/(s.Real + v[1].Real*v[1].Real); v = v/v[1]; } return new ArrayMatrix[] {v, new ArrayMatrix(beta)}; }
public static ArrayMatrix HorizontalConcat(ArrayMatrix A, ArrayMatrix B) { var C = A.Row(1); for (var i = 2; i <= A.RowCount; i++) C.InsertRow(A.Row(i), i); for (var i = 1; i <= B.RowCount; i++) C.InsertRow(B.Row(i), C.RowCount + 1); return C; }
/// <summary> /// Givens product. Internal use for QRGivens. /// </summary> /// <param name="c"></param> /// <param name="s"></param> /// <param name="n"></param> /// <returns></returns> ArrayMatrix GivProd(ArrayMatrix c, ArrayMatrix s, int n) { var n1 = n - 1; var n2 = n - 2; var Q = Eye(n); Q[n1, n1] = c[n1]; Q[n, n] = c[n1]; Q[n1, n] = s[n1]; Q[n, n1] = -s[n1]; for (var k = n2; k >= 1; k--) { var k1 = k + 1; Q[k, k] = c[k]; Q[k1, k] = -s[k]; var q = Q.Extract(k1, k1, k1, n); Q.Insert(k, k1, s[k]*q); Q.Insert(k1, k1, c[k]*q); } return Q; }
/// <summary> /// Creates n by n matrix filled with ones. /// </summary> /// <param name="n">Number of columns.</param> /// <returns>n by n matrix filled with ones.</returns> public static ArrayMatrix Ones(int n) { var M = new ArrayMatrix(n); for (var i = 0; i < n; i++) for (var j = 0; j < n; j++) ((ArrayList) M.Values[i])[j] = Complex.One; return M; }
public static ArrayMatrix operator *(ArrayMatrix A, ArrayMatrix B) { if (A.ColumnCount != B.RowCount) throw new ArgumentException("Inner matrix dimensions must agree."); var C = new ArrayMatrix(A.RowCount, B.ColumnCount); for (var i = 1; i <= A.RowCount; i++) for (var j = 1; j <= B.ColumnCount; j++) C[i, j] = Dot(A.Row(i), B.Column(j)); return C; }
/// <summary> /// Creates m by n matrix filled with random values in {lo,...,hi-1}. /// </summary> ///<param name="lo">Inclusive lower bound.</param> /// <param name="hi">Exclusive upper bound</param> /// <param name="m">Number of rows.</param> /// <param name="n">Number of columns.</param> /// <returns></returns> public static ArrayMatrix Random(int m, int n, int lo, int hi) { var M = new ArrayMatrix(m, n); var r = new Random(); for (var i = 1; i <= m; i++) for (var j = 1; j <= n; j++) M[i, j] = new Complex((double) r.Next(lo, hi)); return M; }
/// <summary> /// Extracts upper trapeze matrix of this matrix. /// </summary> /// <returns></returns> public ArrayMatrix ExtractUpperTrapeze() { var buf = new ArrayMatrix(RowCount, ColumnCount); for (var i = 1; i <= RowCount; i++) for (var j = i; j <= ColumnCount; j++) buf[i, j] = this[i, j]; return buf; }