// Result must be as large as Mesh.MaxVertexID public bool SolveMultipleRHS(Vector3d[] Result) { if (WeightsM == null) { Initialize(); // force initialize... } UpdateForSolve(); // use initial positions as initial solution. double[][] B = BufferUtil.InitNxM(3, N, new double[][] { Bx, By, Bz }); double[][] X = BufferUtil.InitNxM(3, N, new double[][] { Px, Py, Pz }); Action <double[][], double[][]> CombinedMultiply = (Xt, Bt) => { PackedM.Multiply_Parallel_3(Xt, Bt); gParallel.ForEach(Interval1i.Range(3), (j) => { BufferUtil.MultiplyAdd(Bt[j], WeightsM.D, Xt[j]); }); }; var Solver = new SparseSymmetricCGMultipleRHS() { B = B, X = X, MultiplyF = CombinedMultiply, PreconditionMultiplyF = null, UseXAsInitialGuess = true }; bool ok = Solver.Solve(); if (ok == false) { return(false); } for (int i = 0; i < N; ++i) { int vid = ToMeshV[i]; Result[vid] = new Vector3d(X[0][i], X[1][i], X[2][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); }
// // DMesh3 construction utilities // /// <summary> /// ultimate generic mesh-builder, pass it arrays of floats/doubles, or lists /// of Vector3d, or anything in-between. Will figure out how to interpret /// /// This static function attempts to retain a manifold mesh, if you need finer /// control use the concrete class. /// /// Number of issues encountered adding verices or triangls are stored in the /// mesh metadata. Metadata can be cleared once the returning object is evaluated. /// </summary> public static DMesh3 Build <VType, TType, NType>(IEnumerable <VType> Vertices, IEnumerable <TType> Triangles, IEnumerable <NType> Normals = null, IEnumerable <int> TriGroups = null) { DMesh3 mesh = new DMesh3(Normals != null, false, false, TriGroups != null); // build outcomes are stored in the metadata to avoid changes to the function signature // int iAppendTriangleIssues = 0; Vector3d[] v = BufferUtil.ToVector3d(Vertices); for (int i = 0; i < v.Length; ++i) { mesh.AppendVertex(v[i]); } if (Normals != null) { Vector3f[] n = BufferUtil.ToVector3f(Normals); if (n.Length != v.Length) { throw new Exception("DMesh3Builder.Build: incorrect number of normals provided"); } for (int i = 0; i < n.Length; ++i) { mesh.SetVertexNormal(i, n[i]); } } Index3i[] t = BufferUtil.ToIndex3i(Triangles); for (int i = 0; i < t.Length; ++i) { var last = mesh.AppendTriangle(t[i]); if (last == DMesh3.InvalidID || last == DMesh3.NonManifoldID) { iAppendTriangleIssues++; } } if (TriGroups != null) { List <int> groups = new List <int>(TriGroups); if (groups.Count != t.Length) { throw new Exception("DMesh3Builder.Build: incorect number of triangle groups"); } for (int i = 0; i < t.Length; ++i) { mesh.SetTriangleGroup(i, groups[i]); } } mesh.AttachMetadata("AppendTriangleIssues", iAppendTriangleIssues); return(mesh); }
// // DMesh3 construction utilities // /// <summary> /// ultimate generic mesh-builder, pass it arrays of floats/doubles, or lists /// of Vector3d, or anything in-between. Will figure out how to interpret /// </summary> public static DMesh3 Build <VType, TType, NType>(IEnumerable <VType> Vertices, IEnumerable <TType> Triangles, IEnumerable <NType> Normals = null, IEnumerable <int> TriGroups = null) { var mesh = new DMesh3(Normals != null, false, false, TriGroups != null); Vector3d[] v = BufferUtil.ToVector3d(Vertices); for (int i = 0; i < v.Length; ++i) { mesh.AppendVertex(v[i]); } if (Normals != null) { Vector3f[] n = BufferUtil.ToVector3f(Normals); if (n.Length != v.Length) { throw new Exception("DMesh3Builder.Build: incorrect number of normals provided"); } for (int i = 0; i < n.Length; ++i) { mesh.SetVertexNormal(i, n[i]); } } Index3i[] t = BufferUtil.ToIndex3i(Triangles); for (int i = 0; i < t.Length; ++i) { mesh.AppendTriangle(t[i]); } if (TriGroups != null) { var groups = new List <int>(TriGroups); if (groups.Count != t.Length) { throw new Exception("DMesh3Builder.Build: incorect number of triangle groups"); } for (int i = 0; i < t.Length; ++i) { mesh.SetTriangleGroup(i, groups[i]); } } return(mesh); }
/// <summary> /// Find the set of boundary EdgeLoops. Note that if we encounter topological /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible) /// </summary> public bool Compute() { // This algorithm assumes that triangles are oriented consistently, // so closed boundary-loop can be followed by walking edges in-order Loops = new List <EdgeLoop>(); Spans = new List <EdgeSpan>(); // early-out if we don't actually have boundaries if (Mesh.CachedIsClosed) { return(true); } int NE = Mesh.MaxEdgeID; // Temporary memory used to indicate when we have "used" an edge. BitArray used_edge = new BitArray(NE); used_edge.SetAll(false); // current loop is stored here, cleared after each loop extracted List <int> loop_edges = new List <int>(); // [RMS] not sure we need this... List <int> loop_verts = new List <int>(); List <int> bowties = new List <int>(); // Temp buffer for reading back all boundary edges of a vertex. // probably always small but in pathological cases it could be large... int[] all_e = new int[16]; // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx? // process all edges of mesh for (int eid = 0; eid < NE; ++eid) { if (!Mesh.IsEdge(eid)) { continue; } if (used_edge[eid] == true) { continue; } if (Mesh.IsBoundaryEdge(eid) == false) { continue; } if (EdgeFilterF != null && EdgeFilterF(eid) == false) { used_edge[eid] = true; continue; } // ok this is start of a boundary chain int eStart = eid; used_edge[eStart] = true; loop_edges.Add(eStart); int eCur = eid; // follow the chain in order of oriented edges bool bClosed = false; bool bIsOpenSpan = false; while (!bClosed) { Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur); int cure_a = ev.a, cure_b = ev.b; if (bIsOpenSpan) { cure_a = ev.b; cure_b = ev.a; } else { loop_verts.Add(cure_a); } int e0 = -1, e1 = 1; int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1); // have to filter this list, if we are filtering. this is ugly. if (EdgeFilterF != null) { if (bdry_nbrs > 2) { if (bdry_nbrs >= all_e.Length) { all_e = new int[bdry_nbrs]; } // we may repreat this below...irritating... int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be); } else { if (EdgeFilterF(e0) == false) { bdry_nbrs--; } if (EdgeFilterF(e1) == false) { bdry_nbrs--; } } } if (bdry_nbrs < 2) // hit an 'endpoint' vertex (should only happen when Filter is on...) { if (SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b) { UnclosedLoop = true } } ; if (bIsOpenSpan) { bClosed = true; continue; } else { bIsOpenSpan = true; // begin open span eCur = loop_edges[0]; // restart at other end of loop loop_edges.Reverse(); // do this so we can push to front continue; } } int eNext = -1; if (bdry_nbrs > 2) { // found "bowtie" vertex...things just got complicated! if (cure_b == loop_verts[0]) { // The "end" of the current edge is the same as the start vertex. // This means we can close the loop here. Might as well! eNext = -2; // sentinel value used below } else { // try to find an unused outgoing edge that is oriented properly. // This could create sub-loops, we will handle those later if (bdry_nbrs >= all_e.Length) { all_e = new int[2 * bdry_nbrs]; } int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); Debug.Assert(num_be == bdry_nbrs); if (EdgeFilterF != null) { num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be); } // Try to pick the best "turn left" vertex. eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge); if (eNext == -1) { if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b) { BowtieFailure = true } } ; // ok, we are stuck. all we can do now is terminate this loop and keep it as a span if (bIsOpenSpan) { bClosed = true; } else { bIsOpenSpan = true; bClosed = true; } continue; } } if (bowties.Contains(cure_b) == false) { bowties.Add(cure_b); } } else { // walk forward to next available edge Debug.Assert(e0 == eCur || e1 == eCur); eNext = (e0 == eCur) ? e1 : e0; } if (eNext == -2) { // found a bowtie vert that is the same as start-of-loop, so we // are just closing it off explicitly bClosed = true; } else if (eNext == eStart) { // found edge at start of loop, so loop is done. bClosed = true; } else if (used_edge[eNext] != false) { // disaster case - the next edge is already used, but it is not the start of our loop // All we can do is convert to open span and terminate if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext) { RepeatedEdge = true } } ; bIsOpenSpan = true; bClosed = true; } else { // push onto accumulated list Debug.Assert(used_edge[eNext] == false); loop_edges.Add(eNext); used_edge[eNext] = true; eCur = eNext; } } if (bIsOpenSpan) { SawOpenSpans = true; if (SpanBehavior == SpanBehaviors.Compute) { loop_edges.Reverse(); // orient properly EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges); Spans.Add(span); } } else if (bowties.Count > 0) { // if we saw a bowtie vertex, we might need to break up this loop, // so call extract_subloops Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties); foreach (var loop in subloops.Loops) { Loops.Add(loop); } if (subloops.Spans.Count > 0) { FellBackToSpansOnFailure = true; foreach (var span in subloops.Spans) { Spans.Add(span); } } } else { // clean simple loop, convert to EdgeLoop instance EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = loop_verts.ToArray(); loop.Edges = loop_edges.ToArray(); Loops.Add(loop); } // reset these lists loop_edges.Clear(); loop_verts.Clear(); bowties.Clear(); } return(true); } // [TODO] cache this in a dictionary? we will not need very many, but we will // need each multiple times! Vector3d get_vtx_normal(int vid) { Vector3d n = Vector3d.Zero; foreach (int ti in Mesh.VtxTrianglesItr(vid)) { n += Mesh.GetTriNormal(ti); } n.Normalize(); return(n); } // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. // So, we compute the tangent plane at bowtie_v, and then the signed angle for each // viable edge in this plane. // // [TODO] handle degenerate edges. what do we do then? Currently will only chose // degenerate edge if there are no other options (I think...) int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges) { // compute normal and edge [a,bowtie] Vector3d n = get_vtx_normal(bowtie_v); int other_v = Mesh.edge_other_v(incoming_e, bowtie_v); Vector3d ab = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v); // our winner int best_e = -1; double best_angle = double.MaxValue; for (int i = 0; i < bdry_edges_count; ++i) { int bdry_eid = bdry_edges[i]; if (used_edges[bdry_eid] == true) { continue; // this edge is already used } Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid); if (bdry_ev.a != bowtie_v) { continue; // have to be able to chain to end of current edge, orientation-wise } // compute projected angle Vector3d bc = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v); float fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n); // turn left! if (best_angle == double.MaxValue || fAngleS < best_angle) { best_angle = fAngleS; best_e = bdry_eid; } } // [RMS] w/ bowtie vertices and open spans, this does happen //Debug.Assert(best_e != -1); return(best_e); }
/// <summary> /// Preconditioned variant /// Similar to non-preconditioned version, this can suffer if one solution converges /// much slower than others, as we can't skip matrix multiplies in that case. /// </summary> public bool SolvePreconditioned() { Iterations = 0; if (B == null || MultiplyF == null || PreconditionMultiplyF == null) { throw new Exception("SparseSymmetricCGMultipleRHS.SolvePreconditioned(): Must set B and MultiplyF and PreconditionMultiplyF!"); } int NRHS = B.Length; if (NRHS == 0) { throw new Exception("SparseSymmetricCGMultipleRHS.SolvePreconditioned(): Need at least one RHS vector in B"); } int n = B[0].Length; R = BufferUtil.AllocNxM(NRHS, n); P = BufferUtil.AllocNxM(NRHS, n); AP = BufferUtil.AllocNxM(NRHS, n); Z = BufferUtil.AllocNxM(NRHS, n); if (X == null || UseXAsInitialGuess == false) { if (X == null) { X = BufferUtil.AllocNxM(NRHS, n); } for (int j = 0; j < NRHS; ++j) { Array.Clear(X[j], 0, n); Array.Copy(B[j], R[j], n); } } else { // hopefully is X is a decent initialization... InitializeR(R); } // [RMS] for convergence test? double[] norm = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { norm[j] = BufferUtil.Dot(B[j], B[j]); } double[] root1 = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { root1[j] = Math.Sqrt(norm[j]); } // r_0 = b - A*x_0 MultiplyF(X, R); for (int j = 0; j < NRHS; ++j) { for (int i = 0; i < n; ++i) { R[j][i] = B[j][i] - R[j][i]; } } // z0 = M_inverse * r_0 PreconditionMultiplyF(R, Z); // p0 = z0 for (int j = 0; j < NRHS; ++j) { Array.Copy(Z[j], P[j], n); } // compute initial R*Z double[] RdotZ_k = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { RdotZ_k[j] = BufferUtil.Dot(R[j], Z[j]); } double[] alpha_k = new double[NRHS]; double[] beta_k = new double[NRHS]; bool[] converged = new bool[NRHS]; var rhs = Interval1i.Range(NRHS); int iter = 0; while (iter++ < MaxIterations) { // convergence test bool done = true; for (int j = 0; j < NRHS; ++j) { if (converged[j] == false) { double root0 = Math.Sqrt(RdotZ_k[j]); if (root0 <= ConvergeTolerance * root1[j]) { converged[j] = true; } } if (converged[j] == false) { done = false; } } if (done) { break; } MultiplyF(P, AP); gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { alpha_k[j] = RdotZ_k[j] / BufferUtil.Dot(P[j], AP[j]); } }); // x_k+1 = x_k + alpha_k * p_k gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { BufferUtil.MultiplyAdd(X[j], alpha_k[j], P[j]); } }); // r_k+1 = r_k - alpha_k * A * p_k gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { BufferUtil.MultiplyAdd(R[j], -alpha_k[j], AP[j]); } }); // z_k+1 = M_inverse * r_k+1 PreconditionMultiplyF(R, Z); // beta_k = (z_k+1 * r_k+1) / (z_k * r_k) gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { beta_k[j] = BufferUtil.Dot(Z[j], R[j]) / RdotZ_k[j]; } }); // can do these in parallel but improvement is minimal // p_k+1 = z_k+1 + beta_k * p_k gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { for (int i = 0; i < n; ++i) { P[j][i] = Z[j][i] + beta_k[j] * P[j][i]; } } }); gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { RdotZ_k[j] = BufferUtil.Dot(R[j], Z[j]); } }); } //System.Console.WriteLine("{0} iterations", iter); Iterations = iter; return(iter < MaxIterations); }
public bool Solve() { Iterations = 0; int size = B.Length; // Based on the algorithm in "Matrix Computations" by Golum and Van Loan. R = new double[size]; P = new double[size]; AP = new double[size]; if (X == null || UseXAsInitialGuess == false) { if (X == null) { X = new double[size]; } Array.Clear(X, 0, X.Length); Array.Copy(B, R, B.Length); } else { // hopefully is X is a decent initialization... InitializeR(R); } // [RMS] these were inside loop but they are constant! double norm = BufferUtil.Dot(B, B); double root1 = Math.Sqrt(norm); // The first iteration. double rho0 = BufferUtil.Dot(R, R); // [RMS] If we were initialized w/ constraints already satisfied, // then we are done! (happens for example in mesh deformations) if (rho0 < MathUtil.ZeroTolerance * root1) { return(true); } Array.Copy(R, P, R.Length); MultiplyF(P, AP); double alpha = rho0 / BufferUtil.Dot(P, AP); BufferUtil.MultiplyAdd(X, alpha, P); BufferUtil.MultiplyAdd(R, -alpha, AP); double rho1 = BufferUtil.Dot(R, R); // The remaining iterations. int iter; for (iter = 1; iter < MaxIterations; ++iter) { double root0 = Math.Sqrt(rho1); if (root0 <= MathUtil.ZeroTolerance * root1) { break; } double beta = rho1 / rho0; UpdateP(P, beta, R); MultiplyF(P, AP); alpha = rho1 / BufferUtil.Dot(P, AP); // can compute these two steps simultaneously double RdotR = 0; gParallel.Evaluate( () => { BufferUtil.MultiplyAdd(X, alpha, P); }, () => { RdotR = BufferUtil.MultiplyAdd_GetSqrSum(R, -alpha, AP); } ); rho0 = rho1; rho1 = RdotR; // BufferUtil.Dot(R, R); } //System.Console.WriteLine("{0} iterations", iter); Iterations = iter; return(iter < MaxIterations); }
/// <summary> /// standard CG solve /// </summary> public bool Solve() { Iterations = 0; if (B == null || MultiplyF == null) { throw new Exception("SparseSymmetricCGMultipleRHS.Solve(): Must set B and MultiplyF!"); } int NRHS = B.Length; if (NRHS == 0) { throw new Exception("SparseSymmetricCGMultipleRHS.Solve(): Need at least one RHS vector in B"); } int size = B[0].Length; // Based on the algorithm in "Matrix Computations" by Golum and Van Loan. R = BufferUtil.AllocNxM(NRHS, size); P = BufferUtil.AllocNxM(NRHS, size); W = BufferUtil.AllocNxM(NRHS, size); if (X == null || UseXAsInitialGuess == false) { if (X == null) { X = BufferUtil.AllocNxM(NRHS, size); } for (int j = 0; j < NRHS; ++j) { Array.Clear(X[j], 0, size); Array.Copy(B[j], R[j], size); } } else { // hopefully is X is a decent initialization... InitializeR(R); } // [RMS] these were inside loop but they are constant! double[] norm = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { norm[j] = BufferUtil.Dot(B[j], B[j]); } double[] root1 = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { root1[j] = Math.Sqrt(norm[j]); } // The first iteration. double[] rho0 = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { rho0[j] = BufferUtil.Dot(R[j], R[j]); } // [RMS] If we were initialized w/ constraints already satisfied, // then we are done! (happens for example in mesh deformations) bool[] converged = new bool[NRHS]; int nconverged = 0; for (int j = 0; j < NRHS; ++j) { converged[j] = rho0[j] < (ConvergeTolerance * root1[j]); if (converged[j]) { nconverged++; } } if (nconverged == NRHS) { return(true); } for (int j = 0; j < NRHS; ++j) { Array.Copy(R[j], P[j], size); } MultiplyF(P, W); double[] alpha = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { alpha[j] = rho0[j] / BufferUtil.Dot(P[j], W[j]); } for (int j = 0; j < NRHS; ++j) { BufferUtil.MultiplyAdd(X[j], alpha[j], P[j]); } for (int j = 0; j < NRHS; ++j) { BufferUtil.MultiplyAdd(R[j], -alpha[j], W[j]); } double[] rho1 = new double[NRHS]; for (int j = 0; j < NRHS; ++j) { rho1[j] = BufferUtil.Dot(R[j], R[j]); } double[] beta = new double[NRHS]; var rhs = Interval1i.Range(NRHS); // The remaining iterations. int iter; for (iter = 1; iter < MaxIterations; ++iter) { bool done = true; for (int j = 0; j < NRHS; ++j) { if (converged[j] == false) { double root0 = Math.Sqrt(rho1[j]); if (root0 <= ConvergeTolerance * root1[j]) { converged[j] = true; } } if (converged[j] == false) { done = false; } } if (done) { break; } for (int j = 0; j < NRHS; ++j) { beta[j] = rho1[j] / rho0[j]; } UpdateP(P, beta, R, converged); MultiplyF(P, W); gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { alpha[j] = rho1[j] / BufferUtil.Dot(P[j], W[j]); } }); // can do all these in parallel, but improvement is minimal gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { BufferUtil.MultiplyAdd(X[j], alpha[j], P[j]); } }); gParallel.ForEach(rhs, (j) => { if (converged[j] == false) { rho0[j] = rho1[j]; rho1[j] = BufferUtil.MultiplyAdd_GetSqrSum(R[j], -alpha[j], W[j]); } }); } //System.Console.WriteLine("{0} iterations", iter); Iterations = iter; return(iter < MaxIterations); }
public bool SolvePreconditioned() { Iterations = 0; int n = B.Length; R = new double[n]; P = new double[n]; AP = new double[n]; Z = new double[n]; if (X == null || UseXAsInitialGuess == false) { if (X == null) { X = new double[n]; } Array.Clear(X, 0, X.Length); Array.Copy(B, R, B.Length); } else { // hopefully is X is a decent initialization... InitializeR(R); } // [RMS] for convergence test? double norm = BufferUtil.Dot(B, B); double root1 = Math.Sqrt(norm); // r_0 = b - A*x_0 MultiplyF(X, R); for (int i = 0; i < n; ++i) { R[i] = B[i] - R[i]; } // z0 = M_inverse * r_0 PreconditionMultiplyF(R, Z); // p0 = z0 Array.Copy(Z, P, n); double RdotZ_k = BufferUtil.Dot(R, Z); int iter = 0; while (iter++ < MaxIterations) { // convergence test double root0 = Math.Sqrt(RdotZ_k); if (root0 <= MathUtil.ZeroTolerance * root1) { break; } MultiplyF(P, AP); double alpha_k = RdotZ_k / BufferUtil.Dot(P, AP); gParallel.Evaluate( // x_k+1 = x_k + alpha_k * p_k () => { BufferUtil.MultiplyAdd(X, alpha_k, P); }, // r_k+1 = r_k - alpha_k * A * p_k () => { BufferUtil.MultiplyAdd(R, -alpha_k, AP); } ); // z_k+1 = M_inverse * r_k+1 PreconditionMultiplyF(R, Z); // beta_k = (z_k+1 * r_k+1) / (z_k * r_k) double beta_k = BufferUtil.Dot(Z, R) / RdotZ_k; gParallel.Evaluate( // p_k+1 = z_k+1 + beta_k * p_k () => { for (int i = 0; i < n; ++i) { P[i] = Z[i] + beta_k * P[i]; } }, () => { RdotZ_k = BufferUtil.Dot(R, Z); } ); } //System.Console.WriteLine("{0} iterations", iter); Iterations = iter; return(iter < MaxIterations); }
/// <summary> /// Find the set of boundary EdgeLoops. Note that if we encounter topological /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible) /// </summary> public bool Compute() { // This algorithm assumes that triangles are oriented consistently, // so closed boundary-loop can be followed by walking edges in-order Loops = new List <EdgeLoop>(); Spans = new List <EdgeSpan>(); int NE = Mesh.MaxEdgeID; // Temporary memory used to indicate when we have "used" an edge. BitArray used_edge = new BitArray(NE); used_edge.SetAll(false); // current loop is stored here, cleared after each loop extracted List <int> loop_edges = new List <int>(); // [RMS] not sure we need this... List <int> loop_verts = new List <int>(); List <int> bowties = new List <int>(); // Temp buffer for reading back all boundary edges of a vertex. // probably always small but in pathological cases it could be large... int[] all_e = new int[16]; // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx? // process all edges of mesh for (int eid = 0; eid < NE; ++eid) { if (!Mesh.IsEdge(eid)) { continue; } if (used_edge[eid] == true) { continue; } if (Mesh.IsBoundaryEdge(eid) == false) { continue; } if (EdgeFilterF != null && EdgeFilterF(eid) == false) { used_edge[eid] = true; continue; } // ok this is start of a boundary chain int eStart = eid; used_edge[eStart] = true; loop_edges.Add(eStart); int eCur = eid; // follow the chain in order of oriented edges bool bClosed = false; bool bIsOpenSpan = false; while (!bClosed) { Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur); int cure_a = ev.a, cure_b = ev.b; if (bIsOpenSpan) { cure_a = ev.b; cure_b = ev.a; } else { loop_verts.Add(cure_a); } int e0 = -1, e1 = 1; int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1); // have to filter this list, if we are filtering. this is ugly. if (EdgeFilterF != null) { if (bdry_nbrs > 2) { if (bdry_nbrs >= all_e.Length) { all_e = new int[bdry_nbrs]; } // we may repreat this below...irritating... int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be); } else { if (EdgeFilterF(e0) == false) { bdry_nbrs--; } if (EdgeFilterF(e1) == false) { bdry_nbrs--; } } } if (bdry_nbrs < 2) // hit an 'endpoint' vertex (should only happen when Filter is on...) { if (SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b) { UnclosedLoop = true } } ; if (bIsOpenSpan) { bClosed = true; continue; } else { bIsOpenSpan = true; // begin open span eCur = loop_edges[0]; // restart at other end of loop loop_edges.Reverse(); // do this so we can push to front continue; } } int eNext = -1; if (bdry_nbrs > 2) { // found "bowtie" vertex...things just got complicated! if (cure_b == loop_verts[0]) { // The "end" of the current edge is the same as the start vertex. // This means we can close the loop here. Might as well! eNext = -2; // sentinel value used below } else { // try to find an unused outgoing edge that is oriented properly. // This could create sub-loops, we will handle those later if (bdry_nbrs >= all_e.Length) { all_e = new int[2 * bdry_nbrs]; } int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); Debug.Assert(num_be == bdry_nbrs); if (EdgeFilterF != null) { num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be); } // Try to pick the best "turn left" vertex. eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge); if (eNext == -1) { if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b) { BowtieFailure = true } } ; // ok, we are stuck. all we can do now is terminate this loop and keep it as a span if (bIsOpenSpan) { bClosed = true; } else { bIsOpenSpan = true; bClosed = true; } continue; } } if (bowties.Contains(cure_b) == false) { bowties.Add(cure_b); } } else { // walk forward to next available edge Debug.Assert(e0 == eCur || e1 == eCur); eNext = (e0 == eCur) ? e1 : e0; } if (eNext == -2) { // found a bowtie vert that is the same as start-of-loop, so we // are just closing it off explicitly bClosed = true; } else if (eNext == eStart) { // found edge at start of loop, so loop is done. bClosed = true; } else if (used_edge[eNext] != false) { // disaster case - the next edge is already used, but it is not the start of our loop // All we can do is convert to open span and terminate if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext) { RepeatedEdge = true } } ; bIsOpenSpan = true; bClosed = true; } else { // push onto accumulated list Debug.Assert(used_edge[eNext] == false); loop_edges.Add(eNext); used_edge[eNext] = true; eCur = eNext; } } if (bIsOpenSpan) { SawOpenSpans = true; if (SpanBehavior == SpanBehaviors.Compute) { loop_edges.Reverse(); // orient properly EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges); Spans.Add(span); } } else if (bowties.Count > 0) { // if we saw a bowtie vertex, we might need to break up this loop, // so call extract_subloops List <EdgeLoop> subloops = extract_subloops(loop_verts, loop_edges, bowties); for (int i = 0; i < subloops.Count; ++i) { Loops.Add(subloops[i]); } } else { // clean simple loop, convert to EdgeLoop instance EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = loop_verts.ToArray(); loop.Edges = loop_edges.ToArray(); Loops.Add(loop); } // reset these lists loop_edges.Clear(); loop_verts.Clear(); bowties.Clear(); } return(true); } // [TODO] cache this in a dictionary? we will not need very many, but we will // need each multiple times! Vector3d get_vtx_normal(int vid) { Vector3d n = Vector3d.Zero; foreach (int ti in Mesh.VtxTrianglesItr(vid)) { n += Mesh.GetTriNormal(ti); } n.Normalize(); return(n); } // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. // So, we compute the tangent plane at bowtie_v, and then the signed angle for each // viable edge in this plane. // // [TODO] handle degenerate edges. what do we do then? Currently will only chose // degenerate edge if there are no other options (I think...) int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges) { // compute normal and edge [a,bowtie] Vector3d n = get_vtx_normal(bowtie_v); int other_v = Mesh.edge_other_v(incoming_e, bowtie_v); Vector3d ab = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v); // our winner int best_e = -1; double best_angle = double.MaxValue; for (int i = 0; i < bdry_edges_count; ++i) { int bdry_eid = bdry_edges[i]; if (used_edges[bdry_eid] == true) { continue; // this edge is already used } Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid); if (bdry_ev.a != bowtie_v) { continue; // have to be able to chain to end of current edge, orientation-wise } // compute projected angle Vector3d bc = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v); float fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n); // turn left! if (best_angle == double.MaxValue || fAngleS < best_angle) { best_angle = fAngleS; best_e = bdry_eid; } } // [RMS] w/ bowtie vertices and open spans, this does happen //Debug.Assert(best_e != -1); return(best_e); } // This is called when loopV contains one or more "bowtie" vertices. // These vertices *might* be duplicated in loopV (but not necessarily) // If they are, we have to break loopV into subloops that don't contain duplicates. // // The list bowties contains all the possible duplicates // (all v in bowties occur in loopV at least once) // // Currently loopE is not used, and the returned EdgeLoop objects do not have their Edges // arrays initialized. Perhaps to improve in future. List <EdgeLoop> extract_subloops(List <int> loopV, List <int> loopE, List <int> bowties) { List <EdgeLoop> subs = new List <EdgeLoop>(); // figure out which bowties we saw are actually duplicated in loopV List <int> dupes = new List <int>(); foreach (int bv in bowties) { if (count_in_list(loopV, bv) > 1) { dupes.Add(bv); } } // we might not actually have any duplicates, if we got luck. Early out in that case if (dupes.Count == 0) { subs.Add(new EdgeLoop(Mesh) { Vertices = loopV.ToArray(), Edges = loopE.ToArray(), BowtieVertices = bowties.ToArray() }); return(subs); } // This loop extracts subloops until we have dealt with all the // duplicate vertices in loopV while (dupes.Count > 0) { // Find shortest "simple" loop, ie a loop from a bowtie to itself that // does not contain any other bowties. This is an independent loop. // We're doing a lot of extra work here if we only have one element in dupes... int bi = 0, bv = 0; int start_i = -1, end_i = -1; int bv_shortest = -1; int shortest = int.MaxValue; for ( ; bi < dupes.Count; ++bi) { bv = dupes[bi]; if (is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i)) { int len = count_span(loopV, start_i, end_i); if (len < shortest) { bv_shortest = bv; shortest = len; } } } if (bv_shortest == -1) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: Cannot find a valid simple loop"); } if (bv != bv_shortest) { bv = bv_shortest; // running again just to get start_i and end_i... is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i); } Debug.Assert(loopV[start_i] == bv && loopV[end_i] == bv); EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = extract_span(loopV, start_i, end_i, true); loop.Edges = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices); loop.BowtieVertices = bowties.ToArray(); subs.Add(loop); // If there are no more duplicates of this bowtie, we can treat // it like a regular vertex now if (count_in_list(loopV, bv) < 2) { dupes.Remove(bv); } } // Should have one loop left that contains duplicates. // Extract this as a separate loop int nLeft = 0; for (int i = 0; i < loopV.Count; ++i) { if (loopV[i] != -1) { nLeft++; } } if (nLeft > 0) { EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = new int[nLeft]; int vi = 0; for (int i = 0; i < loopV.Count; ++i) { if (loopV[i] != -1) { loop.Vertices[vi++] = loopV[i]; } } loop.Edges = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices); loop.BowtieVertices = bowties.ToArray(); subs.Add(loop); } return(subs); } /* * In all the functions below, the list loopV is assumed to possibly * contain "removed" vertices indicated by -1. These are ignored. */ // Check if the loop from bowtieV to bowtieV inside loopV contains any other bowtie verts. // Also returns start and end indices in loopV of "clean" loop // Note that start may be < end, if the "clean" loop wraps around the end bool is_simple_bowtie_loop(List <int> loopV, List <int> bowties, int bowtieV, out int start_i, out int end_i) { // find two indices of bowtie vert start_i = find_index(loopV, 0, bowtieV); end_i = find_index(loopV, start_i + 1, bowtieV); if (is_simple_path(loopV, bowties, bowtieV, start_i, end_i)) { return(true); } else if (is_simple_path(loopV, bowties, bowtieV, end_i, start_i)) { int tmp = start_i; start_i = end_i; end_i = tmp; return(true); } else { return(false); // not a simple bowtie loop! } } // check if forward path from loopV[i1] to loopV[i2] contains any bowtie verts other than bowtieV bool is_simple_path(List <int> loopV, List <int> bowties, int bowtieV, int i1, int i2) { int N = loopV.Count; for (int i = i1; i != i2; i = (i + 1) % N) { int vi = loopV[i]; if (vi == -1) { continue; // skip removed vertices } if (vi != bowtieV && bowties.Contains(vi)) { return(false); } } return(true); } // Read out the span from loop[i0] to loop [i1-1] into an array. // If bMarkInvalid, then these values are set to -1 in loop int[] extract_span(List <int> loop, int i0, int i1, bool bMarkInvalid) { int num = count_span(loop, i0, i1); int[] a = new int[num]; int ai = 0; int N = loop.Count; for (int i = i0; i != i1; i = (i + 1) % N) { if (loop[i] != -1) { a[ai++] = loop[i]; if (bMarkInvalid) { loop[i] = -1; } } } return(a); } // count number of valid vertices in l between loop[i0] and loop[i1-1] int count_span(List <int> l, int i0, int i1) { int c = 0; int N = l.Count; for (int i = i0; i != i1; i = (i + 1) % N) { if (l[i] != -1) { c++; } } return(c); } // find the index of item in loop, starting at start index int find_index(List <int> loop, int start, int item) { for (int i = start; i < loop.Count; ++i) { if (loop[i] == item) { return(i); } } return(-1); } // count number of times item appears in loop int count_in_list(List <int> loop, int item) { int c = 0; for (int i = 0; i < loop.Count; ++i) { if (loop[i] == item) { c++; } } return(c); } } }
/// <summary> /// Find the set of boundary EdgeLoops. Note that if we encounter topological /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible) /// </summary> public bool Compute() { // This algorithm assumes that triangles are oriented consistently, // so closed boundary-loop can be followed by walking edges in-order Loops = new List <EdgeLoop>(); Spans = new List <EdgeSpan>(); // early-out if we don't actually have boundaries if (Mesh.CachedIsClosed) { return(true); } int NE = Mesh.MaxEdgeID; // Temporary memory used to indicate when we have "used" an edge. var used_edge = new BitArray(NE); used_edge.SetAll(false); // current loop is stored here, cleared after each loop extracted var loop_edges = new List <int>(); // [RMS] not sure we need this... var loop_verts = new List <int>(); var bowties = new List <int>(); // Temp buffer for reading back all boundary edges of a vertex. // probably always small but in pathological cases it could be large... int[] all_e = new int[16]; // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx? // process all edges of mesh for (int eid = 0; eid < NE; ++eid) { if (!Mesh.IsEdge(eid)) { continue; } if (used_edge[eid] == true) { continue; } if (Mesh.IsBoundaryEdge(eid) == false) { continue; } if (EdgeFilterF != null && EdgeFilterF(eid) == false) { used_edge[eid] = true; continue; } // ok this is start of a boundary chain int eStart = eid; used_edge[eStart] = true; loop_edges.Add(eStart); int eCur = eid; // follow the chain in order of oriented edges bool bClosed = false; bool bIsOpenSpan = false; while (!bClosed) { Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur); int cure_a = ev.a, cure_b = ev.b; if (bIsOpenSpan) { cure_a = ev.b; cure_b = ev.a; } else { loop_verts.Add(cure_a); } int e0 = -1, e1 = 1; int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1); // have to filter this list, if we are filtering. this is ugly. if (EdgeFilterF != null) { if (bdry_nbrs > 2) { if (bdry_nbrs >= all_e.Length) { all_e = new int[bdry_nbrs]; } // we may repreat this below...irritating... int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be); } else { if (EdgeFilterF(e0) == false) { bdry_nbrs--; } if (EdgeFilterF(e1) == false) { bdry_nbrs--; } } } if (bdry_nbrs < 2) { // hit an 'endpoint' vertex (should only happen when Filter is on...) if (SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b) { UnclosedLoop = true }; } if (bIsOpenSpan) { bClosed = true; continue; } else { bIsOpenSpan = true; // begin open span eCur = loop_edges[0]; // restart at other end of loop loop_edges.Reverse(); // do this so we can push to front continue; } } int eNext = -1; if (bdry_nbrs > 2) { // found "bowtie" vertex...things just got complicated! if (cure_b == loop_verts[0]) { // The "end" of the current edge is the same as the start vertex. // This means we can close the loop here. Might as well! eNext = -2; // sentinel value used below } else { // try to find an unused outgoing edge that is oriented properly. // This could create sub-loops, we will handle those later if (bdry_nbrs >= all_e.Length) { all_e = new int[2 * bdry_nbrs]; } int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); Debug.Assert(num_be == bdry_nbrs); if (EdgeFilterF != null) { num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be); } // Try to pick the best "turn left" vertex. eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge); if (eNext == -1) { if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b) { BowtieFailure = true }; } // ok, we are stuck. all we can do now is terminate this loop and keep it as a span if (bIsOpenSpan) { bClosed = true; } else { bIsOpenSpan = true; bClosed = true; } continue; } } if (bowties.Contains(cure_b) == false) { bowties.Add(cure_b); } } else { // walk forward to next available edge Debug.Assert(e0 == eCur || e1 == eCur); eNext = (e0 == eCur) ? e1 : e0; } if (eNext == -2) { // found a bowtie vert that is the same as start-of-loop, so we // are just closing it off explicitly bClosed = true; } else if (eNext == eStart) { // found edge at start of loop, so loop is done. bClosed = true; } else if (used_edge[eNext] != false) { // disaster case - the next edge is already used, but it is not the start of our loop // All we can do is convert to open span and terminate if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext) { RepeatedEdge = true }; } bIsOpenSpan = true; bClosed = true; } else { // push onto accumulated list Debug.Assert(used_edge[eNext] == false); loop_edges.Add(eNext); used_edge[eNext] = true; eCur = eNext; } } if (bIsOpenSpan) { SawOpenSpans = true; if (SpanBehavior == SpanBehaviors.Compute) { loop_edges.Reverse(); // orient properly var span = EdgeSpan.FromEdges(Mesh, loop_edges); Spans.Add(span); } } else if (bowties.Count > 0) { // if we saw a bowtie vertex, we might need to break up this loop, // so call extract_subloops Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties); foreach (var loop in subloops.Loops) { Loops.Add(loop); } if (subloops.Spans.Count > 0) { FellBackToSpansOnFailure = true; foreach (var span in subloops.Spans) { Spans.Add(span); } } } else { // clean simple loop, convert to EdgeLoop instance var loop = new EdgeLoop(Mesh); loop.Vertices = loop_verts.ToArray(); loop.Edges = loop_edges.ToArray(); Loops.Add(loop); } // reset these lists loop_edges.Clear(); loop_verts.Clear(); bowties.Clear(); } return(true); }
/// <summary> /// Similar to the static <see cref="Build()"/> method below, but uses the /// <see cref="NonManifoldTriBehavior"/> and <see cref="AddTriangleFailBehaviors"/> properties /// to affect the meshing process and avoids exceptions, preferring feedback in the mesh metadata. /// </summary> public DMesh3 AppendMesh <VType, TType, NType>(IEnumerable <VType> Vertices, IEnumerable <TType> Triangles, IEnumerable <NType> Normals = null, IEnumerable <int> TriGroups = null) { // build outcomes are stored in the metadata to keep the function signature like the static method Build // int iAppendTriangleIssues = 0; bool addNormals = Normals != null; string NormalsMetadata = "None"; bool addTriGroups = TriGroups != null; string TriGroupsMetadata = "None"; // data preparation Vector3d[] v = BufferUtil.ToVector3d(Vertices); Vector3f[] n = null; if (addNormals) { n = BufferUtil.ToVector3f(Normals); if (n.Length != v.Length) { NormalsMetadata = "Error: incorrect number of normals provided, ignored."; addNormals = false; } } Index3i[] t = BufferUtil.ToIndex3i(Triangles); List <int> groups = null; if (addTriGroups) { groups = new List <int>(TriGroups); if (groups.Count != t.Length) { TriGroupsMetadata = "Error: incorrect number of groups provided, ignored."; addTriGroups = false; } } DMesh3 mesh = new DMesh3(addNormals, false, false, addTriGroups); AppendNewMesh(mesh); // vertices for (int i = 0; i < v.Length; ++i) { mesh.AppendVertex(v[i]); } // normals if (addNormals) { for (int i = 0; i < n.Length; ++i) { mesh.SetVertexNormal(i, n[i]); } NormalsMetadata = "Ok"; } // triangles for (int i = 0; i < t.Length; ++i) { var last = AppendTriangle(t[i]); if (last == DMesh3.InvalidID || last == DMesh3.NonManifoldID) { iAppendTriangleIssues++; } } // groups if (addTriGroups) { for (int i = 0; i < t.Length; ++i) { mesh.SetTriangleGroup(i, groups[i]); } TriGroupsMetadata = "Ok"; } // adding the metadata // mesh.AttachMetadata("AppendTriangleIssues", iAppendTriangleIssues); mesh.AttachMetadata("Normals", NormalsMetadata); mesh.AttachMetadata("TriGroups", TriGroupsMetadata); return(mesh); }
public bool Solve() { int size = B.Length; // Based on the algorithm in "Matrix Computations" by Golum and Van Loan. R = new double[size]; P = new double[size]; W = new double[size]; if (X == null || UseXAsInitialGuess == false) { if (X == null) { X = new double[size]; } Array.Clear(X, 0, X.Length); Array.Copy(B, R, B.Length); } else { // hopefully is X is a decent initialization... InitializeR(R); } // The first iteration. double rho0 = BufferUtil.Dot(R, R); Array.Copy(R, P, R.Length); MultiplyF(P, W); double alpha = rho0 / BufferUtil.Dot(P, W); BufferUtil.MultiplyAdd(X, alpha, P); BufferUtil.MultiplyAdd(R, -alpha, W); double rho1 = BufferUtil.Dot(R, R); // [RMS] these were inside loop but they are constant! double norm = BufferUtil.Dot(B, B); double root1 = Math.Sqrt(norm); // The remaining iterations. int iter; for (iter = 1; iter < MaxIterations; ++iter) { double root0 = Math.Sqrt(rho1); if (root0 <= MathUtil.ZeroTolerance * root1) { break; } double beta = rho1 / rho0; UpdateP(P, beta, R); MultiplyF(P, W); alpha = rho1 / BufferUtil.Dot(P, W); BufferUtil.MultiplyAdd(X, alpha, P); BufferUtil.MultiplyAdd(R, -alpha, W); rho0 = rho1; rho1 = BufferUtil.Dot(R, R); } System.Console.WriteLine("{0} iterations", iter); return(iter < MaxIterations); }