/// <summary> /// Extract sub matrix. /// </summary> /// <param name="i1">Start row.</param> /// <param name="i2">End row.</param> /// <param name="j1">Start column.</param> /// <param name="j2">End column.</param> /// <returns></returns> public MatrixQ Extract(int i1, int i2, int j1, int j2) { if (i2 < i1 || j2 < j1 || i1 <= 0 || j2 <= 0 || i2 > rowCount || j2 > columnCount) throw new ArgumentException("Index exceeds matrix dimension."); MatrixQ B = new MatrixQ(i2 - i1 + 1, j2 - j1 + 1); for (int i = i1; i <= i2; i++) for (int j = j1; j <= j2; j++) B[i - i1 + 1, j - j1 + 1] = this[i, j]; return B; }
/// <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(MatrixQ 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."); ArrayList 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; }
public static MatrixQ HorizontalConcat(MatrixQ[] A) { if (A == null) throw new ArgumentNullException(); else if (A.Length == 1) return A[0]; else { MatrixQ C = HorizontalConcat(A[0], A[1]); for (int i = 2; i < A.Length; i++) { C = HorizontalConcat(C, A[i]); } return C; } }
/// <summary> /// Generates diagonal matrix /// </summary> /// <param name="diag_vector">column vector containing the diag elements</param> /// <returns></returns> public static MatrixQ Diag(MatrixQ diag_vector) { int dim = diag_vector.VectorLength(); if (dim == 0) throw new ArgumentException("diag_vector must be 1xN or Nx1"); MatrixQ M = new MatrixQ(dim, dim); for (int i = 1; i <= dim; i++) { M[i, i] = diag_vector[i]; } 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(MatrixQ v, MatrixQ w) { int m = v.VectorLength(); int 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."); Complex buf = Complex.Zero; for (int i = 1; i <= m; i++) { buf += v[i] * w[i]; } return buf; }
public static MatrixQ operator *(Complex x, MatrixQ A) { MatrixQ B = new MatrixQ(A.RowCount, A.ColumnCount); for (int i = 1; i <= A.RowCount; i++) { for (int j = 1; j <= A.ColumnCount; j++) { B[i, j] = A[i, j] * x; } } return B; }
/// <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 MatrixQ ChessboardMatrix(int n, bool even) { MatrixQ M = new MatrixQ(n); if (even) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) M[i, j] = KroneckerDelta((i + j) % 2, 0); else for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) M[i, j] = KroneckerDelta((i + j) % 2, 1); 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(MatrixQ row, int i) { int 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 (int k = 1; k <= size; k++) { ((ArrayList)Values[i]).Add(row[k]); } // fill w/ zeros if vector row is too short for (int 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 MatrixQ[] QRGramSchmidt() { int m = rowCount; int n = columnCount; MatrixQ A = this.Clone(); MatrixQ Q = new MatrixQ(m, n); MatrixQ R = new MatrixQ(n, n); // the first column of Q equals the first column of this matrix for (int i = 1; i <= m; i++) Q[i, 1] = A[i, 1]; R[1, 1] = Complex.One; for (int k = 1; k <= n; k++) { R[k, k] = new Complex(A.Column(k).Norm()); for (int i = 1; i <= m; i++) Q[i, k] = A[i, k] / R[k, k]; for (int j = k + 1; j <= n; j++) { R[k, j] = Dot(Q.Column(k), A.Column(j)); for (int i = 1; i <= m; i++) A[i, j] = A[i, j] - Q[i, k] * R[k, j]; } } return new MatrixQ[] { Q, R }; }
/// <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, MatrixQ M) { for (int m = 1; m <= M.rowCount; m++) for (int n = 1; n <= M.columnCount; n++) this[i + m - 1, j + n - 1] = M[m, n]; }
/// <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(MatrixQ col, int j) { int 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 (int k = 0; k < size; k++) { ((ArrayList)Values[k]).Insert(j, col[k + 1]); } // fill w/ zeros if vector col too short for (int k = size; k < rowCount; k++) { ((ArrayList)Values[k]).Insert(j, 0); } }
/// <summary> /// Performs Hessenberg-Householder reduction, where {H, Q} /// is returned, with H Hessenbergian, Q orthogonal and H = Q'AQ. /// </summary> /// <returns></returns> public MatrixQ[] HessenbergHouseholder() { //throw new NotImplementedException("Still buggy!"); if (!this.IsSquare()) throw new InvalidOperationException("Cannot perform Hessenberg Householder decomposition of non-square matrix."); int n = rowCount; MatrixQ Q = Identity(n); MatrixQ H = this.Clone(); MatrixQ I, N, R, P; MatrixQ[] vbeta = new MatrixQ[2]; int m; // don't try to understand from the code alone. // this is pure magic to me - mathematics, reborn as code. for (int 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 MatrixQ[] { H, Q }; }
/// <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(MatrixQ b) { if (!this.IsLowerTriangular()) throw new InvalidOperationException("Cannot perform forward insertion for matrix not being lower triangular."); if (/*this.Determinant*/this.DiagProd() == 0) throw new InvalidOperationException("Warning: Matrix is nearly singular."); int n = rowCount; if (b.VectorLength() != n) throw new ArgumentException("Parameter must vector of the same height as matrix."); for (int j = 1; j <= n - 1; j++) { b[j] /= this[j, j]; for (int i = 1; i <= n - j; i++) b[j + i] -= b[j] * this[j + i, j]; } b[n] /= this[n, n]; }
/// <summary> /// Extracts upper trapeze matrix of this matrix. /// </summary> /// <returns></returns> public MatrixQ ExtractUpperTrapeze() { MatrixQ buf = new MatrixQ(rowCount, columnCount); for (int i = 1; i <= rowCount; i++) { for (int j = i; j <= columnCount; j++) { buf[i, j] = this[i, j]; } } return buf; }
/// <summary> /// Givens product. Internal use for QRGivens. /// </summary> /// <param name="c"></param> /// <param name="s"></param> /// <param name="n"></param> /// <returns></returns> private MatrixQ GivProd(MatrixQ c, MatrixQ s, int n) { int n1 = n - 1; int n2 = n - 2; MatrixQ Q = Eye(n); Q[n1, n1] = c[n1]; Q[n, n] = c[n1]; Q[n1, n] = s[n1]; Q[n, n1] = -s[n1]; for (int k = n2; k >= 1; k--) { int k1 = k + 1; Q[k, k] = c[k]; Q[k1, k] = -s[k]; MatrixQ q = Q.Extract(k1, k1, k1, n); Q.Insert(k, k1, s[k] * q); Q.Insert(k1, k1, c[k] * q); } return Q; }
/// <summary> /// Executes the QR iteration. /// </summary> /// <param name="max_iterations"></param> /// <returns></returns> public MatrixQ QRIterationBasic(int max_iterations) { if (!this.IsReal()) throw new InvalidOperationException("Basic QR iteration is possible only for real matrices."); MatrixQ T = this.Clone(); MatrixQ[] QR = new MatrixQ[2]; for (int i = 0; i < max_iterations; i++) { QR = T.QRGramSchmidt(); T = QR[1] * QR[0]; } return T; }
public static MatrixQ operator *(MatrixQ A, MatrixQ B) { if (A.ColumnCount != B.RowCount) throw new ArgumentException("Inner matrix dimensions must agree."); MatrixQ C = new MatrixQ(A.RowCount, B.ColumnCount); for (int i = 1; i <= A.RowCount; i++) { for (int j = 1; j <= B.ColumnCount; j++) { C[i, j] = Dot(A.Row(i), B.Column(j)); } } return C; }
/// <summary> /// Returns the matrix of the real parts of the entries of this matrix. /// </summary> /// <returns></returns> public MatrixQ Re() { MatrixQ M = new MatrixQ(rowCount, columnCount); for (int i = 1; i <= rowCount; i++) for (int j = 1; j <= columnCount; j++) M[i, j] = new Complex(this[i, j].Re); return M; }
/// <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 MatrixQ BlockMatrix(MatrixQ A, MatrixQ B, MatrixQ C, MatrixQ 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."); MatrixQ R = new MatrixQ(A.RowCount + C.RowCount, A.ColumnCount + B.ColumnCount); for (int i = 1; i <= R.rowCount; i++) for (int 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> /// Retrieves row with one-based index i. /// </summary> /// <param name="i"></param> /// <returns>i-th row...</returns> public MatrixQ 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(); MatrixQ buf = new MatrixQ(columnCount, 1); for (int j = 1; j <= this.columnCount; j++) { buf[j] = this[i, j]; } 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 MatrixQ DFS(MatrixQ 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."); int 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}."); MatrixQ spanTree = new MatrixQ(n); bool[] marked = new bool[n + 1]; Stack todo = new Stack(); todo.Push(root); marked[root] = true; // adajacence lists for each vertex ArrayList[] A = new ArrayList[n + 1]; for (int i = 1; i <= n; i++) { A[i] = new ArrayList(); for (int j = 1; j <= n; j++) if (adjacence_matrix[i, j].Re != 0 && adjacence_matrix[i, j].Im != 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].Re = 1; // mark vw todo.Push(w); // one more to search } A[v].RemoveAt(0); } else todo.Pop(); } return spanTree; }
/// <summary> /// Splits matrix into its row vectors. /// </summary> /// <returns>Array of row vectors.</returns> public MatrixQ[] RowVectorize() { MatrixQ[] buf = new MatrixQ[rowCount]; for (int i = 1; i <= buf.Length; i++) { buf[i] = this.Row(i); } return buf; }
/// <summary> /// Generates diagonal matrix /// </summary> /// <param name="diag_vector">column vector containing the diag elements</param> /// <returns></returns> public static MatrixQ Diag(MatrixQ diag_vector, int offset) { int 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."); MatrixQ M = new MatrixQ(dim + Math.Abs(offset), dim + Math.Abs(offset)); dim = M.RowCount; if (offset >= 0) { for (int i = 1; i <= dim - offset; i++) { M[i, i + offset] = diag_vector[i]; } } else { for (int i = 1; i <= dim + offset; i++) { M[i - offset, i] = diag_vector[i]; } } return M; }
/// <summary> /// Solves equation this*x = b via conjugate gradient method. /// </summary> /// <param name="b"></param> /// <returns></returns> public MatrixQ SolveCG(MatrixQ b) { throw new NotImplementedException("Still buggy!"); }
/// <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 MatrixQ[] Floyd(MatrixQ 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."); int n = adjacence_matrix.RowCount; MatrixQ D = adjacence_matrix.Clone(); // distance matrix MatrixQ P = new MatrixQ(n); double buf; for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { buf = D[i, k].Re + D[k, j].Re; if (buf < D[i, j].Re) { D[i, j].Re = buf; P[i, j].Re = k; } } return new MatrixQ[] { D, P }; }
/// <summary> /// Swaps each matrix entry A[i, j] with A[j, i]. /// </summary> /// <returns>A transposed matrix.</returns> public MatrixQ Transpose() { MatrixQ M = new MatrixQ(columnCount, rowCount); for (int i = 1; i <= columnCount; i++) { for (int j = 1; j <= rowCount; j++) { M[i, j] = this[j, i]; } } return M; }
public static MatrixQ HorizontalConcat(MatrixQ A, MatrixQ B) { MatrixQ C = A.Row(1); for (int i = 2; i <= A.RowCount; i++) { C.InsertRow(A.Row(i), i); } for (int i = 1; i <= B.RowCount; i++) { C.InsertRow(B.Row(i), C.RowCount + 1); } return C; }
/// <summary> /// Computes the Householder vector. /// </summary> /// <param name="x"></param> /// <returns></returns> private static MatrixQ[] HouseholderVector(MatrixQ x) { //throw new NotImplementedException("Supposingly buggy!"); //if (!x.IsReal()) // throw new ArgumentException("Cannot compute housholder vector of non-real vector."); int n = x.VectorLength(); if (n == 0) throw new InvalidOperationException("Expected vector as argument."); MatrixQ y = x / x.Norm(); MatrixQ buf = y.Extract(2, n, 1, 1); Complex s = Dot(buf, buf); MatrixQ v = Zeros(n, 1); v[1] = Complex.One; v.Insert(2, 1, buf); double beta = 0; if (s != 0) { Complex mu = Complex.Sqrt(y[1] * y[1] + s); if (y[1].Re <= 0) v[1] = y[1] - mu; else v[1] = -s / (y[1] + mu); beta = 2 * v[1].Re * v[1].Re / (s.Re + v[1].Re * v[1].Re); v = v / v[1]; } return new MatrixQ[] { v, new MatrixQ(beta) }; }
/// <summary> /// Creates m by n matrix filled with ones. /// </summary> /// <param name="m">Number of rows.</param> /// <param name="n">Number of columns.</param> /// <returns>m by n matrix filled with ones.</returns> public static MatrixQ Ones(int m, int n) { MatrixQ M = new MatrixQ(m, n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { ((ArrayList)M.Values[i])[j] = Complex.One; } } return M; }
/// <summary> /// Extracts main diagonal vector of the matrix as a column vector. /// </summary> /// <returns></returns> public MatrixQ DiagVector() { if (!this.IsSquare()) throw new InvalidOperationException("Cannot get diagonal of non-square matrix."); MatrixQ v = new MatrixQ(this.columnCount, 1); for (int i = 1; i <= this.columnCount; i++) { v[i] = this[i, i]; } return v; }