13#include <Eigen/Eigenvalues>
80 std::array<double, 15>{-0.991455371120812639, -0.949107912342758525, -0.864864423359769073,
81 -0.741531185599394440, -0.586087235467691130, -0.405845151377397167,
82 -0.207784955007898468, 0.0, 0.207784955007898468,
83 0.405845151377397167, 0.586087235467691130, 0.741531185599394440,
84 0.864864423359769073, 0.949107912342758525, 0.991455371120812639},
85 std::array<double, 15>{0.022935322010529225, 0.063092092629978553, 0.104790010322250184,
86 0.140653259715525919, 0.169004726639267903, 0.190350578064785410,
87 0.204432940075298892, 0.209482141084727828, 0.204432940075298892,
88 0.190350578064785410, 0.169004726639267903, 0.140653259715525919,
89 0.104790010322250184, 0.063092092629978553, 0.022935322010529225},
90 std::array<double, 15>{0.0, 0.129484966168869693, 0.0, 0.279705391489276668, 0.0,
91 0.381830050505118945, 0.0, 0.417959183673469388, 0.0,
92 0.381830050505118945, 0.0, 0.279705391489276668, 0.0,
93 0.129484966168869693, 0.0},
111 if (k < 0 || k > n) {
114 if (k == 0 || k == n) {
117 k = std::min(k, n - k);
119 for (
int i = 1; i <= k; ++i) {
120 out *=
static_cast<double>(n - k + i) /
static_cast<double>(i);
130 return (1 << (level - 1)) + 1;
144 if (degree % 2 == 1) {
147 const int half = degree / 2;
148 return std::exp(std::lgamma(
static_cast<double>(degree) + 1.0) -
149 static_cast<double>(half) * std::log(2.0) -
150 std::lgamma(
static_cast<double>(half) + 1.0));
154 const double a = beta + 1.0;
155 const double b = alpha + 1.0;
158 for (
int j = 0; j <= degree; ++j) {
159 const double log_u_moment = std::lgamma(a +
static_cast<double>(j)) + std::lgamma(a + b) -
160 std::lgamma(a) - std::lgamma(a + b +
static_cast<double>(j));
161 const double u_moment = std::exp(log_u_moment);
162 const double coeff =
binomial(degree, j) * std::pow(2.0,
static_cast<double>(j)) *
163 ((degree - j) % 2 == 0 ? 1.0 : -1.0);
164 moment += coeff * u_moment;
175 switch (dimension.
family) {
180 if (degree % 2 == 1) {
183 return 1.0 /
static_cast<double>(degree + 1);
189 return std::exp(std::lgamma(dimension.
alpha +
static_cast<double>(degree) + 1.0) -
190 std::lgamma(dimension.
alpha + 1.0));
199 throw InvalidArgument(
"clenshaw_curtis_probability_weights: Clenshaw-Curtis is only "
200 "supported for bounded Legendre/Jacobi families");
203 if (nodes.size() == 1) {
210 return 0.5 *
cgl_weights(
static_cast<int>(nodes.size()), nodes);
213 const Eigen::Index n = nodes.size();
217 for (Eigen::Index k = 0; k < n; ++k) {
220 for (Eigen::Index j = 0; j < n; ++j) {
221 double x_power = 1.0;
222 for (Eigen::Index k = 0; k < n; ++k) {
223 vandermonde(k, j) = x_power;
228 const NumericVector weights = vandermonde.colPivHouseholderQr().solve(moments);
229 const NumericVector residual = vandermonde * weights - moments;
230 if (residual.lpNorm<Eigen::Infinity>() > 1e-8) {
231 throw RuntimeError(
"clenshaw_curtis_probability_weights: failed to recover stable "
232 "quadrature weights");
239 NumericMatrix J = NumericMatrix::Zero(diag.size(), diag.size());
241 for (Eigen::Index i = 0; i < offdiag.size(); ++i) {
242 J(i, i + 1) = offdiag(i);
243 J(i + 1, i) = offdiag(i);
246 Eigen::SelfAdjointEigenSolver<NumericMatrix> solver(J);
247 if (solver.info() != Eigen::Success) {
248 throw RuntimeError(
"make_gauss_from_jacobi_matrix: eigendecomposition failed");
252 rule.
nodes = solver.eigenvalues();
253 rule.
weights = solver.eigenvectors().row(0).array().square().transpose();
264 switch (dimension.
family) {
269 rule.
weights = 0.5 * weights_dx;
278 for (
int i = 1; i < order; ++i) {
279 offdiag(i - 1) = std::sqrt(
static_cast<double>(i));
287 const double alpha = dimension.
alpha;
288 const double beta = dimension.
beta;
290 diag(0) = (beta - alpha) / (alpha + beta + 2.0);
291 for (
int n = 1; n < order; ++n) {
292 const double nn =
static_cast<double>(n);
293 const double nab = 2.0 * nn + alpha + beta;
294 diag(n) = (beta * beta - alpha * alpha) / (nab * (nab + 2.0));
297 for (
int n = 1; n < order; ++n) {
298 const double nn =
static_cast<double>(n);
299 const double numerator = 4.0 * nn * (nn + alpha) * (nn + beta) * (nn + alpha + beta);
300 const double denom = std::pow(2.0 * nn + alpha + beta, 2.0) *
301 (2.0 * nn + alpha + beta - 1.0) * (2.0 * nn + alpha + beta + 1.0);
302 offdiag(n - 1) = std::sqrt(numerator / denom);
311 const double alpha = dimension.
alpha;
312 for (
int n = 0; n < order; ++n) {
313 diag(n) = 2.0 *
static_cast<double>(n) + alpha + 1.0;
315 for (
int n = 1; n < order; ++n) {
316 offdiag(n - 1) = std::sqrt(
static_cast<double>(n) * (
static_cast<double>(n) + alpha));
326 int order,
int level) {
328 throw InvalidArgument(
"gauss_kronrod_rule: Gauss-Kronrod 15 is only supported for "
329 "Legendre / uniform dimensions");
331 if (order != 7 && order != 15) {
337 rule.
nodes.resize(order);
343 for (
int i = 0; i < 15; ++i) {
344 rule.
nodes(i) = embedded.nodes[
static_cast<std::size_t
>(i)];
345 rule.
weights(i) = 0.5 * embedded.primary_weights[
static_cast<std::size_t
>(i)];
351 for (
int i = 0; i < 15; ++i) {
352 const double w = embedded.embedded_weights[
static_cast<std::size_t
>(i)];
356 rule.
nodes(cursor) = embedded.nodes[
static_cast<std::size_t
>(i)];
357 rule.
weights(cursor) = 0.5 * w;
364 int order,
int level) {
366 throw InvalidArgument(
"clenshaw_curtis_rule: Clenshaw-Curtis is only supported for "
367 "bounded Legendre/Jacobi dimensions");
372 rule.
nodes = (order == 1) ? NumericVector::Zero(1) :
cgl_nodes(order);
380 std::vector<std::vector<int>> out;
381 std::vector<int> current(
static_cast<std::size_t
>(dim), 1);
383 std::function<void(
int,
int)> recurse = [&](
int axis,
int remaining) {
384 if (axis == dim - 1) {
385 current[
static_cast<std::size_t
>(axis)] = remaining;
386 out.push_back(current);
390 for (
int value = 1; value <= remaining - (dim - axis - 1); ++value) {
391 current[
static_cast<std::size_t
>(axis)] = value;
392 recurse(axis + 1, remaining - value);
401 if (tolerance <= 0.0) {
405 const double scale = 1.0 / tolerance;
407 key.reserve(
static_cast<std::size_t
>(point.size()) * 24u);
408 for (Eigen::Index i = 0; i < point.size(); ++i) {
409 const std::int64_t value =
static_cast<std::int64_t
>(std::llround(point(i) * scale));
410 key += std::to_string(value);
470 throw InvalidArgument(
"stochastic_quadrature_level: Gauss-Kronrod 15 only supports "
471 "levels 1 (7-point) and 2 (15-point)");
484 throw InvalidArgument(
"stochastic_quadrature_level: unsupported rule");
495 throw InvalidArgument(
"tensor_product_quadrature: need at least one univariate rule");
498 Eigen::Index total_points = 1;
501 for (
const auto &rule : rules) {
502 if (rule.nodes.size() == 0 || rule.weights.size() == 0) {
503 throw InvalidArgument(
"tensor_product_quadrature: rules must contain nodes and "
506 if (rule.nodes.size() != rule.weights.size()) {
507 throw InvalidArgument(
"tensor_product_quadrature: node/weight size mismatch");
509 if (total_points > std::numeric_limits<Eigen::Index>::max() / rule.nodes.size()) {
512 total_points *= rule.nodes.size();
513 nested = nested && rule.nested;
514 level = std::max(level, rule.level);
518 grid.
samples.resize(total_points,
static_cast<Eigen::Index
>(rules.size()));
519 grid.
weights.resize(total_points);
523 for (Eigen::Index row = 0; row < total_points; ++row) {
524 Eigen::Index cursor = row;
526 for (Eigen::Index axis =
static_cast<Eigen::Index
>(rules.size()) - 1; axis >= 0; --axis) {
527 const auto &rule = rules[
static_cast<std::size_t
>(axis)];
528 const Eigen::Index local = cursor % rule.nodes.size();
529 cursor /= rule.nodes.size();
530 grid.
samples(row, axis) = rule.nodes(local);
531 weight *= rule.weights(local);
556 if (dimensions.empty()) {
557 throw InvalidArgument(
"smolyak_sparse_grid: need at least one stochastic dimension");
560 if (options.merge_tolerance <= 0.0) {
561 throw InvalidArgument(
"smolyak_sparse_grid: merge_tolerance must be positive");
563 if (options.zero_weight_tolerance < 0.0) {
564 throw InvalidArgument(
"smolyak_sparse_grid: zero_weight_tolerance must be >= 0");
567 struct WeightedPoint {
572 std::map<std::string, WeightedPoint> merged;
573 const int dim =
static_cast<int>(dimensions.size());
574 const int q = level + dim - 1;
575 bool fully_nested =
true;
577 for (
int sum = level; sum <= q; ++sum) {
578 const double combination =
580 if (combination == 0.0) {
585 std::vector<UnivariateQuadratureRule> rules;
586 rules.reserve(dimensions.size());
587 for (
int axis = 0; axis < dim; ++axis) {
589 dimensions[
static_cast<std::size_t
>(axis)],
590 index[
static_cast<std::size_t
>(axis)], options.rule);
591 fully_nested = fully_nested && rule.
nested;
592 rules.push_back(std::move(rule));
596 for (Eigen::Index row = 0; row < tensor.
samples.rows(); ++row) {
599 auto &entry = merged[key];
600 if (entry.point.size() == 0) {
601 entry.point = std::move(point);
603 entry.weight += combination * tensor.
weights(row);
608 std::vector<WeightedPoint> points;
609 points.reserve(merged.size());
610 for (
const auto &[key, value] : merged) {
612 if (std::abs(value.weight) <= options.zero_weight_tolerance) {
615 points.push_back(value);
618 if (points.empty()) {
619 throw RuntimeError(
"smolyak_sparse_grid: all points cancelled numerically");
623 grid.
samples.resize(
static_cast<Eigen::Index
>(points.size()),
static_cast<Eigen::Index
>(dim));
624 grid.
weights.resize(
static_cast<Eigen::Index
>(points.size()));
625 grid.
nested = fully_nested;
628 for (std::size_t i = 0; i < points.size(); ++i) {
629 grid.
samples.row(
static_cast<Eigen::Index
>(i)) = points[i].point.transpose();
630 grid.
weights(
static_cast<Eigen::Index
>(i)) = points[i].weight;
644template <JanusScalar Scalar>
649 throw InvalidArgument(
"pce_projection_coefficients(rule): univariate rule requires a "
650 "one-dimensional PolynomialChaosBasis");
654 samples.col(0) = rule.
nodes;
666template <JanusScalar Scalar>
671 throw InvalidArgument(
"pce_projection_coefficients(rule): univariate rule requires a "
672 "one-dimensional PolynomialChaosBasis");
676 samples.col(0) = rule.
nodes;
688template <JanusScalar Scalar>
703template <JanusScalar Scalar>
C++20 concepts constraining valid Janus scalar types.
Custom exception hierarchy for Janus framework.
Core type aliases for numeric and symbolic Eigen/CasADi interop.
Orthogonal polynomial evaluation, spectral nodes, weights, and differentiation matrices.
Polynomial chaos expansion (PCE) basis, projection, and regression.
Input validation failed (e.g., mismatched sizes, invalid parameters).
Definition JanusError.hpp:31
Multidimensional polynomial chaos basis with fixed truncation/order.
Definition PolynomialChaos.hpp:456
int dimension() const
Definition PolynomialChaos.hpp:488
Operation failed at runtime (e.g., CasADi eval with free variables).
Definition JanusError.hpp:41
Smooth approximation of ReLU function: softplus(x) = (1/beta) * log(1 + exp(beta * x)).
Definition Diagnostics.hpp:131
void validate_dimension(const PolynomialChaosDimension &dimension, const std::string &context)
Definition PolynomialChaos.hpp:112
std::pair< NumericVector, NumericVector > gauss_legendre_rule(int n)
Definition OrthogonalPolynomials.hpp:23
bool is_bounded_support(const PolynomialChaosDimension &dimension)
Definition Quadrature.hpp:138
void validate_level(int level, const std::string &context)
Definition Quadrature.hpp:104
double standard_normal_moment(int degree)
Definition Quadrature.hpp:143
UnivariateQuadratureRule gauss_kronrod_rule(const PolynomialChaosDimension &dimension, int order, int level)
Definition Quadrature.hpp:325
const EmbeddedQuadratureRule & gauss_kronrod_15_rule()
Definition Quadrature.hpp:78
void validate_order(int order, const std::string &context)
Definition Quadrature.hpp:98
NumericVector clenshaw_curtis_probability_weights(const PolynomialChaosDimension &dimension, const NumericVector &nodes)
Definition Quadrature.hpp:196
int clenshaw_curtis_order_from_level(int level)
Definition Quadrature.hpp:125
UnivariateQuadratureRule make_gauss_from_jacobi_matrix(const NumericVector &diag, const NumericVector &offdiag, int level)
Definition Quadrature.hpp:238
int gauss_order_from_level(int level)
Definition Quadrature.hpp:133
UnivariateQuadratureRule clenshaw_curtis_rule(const PolynomialChaosDimension &dimension, int order, int level)
Definition Quadrature.hpp:363
std::vector< std::vector< int > > positive_compositions(int dim, int sum)
Definition Quadrature.hpp:379
double binomial(int n, int k)
Definition Quadrature.hpp:110
double shifted_beta_moment(int degree, double alpha, double beta)
Definition Quadrature.hpp:153
double probability_moment(const PolynomialChaosDimension &dimension, int degree)
Definition Quadrature.hpp:170
UnivariateQuadratureRule gauss_rule(const PolynomialChaosDimension &dimension, int order, int level)
Definition Quadrature.hpp:259
std::string sample_key(const NumericVector &point, double tolerance)
Definition Quadrature.hpp:400
Definition Diagnostics.hpp:19
JanusVector< Scalar > pce_projection_coefficients(const PolynomialChaosBasis &basis, const NumericMatrix &samples, const NumericVector &weights, const JanusVector< Scalar > &sample_values)
Compute PCE projection coefficients from weighted samples (vector).
Definition PolynomialChaos.hpp:554
NumericVector cgl_weights(int N, const NumericVector &nodes)
CGL (Clenshaw-Curtis) quadrature weights on [-1, 1].
Definition OrthogonalPolynomials.hpp:224
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > JanusMatrix
Dynamic-size matrix for both numeric and symbolic backends.
Definition JanusTypes.hpp:43
JanusMatrix< NumericScalar > NumericMatrix
Eigen::MatrixXd equivalent.
Definition JanusTypes.hpp:66
StochasticQuadratureGrid tensor_product_quadrature(const std::vector< UnivariateQuadratureRule > &rules)
Build the tensor-product grid from a list of one-dimensional rules.
Definition Quadrature.hpp:493
JanusVector< NumericScalar > NumericVector
Eigen::VectorXd equivalent.
Definition JanusTypes.hpp:67
UnivariateQuadratureRule stochastic_quadrature_rule(const PolynomialChaosDimension &dimension, int order, StochasticQuadratureRule rule=StochasticQuadratureRule::Gauss)
Build a one-dimensional stochastic quadrature rule with a fixed order.
Definition Quadrature.hpp:426
@ Jacobi
Beta-family random variable on [-1, 1].
Definition PolynomialChaos.hpp:28
@ Hermite
Standard normal random variable via probabilists' Hermite polynomials.
Definition PolynomialChaos.hpp:26
@ Laguerre
Gamma / exponential-family random variable on [0, inf).
Definition PolynomialChaos.hpp:29
@ Legendre
Uniform random variable on [-1, 1].
Definition PolynomialChaos.hpp:27
StochasticQuadratureRule
One-dimensional rule family used to generate stochastic quadrature nodes.
Definition Quadrature.hpp:54
@ AutoNested
Use Clenshaw-Curtis on bounded families, Gauss otherwise.
Definition Quadrature.hpp:58
@ ClenshawCurtis
Nested Clenshaw-Curtis-like rule on bounded support.
Definition Quadrature.hpp:56
@ Gauss
Family-appropriate Gauss rule.
Definition Quadrature.hpp:55
@ GaussKronrod15
Embedded 7/15-point Legendre rule reused from quad(...).
Definition Quadrature.hpp:57
UnivariateQuadratureRule stochastic_quadrature_level(const PolynomialChaosDimension &dimension, int level, StochasticQuadratureRule rule=StochasticQuadratureRule::AutoNested)
Build a one-dimensional stochastic quadrature rule from a refinement level.
Definition Quadrature.hpp:456
NumericVector cgl_nodes(int N)
Compute Chebyshev-Gauss-Lobatto nodes on [-1, 1].
Definition OrthogonalPolynomials.hpp:181
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > JanusVector
Dynamic-size column vector for both numeric and symbolic backends.
Definition JanusTypes.hpp:49
StochasticQuadratureGrid smolyak_sparse_grid(const std::vector< PolynomialChaosDimension > &dimensions, int level, SmolyakQuadratureOptions options={})
Build a Smolyak sparse grid on probability measures.
Definition Quadrature.hpp:554
Multidimensional stochastic quadrature grid with row-major sample layout.
Definition Quadrature.hpp:44
NumericMatrix samples
Definition Quadrature.hpp:45
int level
Definition Quadrature.hpp:48
NumericVector weights
Definition Quadrature.hpp:46
bool nested
Definition Quadrature.hpp:47
One-dimensional stochastic quadrature rule on a probability measure.
Definition Quadrature.hpp:30
bool nested
Definition Quadrature.hpp:33
One stochastic dimension in a polynomial chaos basis.
Definition PolynomialChaos.hpp:47
PolynomialChaosFamily family
Definition PolynomialChaos.hpp:48
double alpha
Definition PolynomialChaos.hpp:49
double beta
Definition PolynomialChaos.hpp:50
Options for Smolyak sparse-grid construction.
Definition Quadrature.hpp:64
double zero_weight_tolerance
Definition Quadrature.hpp:67
double merge_tolerance
Definition Quadrature.hpp:66
StochasticQuadratureRule rule
Definition Quadrature.hpp:65
Multidimensional stochastic quadrature grid with row-major sample layout.
Definition Quadrature.hpp:44
NumericMatrix samples
Definition Quadrature.hpp:45
int level
Definition Quadrature.hpp:48
NumericVector weights
Definition Quadrature.hpp:46
bool nested
Definition Quadrature.hpp:47
One-dimensional stochastic quadrature rule on a probability measure.
Definition Quadrature.hpp:30
int level
Definition Quadrature.hpp:34
NumericVector nodes
Definition Quadrature.hpp:31
NumericVector weights
Definition Quadrature.hpp:32
bool nested
Definition Quadrature.hpp:33
Definition Quadrature.hpp:72
std::array< double, 15 > primary_weights
Definition Quadrature.hpp:74
std::array< double, 15 > nodes
Definition Quadrature.hpp:73
std::array< double, 15 > embedded_weights
Definition Quadrature.hpp:75
JanusVector< NumericScalar > NumericVector
Eigen::VectorXd equivalent.
Definition JanusTypes.hpp:67