Actual source code: ex14.c

  1: const char help[] = "Tests properties of probability distributions";

  3: #include <petscdt.h>

  5: /* Checks that
  6:    - the PDF integrates to 1
  7:    - the incomplete integral of the PDF is the CDF at many points
  8: */
  9: static PetscErrorCode VerifyDistribution(const char name[], PetscBool pos, PetscProbFn *pdf, PetscProbFn *cdf)
 10: {
 11:   const PetscInt digits = 14;
 12:   PetscReal      lower = pos ? 0. : -10., upper = 10.;
 13:   PetscReal      integral, integral2;
 14:   PetscInt       i;

 16:   PetscFunctionBeginUser;
 17:   PetscCall(PetscDTTanhSinhIntegrate((void (*)(const PetscReal[], void *, PetscReal *))pdf, lower, upper, digits, NULL, &integral));
 18:   PetscCheck(PetscAbsReal(integral - 1) < 100 * PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PDF %s must integrate to 1, not %g", name, (double)integral);
 19:   for (i = 0; i <= 10; ++i) {
 20:     const PetscReal x = i;

 22:     PetscCall(PetscDTTanhSinhIntegrate((void (*)(const PetscReal[], void *, PetscReal *))pdf, lower, x, digits, NULL, &integral));
 23:     PetscCall(cdf(&x, NULL, &integral2));
 24:     PetscCheck(PetscAbsReal(integral - integral2) < PETSC_SQRT_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Integral of PDF %s %g != %g CDF at x = %g", name, (double)integral, (double)integral2, (double)x);
 25:   }
 26:   PetscFunctionReturn(PETSC_SUCCESS);
 27: }

 29: /* The 2D and 3D Gaussians depend only on |x|, so their integral over R^d is the integral over r of p(x) with |x| = r
 30:    times the measure of the sphere |x| = r, which is 2 pi r in 2D and 4 pi r^2 in 3D. We take x on the diagonal so every
 31:    coordinate is used. */
 32: static void Gaussian2DRadial(const PetscReal r[], PetscCtx ctx, PetscReal *f)
 33: {
 34:   const PetscReal x[2] = {r[0] / PetscSqrtReal(2.), r[0] / PetscSqrtReal(2.)};

 36:   PetscCallAbort(PETSC_COMM_SELF, PetscPDFGaussian2D(x, NULL, f));
 37:   *f *= 2. * PETSC_PI * r[0];
 38: }

 40: static void Gaussian3DRadial(const PetscReal r[], PetscCtx ctx, PetscReal *f)
 41: {
 42:   const PetscReal x[3] = {r[0] / PetscSqrtReal(3.), r[0] / PetscSqrtReal(3.), r[0] / PetscSqrtReal(3.)};

 44:   PetscCallAbort(PETSC_COMM_SELF, PetscPDFGaussian3D(x, NULL, f));
 45:   *f *= 4. * PETSC_PI * PetscSqr(r[0]);
 46: }

 48: static PetscErrorCode TestDistributions()
 49: {
 50:   PetscProbFn *pdf[]  = {PetscPDFMaxwellBoltzmann1D, PetscPDFMaxwellBoltzmann2D, PetscPDFMaxwellBoltzmann3D, PetscPDFGaussian1D};
 51:   PetscProbFn *cdf[]  = {PetscCDFMaxwellBoltzmann1D, PetscCDFMaxwellBoltzmann2D, PetscCDFMaxwellBoltzmann3D, PetscCDFGaussian1D};
 52:   PetscBool    pos[]  = {PETSC_TRUE, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE};
 53:   const char  *name[] = {"Maxwell-Boltzmann 1D", "Maxwell-Boltzmann 2D", "Maxwell-Boltzmann 3D", "Gaussian"};

 55:   PetscFunctionBeginUser;
 56:   for (PetscInt i = 0; i < (PetscInt)PETSC_STATIC_ARRAY_LENGTH(pdf); ++i) PetscCall(VerifyDistribution(name[i], pos[i], pdf[i], cdf[i]));
 57:   for (PetscInt d = 2; d <= 3; ++d) {
 58:     PetscReal integral;

 60:     PetscCall(PetscDTTanhSinhIntegrate(d == 2 ? Gaussian2DRadial : Gaussian3DRadial, 0., 10., 14, NULL, &integral));
 61:     PetscCheck(PetscAbsReal(integral - 1) < 100 * PETSC_MACHINE_EPSILON, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "PDF Gaussian %" PetscInt_FMT "D must integrate to 1, not %g", d, (double)integral);
 62:   }
 63:   PetscFunctionReturn(PETSC_SUCCESS);
 64: }

 66: static PetscErrorCode TestSampling()
 67: {
 68:   PetscProbFn *cdf[2]     = {PetscCDFMaxwellBoltzmann1D, PetscCDFMaxwellBoltzmann2D};
 69:   PetscProbFn *sampler[2] = {PetscPDFSampleGaussian1D, PetscPDFSampleGaussian2D};
 70:   PetscInt     dim[2]     = {1, 2};
 71:   PetscRandom  rnd;
 72:   Vec          v;
 73:   PetscScalar *a;
 74:   PetscReal    alpha, confidenceLevel = 0.05;
 75:   PetscInt     n = 1000, s, i, d;

 77:   PetscFunctionBeginUser;
 78:   PetscCall(PetscRandomCreate(PETSC_COMM_SELF, &rnd));
 79:   PetscCall(PetscRandomSetInterval(rnd, 0, 1.));
 80:   PetscCall(PetscRandomSetFromOptions(rnd));

 82:   for (s = 0; s < 2; ++s) {
 83:     PetscCall(VecCreateSeq(PETSC_COMM_SELF, n * dim[s], &v));
 84:     PetscCall(VecSetBlockSize(v, dim[s]));
 85:     PetscCall(VecGetArray(v, &a));
 86:     for (i = 0; i < n; ++i) {
 87:       PetscReal r[3], o[3];

 89:       for (d = 0; d < dim[s]; ++d) PetscCall(PetscRandomGetValueReal(rnd, &r[d]));
 90:       PetscCall(sampler[s](r, NULL, o));
 91:       for (d = 0; d < dim[s]; ++d) a[i * dim[s] + d] = o[d];
 92:     }
 93:     PetscCall(VecRestoreArray(v, &a));
 94:     PetscCall(PetscProbComputeKSStatistic(v, cdf[s], &alpha));
 95:     PetscCheck(alpha < confidenceLevel, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "KS finds sampling does not match the distribution at confidence level %.2g", (double)confidenceLevel);
 96:     PetscCall(VecDestroy(&v));
 97:   }
 98:   PetscCall(PetscRandomDestroy(&rnd));
 99:   PetscFunctionReturn(PETSC_SUCCESS);
100: }

102: int main(int argc, char **argv)
103: {
104:   PetscFunctionBeginUser;
105:   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
106:   PetscCall(TestDistributions());
107:   PetscCall(TestSampling());
108:   PetscCall(PetscFinalize());
109:   return 0;
110: }

112: /*TEST

114:   test:
115:     suffix: 0
116:     requires: ks
117:     args:
118:     output_file: output/empty.out

120: TEST*/