public SobolVectorGenerator(int d) : base(d) { sequences = new SobolSequence[d]; for (int i = 0; i < sequences.Length; i++) { SobolSequenceParameters p = SobolSequenceParameters.sobolParameters[i]; sequences[i] = new SobolSequence(p.Dimension, p.Coefficients, p.Seeds); } }
/// <summary> /// Integrates the given function over the given volume. /// </summary> /// <param name="integrand">The function to integrate, which maps R<sup>d</sup> to R.</param> /// <param name="volume">The box defining the volume over with to integrate.</param> /// <param name="settings">The evaluation settings to use.</param> /// <returns>The value of the integral.</returns> public static double Integrate(Func <IList <double>, double> integrand, IList <Interval> volume, EvaluationSettings settings) { if (integrand == null) { throw new ArgumentNullException("integrand"); } if (volume == null) { throw new ArgumentNullException("volume"); } // get the dimension of the problem from the volume int d = volume.Count; if ((d < 1) || (d > sobolParameters.Length)) { throw new InvalidOperationException(); } // if no settings were provided, use defaults if (settings == null) { settings = new EvaluationSettings() { RelativePrecision = 1.0 / (1 << (14 - 3 * d / 2)), AbsolutePrecision = RelativePrecision / 128.0, EvaluationBudget = 1 << (18 + 3 * d / 2) }; } // compute the volume of the box double V = 1.0; for (int j = 0; j < volume.Count; j++) { V *= volume[j].Width; } // generate the appropriate number of Sobol sequences SobolSequence[] s = new SobolSequence[d]; for (int j = 0; j < d; j++) { SobolSequenceParameters p = sobolParameters[j]; s[j] = new SobolSequence(p.Dimension, p.Coefficients, p.Seeds); } // create one vector to store the argument // we don't want to recreate the vector in the // heap on each iteration double[] x = new double[d]; // keep track of the average sampled value double M = 0.0; double M_old = Double.NaN; // set the first convergence checkpoint to ~4000 points for d=2, increasing // for higher d int i_next = 1 << (10 + d); for (int i = 1; i <= settings.EvaluationBudget; i++) { // move to the next value in each dimension's Sobol sequence // and construct the argument vector for (int j = 0; j < d; j++) { s[j].MoveNext(); x[j] = volume[j].LeftEndpoint + s[j].Current * volume[j].Width; } // evaluate the function double y = integrand(x); // update the mean M += (y - M) / i; // check for convergence at regular intervals if (i == i_next) { // estimate error as change since last check double dM = 2.0 * Math.Abs(M - M_old); if (dM < settings.RelativePrecision * Math.Abs(M) || Math.Abs(V) * dM < settings.AbsolutePrecision) { return(V * M); } // no convergence, so remember current value and set next checkpoint M_old = M; i_next = 2 * i_next; // Consider how to do this better: // 1. Is there any way we can do better than doubling? Say increasing by 4/3 and then 3/2, for an average increase of // 40% instead of 100%? If so, how do we estimate error at each step? // 2. Can we use Romberg extrapolation? I tried using both M and M_old, assuming an error term of 1/N. This gives // the extrapolated value 2 M - M_old, but experimentally this is usually a worse value than M. I should try out // 3-value extrapolation. } } throw new NonconvergenceException(); }