public static void test_SparseCG() { int N = 10; SymmetricSparseMatrix M = new SymmetricSparseMatrix(); double[] B = new double[N]; for (int i = 0; i < N; ++i) { for (int j = i; j < N; ++j) { if (i == j) { M.Set(i, j, 1); } else { M.Set(i, j, (double)(i + j) / 100.0); } } B[i] = i + 1; } double[] X = new double[N]; SparseSymmetricCG Solver = new SparseSymmetricCG() { B = B, MultiplyF = M.Multiply }; Solver.Solve(); string s = ""; for (int i = 0; i < N; ++i) { s += " " + Solver.X[i]; } System.Console.WriteLine(s); }
// Result must be as large as Mesh.MaxVertexID public bool Solve(Vector3D[] Result) { UpdateForSolve(); // use initial positions as initial solution. Array.Copy(Px, Sx, N); Array.Copy(Py, Sy, N); Array.Copy(Pz, Sz, N); Action <double[], double[]> CombinedMultiply = (X, B) => { M.Multiply(X, B); for (int i = 0; i < N; ++i) { B[i] += WeightsM[i, i] * X[i]; } }; SparseSymmetricCG SolverX = new SparseSymmetricCG() { B = Bx, X = Sx, MultiplyF = CombinedMultiply, PreconditionMultiplyF = Preconditioner.Multiply, UseXAsInitialGuess = true }; SparseSymmetricCG SolverY = new SparseSymmetricCG() { B = By, X = Sy, MultiplyF = CombinedMultiply, PreconditionMultiplyF = Preconditioner.Multiply, UseXAsInitialGuess = true }; SparseSymmetricCG SolverZ = new SparseSymmetricCG() { B = Bz, X = Sz, MultiplyF = CombinedMultiply, PreconditionMultiplyF = Preconditioner.Multiply, UseXAsInitialGuess = true }; SparseSymmetricCG[] solvers = new SparseSymmetricCG[3] { SolverX, SolverY, SolverZ }; bool[] ok = new bool[3]; int[] indices = new int[3] { 0, 1, 2 }; // preconditioned solve is slower =\ //Action<int> SolveF = (i) => { ok[i] = solvers[i].SolvePreconditioned(); }; Action <int> SolveF = (i) => { ok[i] = solvers[i].Solve(); }; gParallel.ForEach(indices, SolveF); if (ok[0] == false || ok[1] == false || ok[2] == false) { return(false); } for (int i = 0; i < N; ++i) { int vid = ToMeshV[i]; Result[vid] = new Vector3D(Sx[i], Sy[i], Sz[i]); } // apply post-fixed constraints if (HavePostFixedConstraints) { foreach (var constraint in SoftConstraints) { if (constraint.Value.PostFix) { int vid = constraint.Key; Result[vid] = constraint.Value.Position; } } } return(true); }
public static void test_SparseCG_Precond() { // A test case where Jacobi preconditioner (ie M = diag(A)) provides some improvement // described in http://www.math.iit.edu/~fass/477577_Chapter_16.pdf int N = 10000; SymmetricSparseMatrix M = new SymmetricSparseMatrix(); double[] B = new double[N]; for (int i = 0; i < N; ++i) { for (int j = i; j < N; ++j) { if (i == j) { M.Set(i, i, 0.5 + Math.Sqrt(i)); } else if (Math.Abs(i - j) == 1) { M.Set(i, j, 1); } else if (Math.Abs(i - j) == 100) { M.Set(i, j, 1); } } B[i] = 1; } SparseSymmetricCG Solver = new SparseSymmetricCG() { B = B, MultiplyF = M.Multiply }; Solver.Solve(); double[] BTest = new double[N]; M.Multiply(Solver.X, BTest); double diff = BufferUtil.DistanceSquared(B, BTest); if (diff > MathUtil.ZeroTolerance) { System.Console.WriteLine("test_SparseCG: initial solve failed!"); } PackedSparseMatrix PackedM = new PackedSparseMatrix(M); PackedM.Sort(); SparseSymmetricCG Solver_PackedM = new SparseSymmetricCG() { B = B, MultiplyF = PackedM.Multiply }; Solver_PackedM.Solve(); PackedM.Multiply(Solver_PackedM.X, BTest); double diff_packed = BufferUtil.DistanceSquared(B, BTest); if (diff_packed > MathUtil.ZeroTolerance) { System.Console.WriteLine("test_SparseCG: Packed solve failed!"); } #if false SparseCholeskyDecomposition cholDecomp = new SparseCholeskyDecomposition(PackedM); cholDecomp.ComputeIncomplete(); // factorization is filled with NaNs!! doing something wrong. double[] TmpX = new double[N], Y = new double[N]; cholDecomp.Solve(BTest, TmpX, Y); // note: can also try just lower-triangular matrix - this is (L+D), Gauss-Seidel preconditioner? // see http://www.math.iit.edu/~fass/477577_Chapter_16.pdf Action <double[], double[]> cholPrecond = (R, Z) => { cholDecomp.Solve(R, Z, Y); }; SymmetricSparseMatrix diagPrecond = new SymmetricSparseMatrix(N); for (int k = 0; k < N; ++k) { diagPrecond[k, k] = 1.0 / M[k, k]; } SparseSymmetricCG Solver_Precond = new SparseSymmetricCG() { B = B, MultiplyF = PackedM.Multiply, PreconditionMultiplyF = diagPrecond.Multiply }; //SparseSymmetricCG Solver_Precond = new SparseSymmetricCG() { B = B, MultiplyF = PackedM.Multiply, PreconditionMultiplyF = cholPrecond }; Solver_Precond.SolvePreconditioned(); PackedM.Multiply(Solver_Precond.X, BTest); double diff_precond = BufferUtil.DistanceSquared(B, BTest); if (diff_precond > MathUtil.ZeroTolerance) { System.Console.WriteLine("test_SparseCG: cholesky-preconditioned solve failed!"); } System.Console.WriteLine("Iterations regular {0} precond {1}", Solver_PackedM.Iterations, Solver_Precond.Iterations); System.Console.WriteLine("Tol regular {0} precond {1}", diff_packed, diff_precond); #endif }
public static void test_SparseCG() { Random r = new Random(31337); int N = 100; var pts = TestUtil.RandomScalars(N, r, new Interval1d(1, 10)); SymmetricSparseMatrix M = new SymmetricSparseMatrix(); double[] B = new double[N]; for (int i = 0; i < N; ++i) { for (int j = i; j < N; ++j) { if (i == j) { M.Set(i, j, pts[i]); } else { M.Set(i, j, (double)(i + j) / 10.0); } } B[i] = i + 1; } SparseSymmetricCG Solver = new SparseSymmetricCG() { B = B, MultiplyF = M.Multiply }; Solver.Solve(); double[] BTest = new double[N]; M.Multiply(Solver.X, BTest); double diff = BufferUtil.DistanceSquared(B, BTest); if (diff > MathUtil.ZeroTolerance) { System.Console.WriteLine("test_SparseCG: initial solve failed!"); } PackedSparseMatrix PackedM = new PackedSparseMatrix(M); PackedM.Sort(); SparseSymmetricCG Solver_PackedM = new SparseSymmetricCG() { B = B, MultiplyF = PackedM.Multiply }; Solver_PackedM.Solve(); PackedM.Multiply(Solver_PackedM.X, BTest); double diff_packed = BufferUtil.DistanceSquared(B, BTest); if (diff_packed > MathUtil.ZeroTolerance) { System.Console.WriteLine("test_SparseCG: Packed solve failed!"); } #if false SparseCholeskyDecomposition decomp = new SparseCholeskyDecomposition(PackedM); decomp.ComputeIncomplete(); PackedSparseMatrix choleskyPrecond = decomp.L.Square(); SymmetricSparseMatrix diagPrecond = new SymmetricSparseMatrix(N); for (int k = 0; k < N; ++k) { diagPrecond[k, k] = 1.0 / M[k, k]; } SparseSymmetricCG Solver_Precond = new SparseSymmetricCG() { B = B, MultiplyF = PackedM.Multiply, PreconditionMultiplyF = diagPrecond.Multiply }; Solver_Precond.SolvePreconditioned(); PackedM.Multiply(Solver_Precond.X, BTest); double diff_precond = BufferUtil.DistanceSquared(B, BTest); if (diff_precond > MathUtil.ZeroTolerance) { System.Console.WriteLine("test_SparseCG: cholesky-preconditioned solve failed!"); } System.Console.WriteLine("Iterations regular {0} precond {1}", Solver_PackedM.Iterations, Solver_Precond.Iterations); System.Console.WriteLine("Tol regular {0} precond {1}", diff_packed, diff_precond); #endif }
// [RMS] this only tests some basic cases... public static void test_Laplacian() { // compact version DMesh3 mesh = new DMesh3(TestUtil.MakeRemeshedCappedCylinder(1.0), true); Debug.Assert(mesh.IsCompact); AxisAlignedBox3d bounds = mesh.GetBounds(); TestUtil.WriteDebugMesh(mesh, "___CG_before.obj"); List <IMesh> result_meshes = new List <IMesh>(); // make uniform laplacian matrix int N = mesh.VertexCount; SymmetricSparseMatrix M = new SymmetricSparseMatrix(); //DenseMatrix M = new DenseMatrix(N, N); double[] Px = new double[N], Py = new double[N], Pz = new double[N]; int[] nbr_counts = new int[N]; for (int vid = 0; vid < N; ++vid) { nbr_counts[vid] = mesh.GetVtxEdgeCount(vid); } int ti = MeshQueries.FindNearestTriangle_LinearSearch(mesh, new Vector3d(2, 5, 2)); int v_pin = mesh.GetTriangle(ti).a; List <int> constraints = new List <int>() { v_pin }; double consW = 10; double consBottom = 10; foreach (int vid in constraints) { result_meshes.Add(TestUtil.MakeMarker(mesh.GetVertex(vid), (vid == 0) ? 0.2f : 0.1f, Colorf.Red)); } for (int vid = 0; vid < N; ++vid) { int n = nbr_counts[vid]; Vector3d v = mesh.GetVertex(vid), c = Vector3d.Zero; Px[vid] = v.x; Py[vid] = v.y; Pz[vid] = v.z; bool bottom = (v.y - bounds.Min.y) < 0.01f; double sum_w = 0; foreach (int nbrvid in mesh.VtxVerticesItr(vid)) { int n2 = nbr_counts[nbrvid]; // weight options //double w = -1; double w = -1.0 / Math.Sqrt(n + n2); //double w = -1.0 / n; M.Set(vid, nbrvid, w); c += w * mesh.GetVertex(nbrvid); sum_w += w; } sum_w = -sum_w; M.Set(vid, vid, sum_w); // add soft constraints if (constraints.Contains(vid)) { M.Set(vid, vid, sum_w + consW); } else if (bottom) { M.Set(vid, vid, sum_w + consBottom); } } // compute laplacians double[] MLx = new double[N], MLy = new double[N], MLz = new double[N]; M.Multiply(Px, MLx); M.Multiply(Py, MLy); M.Multiply(Pz, MLz); DiagonalMatrix Preconditioner = new DiagonalMatrix(N); for (int i = 0; i < N; i++) { Preconditioner.Set(i, i, 1.0 / M[i, i]); } MLy[v_pin] += consW * 0.5f; MLx[v_pin] += consW * 0.5f; MLz[v_pin] += consW * 0.5f; bool useXAsGuess = true; // preconditioned SparseSymmetricCG SolverX = new SparseSymmetricCG() { B = MLx, X = Px, MultiplyF = M.Multiply, PreconditionMultiplyF = Preconditioner.Multiply, UseXAsInitialGuess = useXAsGuess }; // initial solution SparseSymmetricCG SolverY = new SparseSymmetricCG() { B = MLy, X = Py, MultiplyF = M.Multiply, UseXAsInitialGuess = useXAsGuess }; // neither of those SparseSymmetricCG SolverZ = new SparseSymmetricCG() { B = MLz, MultiplyF = M.Multiply }; bool bx = SolverX.Solve(); bool by = SolverY.Solve(); bool bz = SolverZ.Solve(); for (int vid = 0; vid < mesh.VertexCount; ++vid) { Vector3d newV = new Vector3d(SolverX.X[vid], SolverY.X[vid], SolverZ.X[vid]); mesh.SetVertex(vid, newV); } result_meshes.Add(mesh); TestUtil.WriteDebugMeshes(result_meshes, "___CG_result.obj"); }