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); }