/// <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) > math.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) > math.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); }
// returns this*this (requires less memory) public SymmetricSparseMatrix Square(bool bParallel = true) { SymmetricSparseMatrix R = new SymmetricSparseMatrix(); PackedSparseMatrix 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) > math.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) > math.MathUtil.ZeroTolerance) { R[r1i, c2i] = v; } } } } return(R); }
/// <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++; } }