public void minusTest() { const int N = 50; var A = SparseDoubleMatrix.Identity(N, N); for (int i = 0; i < N; i++) { A[i, i] = i % 5 == 0 ? 1.0 : 0.0; } var A1 = A.Clone(); var Zeros = SparseDoubleMatrix.Identity(N, N); for (int i = 0; i < N; i++) { Zeros[i, i] = 0.0; } var B = SparseDoubleMatrix.Identity(N, N); for (int i = 0; i < N; i++) { B[i, i] = i % 5 == 0 ? 1.0 : 0.0; } var C = A.minus(B); AssertMatrixEqualsEps(C, Zeros); C = A - B; AssertMatrixEqualsEps(C, Zeros); }
public void MapTest1() { int N = 6; var m1 = new SparseDoubleMatrix(N, N); var m2 = new SparseDoubleMatrix(N, N); // Fill matrix m2 diagonal elements with 66 m1.MapSparseIncludingDiagonal((a, r, c) => a + (r == c ? 66 : 0), m2); for (int i = 0; i < m2.RowCount; ++i) { for (int j = 0; j < m2.ColumnCount; ++j) { Assert.AreEqual(i == j ? 66 : 0, m2[i, j]); } } // the same should work with the now already used matrix // fill diagonal now with 77 m1.MapSparseIncludingDiagonal((a, r, c) => a + (r == c ? 77 : 0), m2); for (int i = 0; i < m2.RowCount; ++i) { for (int j = 0; j < m2.ColumnCount; ++j) { Assert.AreEqual(i == j ? 77 : 0, m2[i, j]); } } }
public void TimesTest() { const int N = 50; const int M = 30; var A = SparseDoubleMatrix.Identity(M, N); for (int i = 0; i < M; i++) { if (i < N) { A[i, i] = i % 5 == 0 ? 1.0 : 0.0; } } var b = new DoubleVector(N); for (int i = 0; i < N; i++) { b[i] = 2.0; } var B = new DoubleVector(M); for (int i = 0; i < M; i++) { B[i] = i % 5 == 0 ? 2.0 : 0.0; } var C = A.times(b); AssertVectorEqualsEps(B, C); }
/// <summary>Solves system of linear equations Ax = b using Gaussian elimination with partial pivoting</summary> /// <param name="A">Sparse matrix, 'A'. This matrix is modified during solution!</param> /// <param name="b">Right part, 'b', is modified during solution.</param> /// <returns>Vector x with the result.</returns> public double[] SolveDestructive(SparseDoubleMatrix A, double[] b) { var x = new double[b.Length]; SolveDestructive(A, b, x); return(x); }
/// <summary>Matrix subtraction for a sparse matrix</summary> /// <param name="B">The matrix to subtract</param> /// <returns>The result A - B</returns> public SparseDoubleMatrix minus(SparseDoubleMatrix B) { if (B == null) { throw new ArgumentNullException("B"); } var C = new SparseDoubleMatrix(m, n); for (int i = 0; i < m; i++) { if (indices[i] != null) { C.indices[i] = new int[count[i]]; C.items[i] = new double[count[i]]; for (int j = 0; j < count[i]; j++) { C.indices[i][j] = indices[i][j]; C.items[i][j] = items[i][j] - B.items[i][j]; } } C.count[i] = count[i]; } return(C); }
public void MapTest5() { int N = 6; var m1 = new SparseDoubleMatrix(N, N); var m2 = new SparseDoubleMatrix(N, N); // Pre-fill m1 elements right of the diagonal with values for (int i = 0; i < N - 1; ++i) { m1[i, i + 1] = i * 13; } // Fill matrix m2 diagonal elements with 66 m1.MapSparseIncludingDiagonal((a, r, c) => 3 * a + (r == c ? 66 : 0), m2); for (int i = 0; i < m2.RowCount; ++i) { for (int j = 0; j < m2.ColumnCount; ++j) { double expected = 0; if (i == j) { expected = 66; } else if (i + 1 == j) { expected = i * 13 * 3; } Assert.AreEqual(expected, m2[i, j]); } } // the same should work with the now already used matrix // fill diagonal now with 77 m1.MapSparseIncludingDiagonal((a, r, c) => 5 * a + (r == c ? 77 : 0), m2); for (int i = 0; i < m2.RowCount; ++i) { for (int j = 0; j < m2.ColumnCount; ++j) { double expected = 0; if (i == j) { expected = 77; } else if (i + 1 == j) { expected = i * 13 * 5; } Assert.AreEqual(expected, m2[i, j]); } } }
public void SolveGETest() { const int N = 50; var a = new SparseDoubleMatrix(N, N); for (int i = 0; i < N; i++) { a[i, i] = 1; } // Apply random rotations around each pair of axes. This will keep det(A) ~ 1 var rand = new Random(); for (int i = 0; i < N; i++) { for (int j = i + 1; j < N; j++) { double angle = rand.NextDouble() * 2 * Math.PI; var r = new SparseDoubleMatrix(N, N); for (int k = 0; k < N; k++) { r[k, k] = 1; } r[i, i] = r[j, j] = Math.Cos(angle); r[i, j] = Math.Sin(angle); r[j, i] = -Math.Sin(angle); a = a * r; } } var ainit = a.Clone(); // Generate random vector var b = new DoubleVector(N); for (int i = 0; i < N; i++) { b[i] = rand.NextDouble(); } var binit = b.Clone(); // Solve system var solver = new GaussianEliminationSolver(); var sw = new Stopwatch(); sw.Start(); var x = solver.SolveDestructive(a, b.GetInternalData()); sw.Stop(); Trace.WriteLine("Gaussian elimination took: " + sw.ElapsedTicks); // Put solution into system var b2 = ainit * x; // Verify result is the same Assert.IsTrue(VectorMath.LInfinityNorm(binit, b2) < 1e-6); }
public SparseDoubleMatrix Clone() { var A = new SparseDoubleMatrix(m, n); for (int i = 0; i < m; i++) { A.indices[i] = (int[])indices[i].Clone(); A.items[i] = (double[])items[i].Clone(); A.count = (int[])count.Clone(); } return(A); }
private void AssertMatrixEqualsEps(SparseDoubleMatrix A, SparseDoubleMatrix B) { double sum = 0.0; for (int i = 0; i < A.RowCount; i++) { for (int j = 0; j < A.ColumnCount; j++) { sum += A[i, j] - B[i, j]; } } AssertEqualsEps(sum, 0.0); }
public void isLowerTriangularTest() { const int N = 50; var A = SparseDoubleMatrix.Identity(N, N); for (int i = 0; i < N; i++) { A[i, i] = i % 5 == 0 ? 1.0 : 0.0; } Assert.AreEqual(A.IsLowerTriangular(), true); A[45, 40] = 1.0; Assert.AreEqual(A.IsLowerTriangular(), true); A[40, 45] = 1.0; Assert.AreEqual(A.IsLowerTriangular(), false); }
/// <summary>Matrix multiplication by a scalar</summary> /// <param name="s">Scalar</param> /// <returns>Scaled sparse matrix</returns> public SparseDoubleMatrix times(double s) { var B = new SparseDoubleMatrix(m, n); for (int i = 0; i < m; i++) { if (indices[i] != null) { B.indices[i] = new int[count[i]]; B.items[i] = new double[count[i]]; for (int j = 0; j < count[i]; j++) { B.indices[i][j] = indices[i][j]; B.items[i][j] = s * items[i][j]; } } B.count[i] = count[i]; } return(B); }
/// <summary>Matrix right multiplication by a matrix</summary> /// <param name="B">Scaling factor</param> /// <returns>A * B where A is current sparce matrix</returns> public SparseDoubleMatrix times(SparseDoubleMatrix B) { if (B == null) { throw new ArgumentNullException("B"); } if (B.m != n) { throw new System.ArgumentException("Sparse natrix inner dimensions must agree."); } var C = new SparseDoubleMatrix(m, B.n); int idx, ii; for (int i = 0; i < m; i++) { if (indices[i] != null) { for (int j = 0; j < B.n; j++) { for (int jj = 0; jj < count[i]; jj++) { ii = indices[i][jj]; if (B.indices[ii] != null) { idx = Array.BinarySearch(B.indices[ii], 0, B.count[ii], j); if (idx >= 0) { C[i, j] += items[i][jj] * B.items[ii][idx]; } } } } } } return(C); }
public void MapTest3() { int N = 6; var m1 = new SparseDoubleMatrix(N, N); var m2 = new SparseDoubleMatrix(N, N); // Pre-fill m2 elements right of the diagonal with values, these values should be discarded; for (int i = 0; i < N - 1; ++i) { m2[i, i + 1] = i * 13; } // Fill matrix m2 diagonal elements with 66 m1.MapSparseIncludingDiagonal((a, r, c) => a + (r == c ? 66 : 0), m2); for (int i = 0; i < m2.RowCount; ++i) { for (int j = 0; j < m2.ColumnCount; ++j) { Assert.AreEqual(i == j ? 66 : 0, m2[i, j]); } } // the same should work with the now already used matrix // fill diagonal now with 77 m1.MapSparseIncludingDiagonal((a, r, c) => a + (r == c ? 77 : 0), m2); for (int i = 0; i < m2.RowCount; ++i) { for (int j = 0; j < m2.ColumnCount; ++j) { Assert.AreEqual(i == j ? 77 : 0, m2[i, j]); } } }
public void TimesEqualsTest() { const int N = 50; var A = SparseDoubleMatrix.Identity(N, N); for (int i = 0; i < N; i++) { A[i, i] = i % 5 == 0 ? 1.0 : 0.0; } SparseDoubleMatrix AInit = A.Clone(); SparseDoubleMatrix B = A.Clone(); for (int i = 0; i < N; i++) { B[i, i] = i % 5 == 0 ? 2.0 : 0.0; } var C = A.Mul(2.0); AssertMatrixEqualsEps(B, C); var D = AInit * 2.0; AssertMatrixEqualsEps(B, D); }
/// <summary>Solves system of linear equations Ax = b using Gaussian elimination with partial pivoting</summary> /// <param name="A">Sparse matrix, 'A'. This matrix is modified during solution!</param> /// <param name="b">Right part, 'b', is modified during solution.</param> /// <param name="x">Vector to store the solution.</param> public void SolveDestructive(SparseDoubleMatrix A, double[] b, double[] x) { if (A == null) { throw new ArgumentNullException(nameof(A)); } if (b == null) { throw new ArgumentNullException(nameof(b)); } if (x == null) { throw new ArgumentNullException(nameof(x)); } int n = A.RowCount; if (!(n == b.Length)) { throw new RankException("Mismatch between number of rows of the matrix A and length of vector b"); } if (!(A.ColumnCount == x.Length)) { throw new RankException("Mismatch between number of columns of the matrix A and length of vector x"); } for (int j = 0; j < n; j++) { // Find row with largest absolute value of j-st element int maxIdx = 0; double maxVal = A[maxIdx, j]; double Aij = 0.0; for (int i = 0; i < n - j; i++) { Aij = A[i, j]; if (Math.Abs(Aij) > Math.Abs(maxVal)) { maxIdx = i; maxVal = Aij; } } if (Math.Abs(maxVal) < 1e-12) { throw new InvalidOperationException("Cannot apply Gauss method"); } // Divide this row by max value A.ScaleRow(maxIdx, j + 1, n - 1, 1 / maxVal); b[maxIdx] /= maxVal; A[maxIdx, j] = 1.0; // Move this row to bottom if (maxIdx != n - j - 1) { A.SwitchRows(maxIdx, n - j - 1); var temp3 = b[n - j - 1]; b[n - j - 1] = b[maxIdx]; b[maxIdx] = temp3; } // Process all other rows for (int i = 0; i < n - j - 1; i++) { Aij = A[i, j]; if (Aij != 0) { var indices = A.GetIndicesOfRow(n - j - 1); foreach (int k in indices) { if (k > j) { A[i, k] -= Aij * A[n - j - 1, k]; } } b[i] -= Aij * b[n - j - 1]; A[i, j] = 0; } } } // Build answer for (int i = 0; i < n; i++) { double s = b[i]; var Ai = A.GetRow(i); for (int k = 0; k < Ai.count; k++) { if (Ai.indices[k] >= n - i) { s -= x[Ai.indices[k]] * A.GetRow(i).items[k]; } } x[n - i - 1] = s; } }