private static UncertainValue Integrate_Adaptive(MultiFunctor f, CoordinateTransform[] map, IntegrationRegion r, EvaluationSettings settings) { // Create an evaluation rule GenzMalik75Rule rule = new GenzMalik75Rule(r.Dimension); // Use it on the whole region and put the answer in a linked list rule.Evaluate(f, map, r); LinkedList<IntegrationRegion> regionList = new LinkedList<IntegrationRegion>(); regionList.AddFirst(r); // Iterate until convergence while (f.EvaluationCount < settings.EvaluationBudget) { // Add up value and errors in all regions. // While we are at it, take note of the region with the largest error. double value = 0.0; double error = 0.0; LinkedListNode<IntegrationRegion> regionNode = regionList.First; double maxError = 0.0; LinkedListNode<IntegrationRegion> maxErrorNode = null; while (regionNode != null) { IntegrationRegion region = regionNode.Value; value += region.Value; error += region.Error; //error += MoreMath.Sqr(region.Error); if (region.Error > maxError) { maxError = region.Error; maxErrorNode = regionNode; } regionNode = regionNode.Next; } // Check for convergence. if ((error <= settings.AbsolutePrecision) || (error <= settings.RelativePrecision * Math.Abs(value))) { return (new UncertainValue(value, error)); } // Split the region with the largest error, and evaluate each subregion. IntegrationRegion maxErrorRegion = maxErrorNode.Value; regionList.Remove(maxErrorNode); IList<IntegrationRegion> subRegions = maxErrorRegion.Split(maxErrorRegion.SplitIndex); /* Countdown cnt = new Countdown(2); ThreadPool.QueueUserWorkItem((object state) => { rule.Evaluate(f, subRegions[0]); cnt.Signal(); }); ThreadPool.QueueUserWorkItem((object state) => { rule.Evaluate(f, subRegions[1]); cnt.Signal(); }); cnt.Wait(); foreach (IntegrationRegion subRegion in subRegions) { regionList.AddLast(subRegion); } */ foreach (IntegrationRegion subRegion in subRegions) { rule.Evaluate(f, map, subRegion); regionList.AddLast(subRegion); } } throw new NonconvergenceException(); }
/// <summary> /// Estimates a multi-dimensional integral using the given evaluation settings. /// </summary> /// <param name="function">The function to be integrated.</param> /// <param name="volume">The volume over which to integrate.</param> /// <param name="settings">The integration settings.</param> /// <returns>A numerical estimate of the multi-dimensional integral.</returns> /// <remarks> /// <para>Note that the integration function must not attempt to modify the argument passed to it.</para> /// <para>Note that the integration volume must be a hyper-rectangle. You can integrate over regions with more complex boundaries by specifying the integration /// volume as a bounding hyper-rectangle that encloses your desired integration region, and returing the value 0 for the integrand outside of the desired integration /// region. For example, to find the volume of a unit d-sphere, you can integrate a function that is 1 inside the unit d-sphere and 0 outside it over the volume /// [-1,1]<sup>d</sup>. You can integrate over infinite volumes by specifing volume endpoints of <see cref="Double.PositiveInfinity"/> and/or /// <see cref="Double.NegativeInfinity"/>. Volumes with dimension greater than 15 are not currently supported.</para> /// <para>Integrals with hard boundaries (like our hyper-sphere volume problem) typically require more evaluations than integrals of smooth functions to achieve the same accuracy. /// Integrals with canceling positive and negative contributions also typically require more evaluations than integtrals of purely positive functions.</para> /// <para>Numerical multi-dimensional integration is computationally expensive. To make problems more tractable, keep in mind some rules of thumb:</para> /// <ul> /// <li>Reduce the required accuracy to the minimum required. Alternatively, if you are willing to wait longer, increase the evaluation budget.</li> /// <li>Exploit symmetries of the problem to reduce the integration volume. For example, to compute the volume of the unit d-sphere, it is better to /// integrate over [0,1]<sup>d</sup> and multiply the result by 2<sup>d</sup> than to simply integrate over [-1,1]<sup>d</sup>.</li> /// <li>Apply analytic techniques to reduce the dimension of the integral. For example, when computing the volume of the unit d-sphere, it is better /// to do a (d-1)-dimesional integral over the function that is the height of the sphere in the dth dimension than to do a d-dimensional integral over the /// indicator function that is 1 inside the sphere and 0 outside it.</li> /// </ul> /// </remarks> /// <exception cref="ArgumentException"><paramref name="function"/>, <paramref name="volume"/>, or <paramref name="settings"/> are null, or /// the dimension of <paramref name="volume"/> is larger than 15.</exception> /// <exception cref="NonconvergenceException">The prescribed accuracy could not be achieved with the given evaluation budget.</exception> public static IntegrationResult Integrate(Func <IList <double>, double> function, IList <Interval> volume, IntegrationSettings settings) { if (function == null) { throw new ArgumentNullException(nameof(function)); } if (volume == null) { throw new ArgumentNullException(nameof(volume)); } if (settings == null) { throw new ArgumentNullException(nameof(settings)); } // Get the dimension from the box int d = volume.Count; settings = SetMultiIntegrationDefaults(settings, d); // Translate the integration volume, which may be infinite, into a bounding box plus a coordinate transform. Interval[] box = new Interval[d]; CoordinateTransform[] map = new CoordinateTransform[d]; for (int i = 0; i < d; i++) { Interval limits = volume[i]; if (Double.IsInfinity(limits.RightEndpoint)) { if (Double.IsInfinity(limits.LeftEndpoint)) { // -\infinity to +\infinity box[i] = Interval.FromEndpoints(-1.0, +1.0); map[i] = new TangentCoordinateTransform(0.0); } else { // a to +\infinity box[i] = Interval.FromEndpoints(0.0, 1.0); map[i] = new TangentCoordinateTransform(limits.LeftEndpoint); } } else { if (Double.IsInfinity(limits.LeftEndpoint)) { // -\infinity to b box[i] = Interval.FromEndpoints(-1.0, 0.0); map[i] = new TangentCoordinateTransform(limits.RightEndpoint); } else { // a to b box[i] = limits; map[i] = new IdentityCoordinateTransform(); } } } // Use adaptive cubature for small dimensions, Monte-Carlo for large dimensions. MultiFunctor f = new MultiFunctor(function); f.IgnoreInfinity = true; f.IgnoreNaN = true; UncertainValue estimate; if (d < 1) { throw new ArgumentException("The dimension of the integration volume must be at least 1.", "volume"); } else if (d < 4) { IntegrationRegion r = new IntegrationRegion(box); estimate = Integrate_Adaptive(f, map, r, settings); } else if (d < 16) { estimate = Integrate_MonteCarlo(f, map, box, settings); } else { throw new ArgumentException("The dimension of the integrtion volume must be less than 16.", "volume"); } // Sometimes the estimated uncertainty drops precipitiously. We will not report an uncertainty less than 3/4 of that demanded. double minUncertainty = Math.Max(0.75 * settings.AbsolutePrecision, Math.Abs(estimate.Value) * 0.75 * settings.RelativePrecision); if (estimate.Uncertainty < minUncertainty) { estimate = new UncertainValue(estimate.Value, minUncertainty); } return(new IntegrationResult(estimate, f.EvaluationCount, settings)); }
public void Evaluate(MultiFunctor f, CoordinateTransform[] map, IntegrationRegion r) { if (map == null) { Evaluate(f, r); return; } // Evaluation at origin. Keep a vector of origin coordinates and jacobian values. double[] x0 = new double[d]; double[] x = new double[d]; double[] v0 = new double[d]; double[] v = new double[d]; for (int i = 0; i < x.Length; i++) { x0[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, 0.0), out v0[i]); x[i] = x0[i]; v[i] = v0[i]; } double f0 = Jacobian(v) * f.Evaluate(x); double I7 = w70 * f0; double I5 = w50 * f0; // near off-one-axis evaluations double f1 = 0.0; for (int i = 0; i < d; i++) { x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a1), out v[i]); f1 += Jacobian(v) * f.Evaluate(x); x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a1), out v[i]); f1 += Jacobian(v) * f.Evaluate(x); x[i] = x0[i]; v[i] = v0[i]; } I7 += w71 * f1; I5 += w51 * f1; // far off-one-axis evaluations int sIndex = 0; double sMax = 0.0; double f2 = 0.0; for (int i = 0; i < d; i++) { x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a2), out v[i]); double fp2 = Jacobian(v) * f.Evaluate(x); f2 += fp2; x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a2), out v[i]); double fm2 = Jacobian(v) * f.Evaluate(x); f2 += fm2; x[i] = x0[i]; v[i] = v0[i]; double s = Math.Abs(fm2 + fp2 - 2.0 * f0); if (s > sMax) { sMax = s; sIndex = i; } else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex))) { sIndex = i; } } I7 += w72 * f2; I5 += w52 * f2; // far off-two-axis evaluations double f3 = 0.0; for (int i = 0; i < d; i++) { for (int j = 0; j < i; j++) { // ++ x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a3), out v[i]); x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]); f3 += Jacobian(v) * f.Evaluate(x); // +- x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, -a3), out v[j]); f3 += Jacobian(v) * f.Evaluate(x); // -- x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a3), out v[i]); f3 += Jacobian(v) * f.Evaluate(x); // -+ x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]); f3 += Jacobian(v) * f.Evaluate(x); x[i] = x0[i]; x[j] = x0[j]; v[i] = v0[i]; v[j] = v0[j]; } } I7 += w73 * f3; I5 += w53 * f3; // mid off-all-axis evaluations // We need all 2^d permutations of + and - in each position. // So that we only need to change one component each time, we proceed in Gray code order. // We use a bit-vector to keep track of which component is + (0) and which is - (1). int state = 0; for (int j = 0; j < d; j++) { x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a4), out v[j]); } double f4 = Jacobian(v) * f.Evaluate(x); for (int i = 0; i < (td - 1); i++) { int j = GrayFlipIndex(i); int mask = 1 << j; state = state ^ mask; x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4), out v[j]); f4 += Jacobian(v) * f.Evaluate(x); } I7 += w74 * f4; double V = r.Volume(); r.Value = V * I7; r.Error = V * Math.Abs(I7 - I5); r.SplitIndex = sIndex; }
public void Evaluate(MultiFunctor f, IntegrationRegion r) { // evaluation at origin // Keep a vector of origin coordinates, we will often re-set components to them. double[] x0 = new double[d]; double[] x = new double[d]; for (int i = 0; i < x.Length; i++) { x0[i] = r.MapCoordinateFromSymmetricUnitInterval(i, 0.0); x[i] = x0[i]; } double f0 = f.Evaluate(x); double I7 = w70 * f0; double I5 = w50 * f0; // near off-one-axis evaluations double f1 = 0.0; for (int i = 0; i < d; i++) { x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a1); f1 += f.Evaluate(x); x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a1); f1 += f.Evaluate(x); x[i] = x0[i]; } I7 += w71 * f1; I5 += w51 * f1; // far off-one-axis evaluations // while doing this, determine along which direction we will split, if asked int sIndex = 0; double sMax = 0.0; double f2 = 0.0; for (int i = 0; i < d; i++) { x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a2); double fp2 = f.Evaluate(x); f2 += fp2; x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a2); double fm2 = f.Evaluate(x); f2 += fm2; x[i] = x0[i]; // One way to view fm2 + fp2 - 2 f0 is as numerical 2nd derivative // Another way is as difference of f0 from value predicted by linear interpolation from fm2 and fp2 double s = Math.Abs(fm2 + fp2 - 2.0 * f0); if (s > sMax) { // Use the direction of largest difference for future splits. sMax = s; sIndex = i; } else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex))) { // If two directions both have the same difference, pick the one with the larger width to split. // This may seem like an unimportant corner case, but it turns out to be critical to convergence. // Without this clause we get into cycles in which we split along the same direction forever. // For example, given the 3D watson integral of [1 - \cos(x) \cos(y) \cos(z)]^{-1} over [0,\pi]^3, // all directions have zero deficit from starting point because one of the cosines is always zero, // and without this branch we split along the x-axis forever. sIndex = i; } } I7 += w72 * f2; I5 += w52 * f2; r.SplitIndex = sIndex; // far off-two-axis evaluations double f3 = 0.0; for (int i = 0; i < d; i++) { for (int j = 0; j < i; j++) { // ++ x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a3); x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3); f3 += f.Evaluate(x); // +- x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, -a3); f3 += f.Evaluate(x); // -- x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a3); f3 += f.Evaluate(x); // -+ x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3); f3 += f.Evaluate(x); x[i] = x0[i]; x[j] = x0[j]; } } I7 += w73 * f3; I5 += w53 * f3; // mid off-all-axis evaluations // We need all 2^d permutations of + and - in each position. // So that we only need to change one component each time, we proceed in Gray code order. // We use a bit-vector to keep track of which component is + (0) and which is - (1). int state = 0; for (int j = 0; j < d; j++) { x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a4); } double f4 = f.Evaluate(x); for (int i = 0; i < (td - 1); i++) { int j = GrayFlipIndex(i); int mask = 1 << j; state = state ^ mask; x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4); f4 += f.Evaluate(x); } I7 += w74 * f4; double V = r.Volume(); r.Value = V * I7; r.Error = V * Math.Abs(I7 - I5); }
public void Evaluate(MultiFunctor f, CoordinateTransform[] map, IntegrationRegion r) { if (map == null) { Evaluate(f, r); return; } // Evaluation at origin. Keep a vector of origin coordinates and jacobian values. double[] x0 = new double[d]; double[] x = new double[d]; double[] v0 = new double[d]; double[] v = new double[d]; for (int i = 0; i < x.Length; i++) { x0[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, 0.0), out v0[i]); x[i] = x0[i]; v[i] = v0[i]; } double f0 = Jacobian(v) * f.Evaluate(x); double I7 = w70 * f0; double I5 = w50 * f0; // near off-one-axis evaluations double f1 = 0.0; for (int i = 0; i < d; i++) { x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a1), out v[i]); f1 += Jacobian(v) * f.Evaluate(x); x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a1), out v[i]); f1 += Jacobian(v) * f.Evaluate(x); x[i] = x0[i]; v[i] = v0[i]; } I7 += w71 * f1; I5 += w51 * f1; // far off-one-axis evaluations int sIndex = 0; double sMax = 0.0; double f2 = 0.0; for (int i = 0; i < d; i++) { x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a2), out v[i]); double fp2 = Jacobian(v) * f.Evaluate(x); f2 += fp2; x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a2), out v[i]); double fm2 = Jacobian(v) * f.Evaluate(x); f2 += fm2; x[i] = x0[i]; v[i] = v0[i]; double s = Math.Abs(fm2 + fp2 - 2.0 * f0); if (s > sMax) { sMax = s; sIndex = i; } else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex))) { sIndex = i; } } I7 += w72 * f2; I5 += w52 * f2; // far off-two-axis evaluations double f3 = 0.0; for (int i = 0; i < d; i++) { for (int j = 0; j < i; j++) { // ++ x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, a3), out v[i]); x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]); f3 += Jacobian(v) * f.Evaluate(x); // +- x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, -a3), out v[j]); f3 += Jacobian(v) * f.Evaluate(x); // -- x[i] = map[i].Transform(r.MapCoordinateFromSymmetricUnitInterval(i, -a3), out v[i]); f3 += Jacobian(v) * f.Evaluate(x); // -+ x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a3), out v[j]); f3 += Jacobian(v) * f.Evaluate(x); x[i] = x0[i]; x[j] = x0[j]; v[i] = v0[i]; v[j] = v0[j]; } } I7 += w73 * f3; I5 += w53 * f3; // mid off-all-axis evaluations // We need all 2^d permutations of + and - in each position. // So that we only need to change one component each time, we proceed in Gray code order. // We use a bit-vector to keep track of which component is + (0) and which is - (1). int state = 0; for (int j = 0; j < d; j++) { x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, a4), out v[j]); } double f4 = Jacobian(v) * f.Evaluate(x); for (int i = 0; i < (td - 1); i++) { int j = GrayFlipIndex(i); int mask = 1 << j; state = state ^ mask; x[j] = map[j].Transform(r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4), out v[j]); f4 += Jacobian(v) * f.Evaluate(x); } I7 += w74 * f4; double V = r.Volume(); r.Value = V * I7; r.Error = V * Math.Abs(I7 - I5); r.SplitIndex = sIndex; }
public void Evaluate(MultiFunctor f, IntegrationRegion r) { // evaluation at origin // Keep a vector of origin coordinates, we will often re-set components to them. double[] x0 = new double[d]; double[] x = new double[d]; for (int i = 0; i < x.Length; i++) { x0[i] = r.MapCoordinateFromSymmetricUnitInterval(i, 0.0); x[i] = x0[i]; } double f0 = f.Evaluate(x); double I7 = w70 * f0; double I5 = w50 * f0; // near off-one-axis evaluations double f1 = 0.0; for (int i = 0; i < d; i++) { x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a1); f1 += f.Evaluate(x); x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a1); f1 += f.Evaluate(x); x[i] = x0[i]; } I7 += w71 * f1; I5 += w51 * f1; // far off-one-axis evaluations // while doing this, determine along which direction we will split, if asked int sIndex = 0; double sMax = 0.0; double f2 = 0.0; for (int i = 0; i < d; i++) { x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a2); double fp2 = f.Evaluate(x); f2 += fp2; x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a2); double fm2 = f.Evaluate(x); f2 += fm2; x[i] = x0[i]; // One way to view fm2 + fp2 - 2 f0 is as numerical 2nd derivative // Another way is as difference of f0 from value predicted by linear interpolation from fm2 and fp2 double s = Math.Abs(fm2 + fp2 - 2.0 * f0); if (s > sMax) { // Use the direction of largest difference for future splits. sMax = s; sIndex = i; } else if ((s == sMax) && (r.CoordinateWidth(i) > r.CoordinateWidth(sIndex))) { // If two directions both have the same difference, pick the one with the larger width to split. // This may seem like an unimportant corner case, but it turns out to be critical to convergence. // Without this clause we get into cycles in which we split along the same direction forever. // For example, given the 3D watson integral of [1 - \cos(x) \cos(y) \cos(z)]^{-1} over [0,\pi]^3, // all directions have zero deficit from starting point because one of the cosines is always zero, // and without this branch we split along the x-axis forever. sIndex = i; } } I7 += w72 * f2; I5 += w52 * f2; r.SplitIndex = sIndex; // far off-two-axis evaluations double f3 = 0.0; for (int i = 0; i < d; i++) { for (int j = 0; j < i; j++) { // ++ x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, a3); x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3); f3 += f.Evaluate(x); // +- x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, -a3); f3 += f.Evaluate(x); // -- x[i] = r.MapCoordinateFromSymmetricUnitInterval(i, -a3); f3 += f.Evaluate(x); // -+ x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a3); f3 += f.Evaluate(x); x[i] = x0[i]; x[j] = x0[j]; } } I7 += w73 * f3; I5 += w53 * f3; // mid off-all-axis evaluations // We need all 2^d permutations of + and - in each position. // So that we only need to change one component each time, we proceed in Gray code order. // We use a bit-vector to keep track of which component is + (0) and which is - (1). int state = 0; for (int j = 0; j < d; j++) { x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, a4); } double f4 = f.Evaluate(x); for (int i = 0; i < (td - 1); i++) { int j = GrayFlipIndex(i); int mask = 1 << j; state = state ^ mask; x[j] = r.MapCoordinateFromSymmetricUnitInterval(j, ((state & mask) > 0) ? -a4 : a4); f4 += f.Evaluate(x); } I7 += w74 * f4; double V = r.Volume(); r.Value = V * I7; r.Error = V * Math.Abs(I7 - I5); }
private static UncertainValue Integrate_Adaptive(MultiFunctor f, CoordinateTransform[] map, IntegrationRegion r, EvaluationSettings settings) { // Create an evaluation rule GenzMalik75Rule rule = new GenzMalik75Rule(r.Dimension); // Use it on the whole region and put the answer in a linked list rule.Evaluate(f, map, r); LinkedList <IntegrationRegion> regionList = new LinkedList <IntegrationRegion>(); regionList.AddFirst(r); // Iterate until convergence while (f.EvaluationCount < settings.EvaluationBudget) { // Add up value and errors in all regions. // While we are at it, take note of the region with the largest error. double value = 0.0; double error = 0.0; LinkedListNode <IntegrationRegion> regionNode = regionList.First; double maxError = 0.0; LinkedListNode <IntegrationRegion> maxErrorNode = null; while (regionNode != null) { IntegrationRegion region = regionNode.Value; value += region.Value; error += region.Error; //error += MoreMath.Sqr(region.Error); if (region.Error > maxError) { maxError = region.Error; maxErrorNode = regionNode; } regionNode = regionNode.Next; } // Check for convergence. if ((error <= settings.AbsolutePrecision) || (error <= settings.RelativePrecision * Math.Abs(value))) { return(new UncertainValue(value, error)); } // Split the region with the largest error, and evaluate each subregion. IntegrationRegion maxErrorRegion = maxErrorNode.Value; regionList.Remove(maxErrorNode); IList <IntegrationRegion> subRegions = maxErrorRegion.Split(maxErrorRegion.SplitIndex); /* * Countdown cnt = new Countdown(2); * ThreadPool.QueueUserWorkItem((object state) => { rule.Evaluate(f, subRegions[0]); cnt.Signal(); }); * ThreadPool.QueueUserWorkItem((object state) => { rule.Evaluate(f, subRegions[1]); cnt.Signal(); }); * cnt.Wait(); * foreach (IntegrationRegion subRegion in subRegions) { * regionList.AddLast(subRegion); * } */ foreach (IntegrationRegion subRegion in subRegions) { rule.Evaluate(f, map, subRegion); regionList.AddLast(subRegion); } } throw new NonconvergenceException(); }