5#ifndef LINE_SOLVERS_FLUID_FLUID_MVN_RECTANGLE_H
6#define LINE_SOLVERS_FLUID_FLUID_MVN_RECTANGLE_H
49inline const std::vector<int>& mvn_primes() {
50 static const std::vector<int> p = {
51 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
52 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
53 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
54 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
55 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541};
60inline double mvn_phi(
double x) {
61 if (std::isinf(x))
return x > 0.0 ? 1.0 : 0.0;
62 return 0.5 * std::erfc(-x / std::sqrt(2.0));
73inline double mvn_phi_inv(
double u) {
74 static const double a[6] = {-3.969683028665376e+01, 2.209460984245205e+02, -2.759285104469687e+02,
75 1.383577518672690e+02, -3.066479806614716e+01, 2.506628277459239e+00};
76 static const double b[5] = {-5.447609879822406e+01, 1.615858368580409e+02, -1.556989798598866e+02,
77 6.680131188771972e+01, -1.328068155288572e+01};
78 static const double c[6] = {-7.784894002430293e-03, -3.223964580411365e-01, -2.400758277161838e+00,
79 -2.549732539343734e+00, 4.374664141464968e+00, 2.938163982698783e+00};
80 static const double d[4] = {7.784695709041462e-03, 3.224671290700398e-01, 2.445134137142996e+00,
81 3.754408661907416e+00};
82 const double plow = 0.02425, phigh = 1.0 - plow;
85 const double q = std::sqrt(-2.0 * std::log(u));
86 x = (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
87 ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0);
88 }
else if (u > phigh) {
89 const double q = std::sqrt(-2.0 * std::log(1.0 - u));
90 x = -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
91 ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1.0);
93 const double q = u - 0.5, r = q * q;
94 x = (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
95 (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1.0);
98 const double e = mvn_phi(x) - u;
99 const double pdf = std::exp(-0.5 * x * x) / std::sqrt(2.0 * 3.14159265358979323846);
101 const double t = e / pdf;
102 x -= t / (1.0 + 0.5 * x * t);
112inline Matrix<double> mvn_chol_psd(
const Matrix<double>& C,
double dtol) {
113 const std::size_t d = C.rows();
114 Matrix<double> L(d, d, 0.0);
115 for (std::size_t i = 0; i < d; ++i) {
117 for (std::size_t j = 0; j < i; ++j) v -= L(i, j) * L(i, j);
119 L(i, i) = std::sqrt(v);
120 for (std::size_t r = i + 1; r < d; ++r) {
122 for (std::size_t j = 0; j < i; ++j) s -= L(r, j) * L(i, j);
123 L(r, i) = s / L(i, i);
127 for (std::size_t r = i + 1; r < d; ++r) L(r, i) = 0.0;
134inline double mvn_evaluate(
const Matrix<double>& L,
const std::vector<double>& al,
135 const std::vector<double>& bu,
const std::vector<double>& w,
136 std::vector<double>& y, std::size_t last_int,
bool has_int,
double ctol,
138 const std::size_t d = al.size();
141 for (std::size_t i = 0; i < d; ++i) {
143 for (std::size_t j = 0; j < i; ++j) s += L(i, j) * y[j];
145 const double dd = mvn_phi((al[i] - s) / L(i, i));
146 const double ee = mvn_phi((bu[i] - s) / L(i, i));
147 f *= std::max(0.0, ee - dd);
148 if (f == 0.0)
return 0.0;
149 if (!(has_int && i == last_int)) {
150 const double wk = antithetic ? 1.0 - w[kw] : w[kw];
152 double u = dd + wk * (ee - dd);
154 u = std::min(std::max(u, 1e-15), 1.0 - 1e-15);
155 y[i] = mvn_phi_inv(u);
160 if (s < al[i] - ctol || s > bu[i] + ctol)
return 0.0;
182 const std::vector<double>& a,
const std::vector<double>& b,
184 const std::size_t d = m.size();
185 if (d == 0)
return 1.0;
186 std::vector<double> al(d), bu(d);
187 for (std::size_t i = 0; i < d; ++i) {
190 if (bu[i] <= al[i])
return 0.0;
196 for (std::size_t i = 0; i < d; ++i) scale = std::max(scale, std::fabs(C(i, i)));
197 const double dtol = 1e-12 * scale;
198 const double ctol = 1e-6 * std::sqrt(scale);
201 std::size_t n_int = 0, last_int = 0;
202 bool has_int =
false;
203 for (std::size_t i = 0; i < d; ++i)
209 const std::size_t nw = (n_int > 0) ? n_int - 1 : 0;
210 if (nw > detail::mvn_primes().size())
212 "fluid_mvn_rectangle: the lattice rule carries generators for at most 100 integration "
213 "dimensions. Aggregate classes before evaluating the cell");
215 std::vector<double> alpha(nw);
216 for (std::size_t j = 0; j < nw; ++j) alpha[j] = std::sqrt(static_cast<double>(detail::mvn_primes()[j]));
218 const std::size_t npairs = (nw == 0) ? 1 : npoints;
219 const std::size_t n_eval = (nw == 0) ? 1 : 2 * npoints;
220 std::vector<double> w(nw), y(d, 0.0);
222 for (std::size_t k = 1; k <= npairs; ++k) {
223 for (std::size_t j = 0; j < nw; ++j) {
224 const double v =
static_cast<double>(k) * alpha[j];
225 w[j] = v - std::floor(v);
227 acc += detail::mvn_evaluate(L, al, bu, w, y, last_int, has_int, ctol,
false);
228 if (nw > 0) acc += detail::mvn_evaluate(L, al, bu, w, y, last_int, has_int, ctol,
true);
231 const double p = acc /
static_cast<double>(n_eval);
232 return std::min(std::max(p, 0.0), 1.0);
The exception types the port throws.
Dense matrix and non-owning view.
double fluid_mvn_rectangle(const std::vector< double > &m, const Matrix< double > &C, const std::vector< double > &a, const std::vector< double > &b, std::size_t npoints=FLUID_MVN_POINTS)
P(a <= Y <= b) for Y ~ Normal(m, C).
const std::size_t FLUID_MVN_POINTS
Lattice points per antithetic pair.