/// <summary> /// Construct packed versions of input matrices, and then use sparse row/column dot /// to compute elements of output matrix. This is faster. But still relatively expensive. /// </summary> void multiply_fast(SymmetricSparseMatrix M2in, ref SymmetricSparseMatrix Rin, bool bParallel) { int N = Rows; if (M2in.Rows != N) { throw new Exception("SymmetricSparseMatrix.Multiply: matrices have incompatible dimensions"); } if (Rin == null) { Rin = new SymmetricSparseMatrix(); } SymmetricSparseMatrix R = Rin; // require alias for use in lambda below PackedSparseMatrix M = new PackedSparseMatrix(this); M.Sort(); PackedSparseMatrix M2 = new PackedSparseMatrix(M2in, true); M2.Sort(); // Parallel variant is vastly faster, uses spinlock to control access to R if (bParallel) { // goddamn SpinLock is in .Net 4 //SpinLock spin = new SpinLock(); gParallel.ForEach(Interval1i.Range(N), (r1i) => { for (int c2i = r1i; c2i < N; c2i++) { double v = M.DotRowColumn(r1i, c2i, M2); if (Math.Abs(v) > MathUtil.ZeroTolerance) { //bool taken = false; //spin.Enter(ref taken); //Debug.Assert(taken); //R[r1i, c2i] = v; //spin.Exit(); lock (R) { R[r1i, c2i] = v; } } } }); } else { for (int r1i = 0; r1i < N; r1i++) { for (int c2i = r1i; c2i < N; c2i++) { double v = M.DotRowColumn(r1i, c2i, M2); if (Math.Abs(v) > MathUtil.ZeroTolerance) { R[r1i, c2i] = v; } } } } }
/// <summary> /// Compute dot product of this.row[r] and M.col[c], where the /// column is stored as MTranspose.row[c] /// </summary> public double DotRowColumn(int r, int c, PackedSparseMatrix MTranspose) { Debug.Assert(Sorted && MTranspose.Sorted); Debug.Assert(Rows.Length == MTranspose.Rows.Length); int a = 0; int b = 0; nonzero[] Row = Rows[r]; nonzero[] Col = MTranspose.Rows[c]; int NA = Row.Length; int NB = Col.Length; double sum = 0; while (a < NA && b < NB) { if (Row[a].j == Col[b].j) { sum += Row[a].d * Col[b].d; a++; b++; } else if (Row[a].j < Col[b].j) { a++; } else { b++; } } return(sum); }
/// <summary> /// Returns this*this, as a packed sparse matrix. Computes in parallel. /// </summary> public PackedSparseMatrix SquarePackedParallel() { PackedSparseMatrix M = new PackedSparseMatrix(this); M.Sort(); return(M.Square()); }
public PackedSparseMatrix Square() { if (Rows.Length != Columns) { throw new Exception("PackedSparseMatrix.Square: matrix is not square!"); } int N = Columns; var entries = new DVector <matrix_entry>(); var entries_lock = new SpinLock(); gParallel.BlockStartEnd(0, N - 1, (r_start, r_end) => { for (int r1i = r_start; r1i <= r_end; r1i++) { // determine which entries of squared matrix might be nonzeros var nbrs = new HashSet <int>(); nbrs.Add(r1i); PackedSparseMatrix.nonzero[] row = Rows[r1i]; for (int k = 0; k < row.Length; ++k) { if (row[k].j > r1i) { nbrs.Add(row[k].j); } PackedSparseMatrix.nonzero[] row2 = Rows[row[k].j]; for (int j = 0; j < row2.Length; ++j) { if (row2[j].j > r1i) // only compute lower-triangular entries { nbrs.Add(row2[j].j); } } } // compute them! foreach (int c2i in nbrs) { double v = DotRowColumn(r1i, c2i, this); if (Math.Abs(v) > MathUtil.ZeroTolerance) { bool taken = false; entries_lock.Enter(ref taken); entries.Add(new matrix_entry() { r = r1i, c = c2i, value = v }); entries_lock.Exit(); } } } }); var R = new PackedSparseMatrix(entries, N, N, true); return(R); }
/// <summary> /// Compute dot product of this.row[r] and M.col[c], where the /// column is stored as MTranspose.row[c] /// </summary> public double DotRowColumn(int r, int c, PackedSparseMatrix MTranspose) { if (Sorted == false || MTranspose.Sorted == false) { throw new Exception("PackedSparseMatrix.DotRowColumn: matrices must be sorted!"); } if (Rows.Length != MTranspose.Rows.Length) { throw new Exception("PackedSparseMatrix.DotRowColumn: matrices are not the same size!"); } Debug.Assert(Sorted && MTranspose.Sorted); Debug.Assert(Rows.Length == MTranspose.Rows.Length); int ri = 0; int ci = 0; nonzero[] Row = Rows[r]; nonzero[] Col = MTranspose.Rows[c]; int NR = Row.Length; int NC = Col.Length; int last_col_j = Col[NC - 1].j; int last_row_j = Row[NR - 1].j; double sum = 0; while (ri < NR && ci < NC) { // early out if we passed last nonzero in other array if (Row[ri].j > last_col_j || Col[ci].j > last_row_j) { break; } if (Row[ri].j == Col[ci].j) { sum += Row[ri].d * Col[ci].d; ri++; ci++; } else if (Row[ri].j < Col[ci].j) { ri++; } else { ci++; } } return(sum); }
// returns this*this (requires less memory) public SymmetricSparseMatrix Square(bool bParallel = true) { var R = new SymmetricSparseMatrix(); var M = new PackedSparseMatrix(this); M.Sort(); // Parallel variant is vastly faster, uses spinlock to control access to R if (bParallel) { // goddamn SpinLock is in .Net 4 //SpinLock spin = new SpinLock(); gParallel.ForEach(Interval1i.Range(N), (r1i) => { for (int c2i = r1i; c2i < N; c2i++) { double v = M.DotRowColumn(r1i, c2i, M); if (Math.Abs(v) > MathUtil.ZeroTolerance) { //bool taken = false; //spin.Enter(ref taken); //Debug.Assert(taken); //R[r1i, c2i] = v; //spin.Exit(); lock (R) { R[r1i, c2i] = v; } } } }); } else { for (int r1i = 0; r1i < N; r1i++) { for (int c2i = r1i; c2i < N; c2i++) { double v = M.DotRowColumn(r1i, c2i, M); if (Math.Abs(v) > MathUtil.ZeroTolerance) { R[r1i, c2i] = v; } } } } return(R); }
public PackedSparseMatrix(PackedSparseMatrix copy) { int N = copy.Rows.Length; Rows = new nonzero[N][]; for (int r = 0; r < N; ++r) { Rows[r] = new nonzero[copy.Rows[r].Length]; Array.Copy(copy.Rows[r], Rows[r], Rows[r].Length); } Columns = copy.Columns; Sorted = copy.Sorted; NumNonZeros = copy.NumNonZeros; StorageMode = copy.StorageMode; IsSymmetric = copy.IsSymmetric; }
/// <summary> /// Compute dot product of this.row[r] with all columns of M, /// where columns are stored in MTranspose rows. /// In theory more efficient than doing DotRowColumn(r,c) for each c, /// however so far the difference is negligible...perhaps because /// there are quite a few more branches in the inner loop /// </summary> public void DotRowAllColumns(int r, double[] sums, int[] col_indices, PackedSparseMatrix MTranspose) { Debug.Assert(Sorted && MTranspose.Sorted); Debug.Assert(Rows.Length == MTranspose.Rows.Length); int N = Rows.Length; int a = 0; nonzero[] Row = Rows[r]; int NA = Row.Length; Array.Clear(sums, 0, N); Array.Clear(col_indices, 0, N); while (a < NA) { int aj = Row[a].j; for (int ci = 0; ci < N; ++ci) { nonzero[] Col = MTranspose.Rows[ci]; int b = col_indices[ci]; if (b >= Col.Length) { continue; } while (b < Col.Length && Col[b].j < aj) { b++; } if (b < Col.Length && aj == Col[b].j) { sums[ci] += Row[a].d * Col[b].d; b++; } col_indices[ci] = b; } a++; } }
public void Initialize() { ToMeshV = new int[Mesh.MaxVertexID]; ToIndex = new int[Mesh.MaxVertexID]; N = 0; foreach (int vid in Mesh.VertexIndices()) { ToMeshV[N] = vid; ToIndex[vid] = N; N++; } Px = new double[N]; Py = new double[N]; Pz = new double[N]; nbr_counts = new int[N]; M = new SymmetricSparseMatrix(); for (int i = 0; i < N; ++i) { int vid = ToMeshV[i]; Vector3d v = Mesh.GetVertex(vid); Px[i] = v.x; Py[i] = v.y; Pz[i] = v.z; nbr_counts[i] = Mesh.GetVtxEdgeCount(vid); } // construct laplacian matrix for (int i = 0; i < N; ++i) { int vid = ToMeshV[i]; int n = nbr_counts[i]; double sum_w = 0; foreach (int nbrvid in Mesh.VtxVerticesItr(vid)) { int j = ToIndex[nbrvid]; int n2 = nbr_counts[j]; // weight options //double w = -1; double w = -1.0 / Math.Sqrt(n + n2); //double w = -1.0 / n; M.Set(i, j, w); sum_w += w; } sum_w = -sum_w; M.Set(vid, vid, sum_w); } // transpose(L) * L, but matrix is symmetric... if (UseSoftConstraintNormalEquations) { //M = M.Multiply(M); M = M.Square(); // only works if M is symmetric } // construct packed version of M matrix PackedM = new PackedSparseMatrix(M); // compute laplacian vectors of initial mesh positions MLx = new double[N]; MLy = new double[N]; MLz = new double[N]; M.Multiply(Px, MLx); M.Multiply(Py, MLy); M.Multiply(Pz, MLz); // zero out... for (int i = 0; i < Px.Length; ++i) { MLx[i] = 0; MLy[i] = 0; MLz[i] = 0; } // allocate memory for internal buffers Preconditioner = new DiagonalMatrix(N); WeightsM = new DiagonalMatrix(N); Cx = new double[N]; Cy = new double[N]; Cz = new double[N]; Bx = new double[N]; By = new double[N]; Bz = new double[N]; Sx = new double[N]; Sy = new double[N]; Sz = new double[N]; UpdateForSolve(); }
public void Initialize() { ToMeshV = new int[Mesh.MaxVertexID]; ToIndex = new int[Mesh.MaxVertexID]; N = 0; foreach (int vid in Mesh.VertexIndices()) { ToMeshV[N] = vid; ToIndex[vid] = N; N++; } Px = new double[N]; Py = new double[N]; Pz = new double[N]; nbr_counts = new int[N]; SymmetricSparseMatrix M = new SymmetricSparseMatrix(); for (int i = 0; i < N; ++i) { int vid = ToMeshV[i]; Vector3d v = Mesh.GetVertex(vid); Px[i] = v.x; Py[i] = v.y; Pz[i] = v.z; nbr_counts[i] = Mesh.GetVtxEdgeCount(vid); } // construct laplacian matrix for (int i = 0; i < N; ++i) { int vid = ToMeshV[i]; int n = nbr_counts[i]; double sum_w = 0; foreach (int nbrvid in Mesh.VtxVerticesItr(vid)) { int j = ToIndex[nbrvid]; int n2 = nbr_counts[j]; // weight options //double w = -1; double w = -1.0 / Math.Sqrt(n + n2); //double w = -1.0 / n; M.Set(i, j, w); sum_w += w; } sum_w = -sum_w; // TODO: Investigate: is this ia bug? // Source https://github.com/ZelimDamian/geometry3Sharp/commit/7a50d8de10faad762e726e60956acc4bdc5456b5 // makes the following line M.Set(i, i, sum_w); M.Set(vid, vid, sum_w); } // transpose(L) * L, but matrix is symmetric... if (UseSoftConstraintNormalEquations) { //M = M.Multiply(M); // only works if M is symmetric!! PackedM = M.SquarePackedParallel(); } else { PackedM = new PackedSparseMatrix(M); } // compute laplacian vectors of initial mesh positions MLx = new double[N]; MLy = new double[N]; MLz = new double[N]; PackedM.Multiply(Px, MLx); PackedM.Multiply(Py, MLy); PackedM.Multiply(Pz, MLz); // allocate memory for internal buffers Preconditioner = new DiagonalMatrix(N); WeightsM = new DiagonalMatrix(N); Cx = new double[N]; Cy = new double[N]; Cz = new double[N]; Bx = new double[N]; By = new double[N]; Bz = new double[N]; Sx = new double[N]; Sy = new double[N]; Sz = new double[N]; need_solve_update = true; UpdateForSolve(); }
public void Initialize() { int NV = Curve.VertexCount; ToCurveV = new int[NV]; ToIndex = new int[NV]; N = 0; for (int k = 0; k < NV; k++) { int vid = k; ToCurveV[N] = vid; ToIndex[vid] = N; N++; } Px = new double[N]; Py = new double[N]; Pz = new double[N]; nbr_counts = new int[N]; SymmetricSparseMatrix M = new SymmetricSparseMatrix(); for (int i = 0; i < N; ++i) { int vid = ToCurveV[i]; Vector3d v = Curve.GetVertex(vid); Px[i] = v.x; Py[i] = v.y; Pz[i] = v.z; nbr_counts[i] = (i == 0 || i == N - 1) ? 1 : 2; } // construct laplacian matrix for (int i = 0; i < N; ++i) { int vid = ToCurveV[i]; int n = nbr_counts[i]; Index2i nbrs = Curve.Neighbours(vid); double sum_w = 0; for (int k = 0; k < 2; ++k) { int nbrvid = nbrs[k]; if (nbrvid == -1) { continue; } int j = ToIndex[nbrvid]; int n2 = nbr_counts[j]; // weight options double w = -1; //double w = -1.0 / Math.Sqrt(n + n2); //double w = -1.0 / n; M.Set(i, j, w); sum_w += w; } sum_w = -sum_w; M.Set(vid, vid, sum_w); } // transpose(L) * L, but matrix is symmetric... if (UseSoftConstraintNormalEquations) { //M = M.Multiply(M); // only works if M is symmetric!! PackedM = M.SquarePackedParallel(); } else { PackedM = new PackedSparseMatrix(M); } // compute laplacian vectors of initial mesh positions MLx = new double[N]; MLy = new double[N]; MLz = new double[N]; PackedM.Multiply(Px, MLx); PackedM.Multiply(Py, MLy); PackedM.Multiply(Pz, MLz); // allocate memory for internal buffers Preconditioner = new DiagonalMatrix(N); WeightsM = new DiagonalMatrix(N); Cx = new double[N]; Cy = new double[N]; Cz = new double[N]; Bx = new double[N]; By = new double[N]; Bz = new double[N]; Sx = new double[N]; Sy = new double[N]; Sz = new double[N]; need_solve_update = true; UpdateForSolve(); }