113 const std::string &context) {
114 switch (dimension.
family) {
119 if (dimension.
alpha <= -1.0 || dimension.
beta <= -1.0) {
120 throw InvalidArgument(context +
": Jacobi parameters must satisfy alpha > -1 and "
125 if (dimension.
alpha <= -1.0) {
126 throw InvalidArgument(context +
": Laguerre parameter must satisfy alpha > -1");
142 Scalar h_nm1 = Scalar(1.0);
144 for (
int n = 1; n < degree; ++n) {
145 Scalar h_np1 = x * h_n -
static_cast<double>(n) * h_nm1;
155 if constexpr (std::floating_point<Scalar>) {
165 Scalar p_nm1 = Scalar(1.0);
167 for (
int n = 1; n < degree; ++n) {
168 const double nn =
static_cast<double>(n);
169 Scalar p_np1 = ((2.0 * nn + 1.0) * x * p_n - nn * p_nm1) / (nn + 1.0);
177template <JanusScalar Scalar>
186 return 0.5 * ((alpha - beta) + (alpha + beta + 2.0) * x);
189 Scalar p_nm1 = Scalar(1.0);
190 Scalar p_n = 0.5 * ((alpha - beta) + (alpha + beta + 2.0) * x);
192 for (
int n = 1; n < degree; ++n) {
193 const double nn =
static_cast<double>(n);
194 const double two_n_ab = 2.0 * nn + alpha + beta;
195 const double a_n = 2.0 * (nn + 1.0) * (nn + alpha + beta + 1.0) * two_n_ab;
197 (two_n_ab + 1.0) * (two_n_ab * (two_n_ab + 2.0) * x + alpha * alpha - beta * beta);
198 const double c_n = 2.0 * (nn + alpha) * (nn + beta) * (two_n_ab + 2.0);
199 Scalar p_np1 = (b_n * p_n - c_n * p_nm1) / a_n;
207template <JanusScalar Scalar>
216 return Scalar(1.0 + alpha) - x;
219 Scalar l_nm1 = Scalar(1.0);
220 Scalar l_n = Scalar(1.0 + alpha) - x;
222 for (
int n = 1; n < degree; ++n) {
223 const double nn =
static_cast<double>(n);
224 Scalar l_np1 = ((2.0 * nn + 1.0 + alpha - x) * l_n - (nn + alpha) * l_nm1) / (nn + 1.0);
236 switch (dimension.
family) {
238 return std::exp(std::lgamma(
static_cast<double>(degree) + 1.0));
241 return 2.0 / (2.0 *
static_cast<double>(degree) + 1.0);
244 const double n =
static_cast<double>(degree);
245 const double alpha = dimension.
alpha;
246 const double beta = dimension.
beta;
247 const double log_raw = (alpha + beta + 1.0) * std::log(2.0) -
248 std::log(2.0 * n + alpha + beta + 1.0) +
249 std::lgamma(n + alpha + 1.0) + std::lgamma(n + beta + 1.0) -
250 std::lgamma(n + 1.0) - std::lgamma(n + alpha + beta + 1.0);
251 return std::exp(log_raw);
255 return std::exp(std::lgamma(
static_cast<double>(degree) + dimension.
alpha + 1.0) -
256 std::lgamma(
static_cast<double>(degree) + 1.0));
270 const int lhs_sum = std::accumulate(lhs.begin(), lhs.end(), 0);
271 const int rhs_sum = std::accumulate(rhs.begin(), rhs.end(), 0);
272 if (lhs_sum != rhs_sum) {
273 return lhs_sum < rhs_sum;
279 std::vector<std::vector<int>> &indices) {
281 indices.push_back(current);
285 const int used = std::accumulate(current.begin(), current.begin() + axis, 0);
286 const int remaining = order - used;
287 for (
int degree = 0; degree <= remaining; ++degree) {
288 current[axis] = degree;
294 std::vector<std::vector<int>> &indices) {
296 indices.push_back(current);
300 for (
int degree = 0; degree <= order; ++degree) {
301 current[axis] = degree;
308 std::vector<std::vector<int>> indices;
309 std::vector<int> current(
static_cast<std::size_t
>(dim), 0);
311 switch (truncation) {
324template <JanusScalar Scalar>
326 const std::string &context) {
327 if (values.rows() != op.cols()) {
328 throw InvalidArgument(context +
": sample value size must match the number of rows in the "
333 for (Eigen::Index i = 0; i < op.rows(); ++i) {
334 Scalar accum = Scalar(0.0);
335 for (Eigen::Index j = 0; j < op.cols(); ++j) {
336 accum += op(i, j) * values(j);
343template <JanusScalar Scalar>
345 const std::string &context) {
346 if (values.rows() != op.cols()) {
347 throw InvalidArgument(context +
": sample value rows must match the number of rows in the "
352 for (Eigen::Index i = 0; i < op.rows(); ++i) {
353 for (Eigen::Index col = 0; col < values.cols(); ++col) {
354 Scalar accum = Scalar(0.0);
355 for (Eigen::Index j = 0; j < op.cols(); ++j) {
356 accum += op(i, j) * values(j, col);
365 const std::string &context) {
366 if (samples.rows() == 0) {
367 throw InvalidArgument(context +
": sample matrix must contain at least one sample");
369 if (samples.cols() != dimension) {
370 throw InvalidArgument(context +
": sample matrix column count must match the PCE "
376 const std::string &context) {
377 if (design_matrix.rows() < design_matrix.cols()) {
378 throw InvalidArgument(context +
": need at least as many samples as basis terms for "
382 throw InvalidArgument(context +
": ridge regularization must be >= 0");
386 Eigen::JacobiSVD<NumericMatrix> svd(design_matrix,
387 Eigen::ComputeThinU | Eigen::ComputeThinV);
388 if (svd.rank() < design_matrix.cols()) {
391 return svd.solve(NumericMatrix::Identity(design_matrix.rows(), design_matrix.rows()));
394 NumericMatrix gram = design_matrix.transpose() * design_matrix;
396 gram.diagonal().array() += ridge;
398 return gram.ldlt().solve(design_matrix.transpose());
412template <JanusScalar Scalar>
414 bool normalized =
true) {
418 Scalar value = Scalar(0.0);
419 switch (dimension.
family) {
446 bool normalized =
true) {
462 : dimensions_(std::move(
dimensions)), order_(
order), options_(options) {
463 if (dimensions_.empty()) {
464 throw InvalidArgument(
"PolynomialChaosBasis: need at least one stochastic dimension");
469 for (
const auto &
dimension : dimensions_) {
474 static_cast<int>(dimensions_.size()), order_, options_.truncation);
476 terms_.reserve(indices.size());
477 squared_norms_.resize(
static_cast<Eigen::Index
>(indices.size()));
478 for (std::size_t i = 0; i < indices.size(); ++i) {
480 for (std::size_t axis = 0; axis < dimensions_.size(); ++axis) {
483 terms_.push_back(PolynomialChaosTerm{indices[i],
norm});
484 squared_norms_(
static_cast<Eigen::Index
>(i)) =
norm;
488 int dimension()
const {
return static_cast<int>(dimensions_.size()); }
490 int order()
const {
return order_; }
492 int size()
const {
return static_cast<int>(terms_.size()); }
498 const std::vector<PolynomialChaosDimension> &
dimensions()
const {
return dimensions_; }
500 const std::vector<PolynomialChaosTerm> &
terms()
const {
return terms_; }
504 template <JanusScalar Scalar>
507 throw InvalidArgument(
"PolynomialChaosBasis::evaluate(point): point dimension must "
508 "match the basis dimension");
512 for (
int term_idx = 0; term_idx <
size(); ++term_idx) {
513 Scalar value = Scalar(1.0);
514 for (
int axis = 0; axis <
dimension(); ++axis) {
515 value *=
pce_polynomial(dimensions_[
static_cast<std::size_t
>(axis)],
516 terms_[
static_cast<std::size_t
>(term_idx)]
517 .multi_index[
static_cast<std::size_t
>(axis)],
518 point(axis), options_.normalized);
520 values(term_idx) = value;
529 for (Eigen::Index row = 0; row < samples.rows(); ++row) {
531 design.row(row) =
evaluate(point).transpose();
537 std::vector<PolynomialChaosDimension> dimensions_;
540 std::vector<PolynomialChaosTerm> terms_;
553template <JanusScalar Scalar>
559 if (weights.size() != samples.rows()) {
560 throw InvalidArgument(
"pce_projection_coefficients: weights size must match the number of "
563 if (sample_values.size() != samples.rows()) {
565 "pce_projection_coefficients: sample value size must match the number of samples");
570 for (
int term_idx = 0; term_idx < basis.
size(); ++term_idx) {
571 Scalar accum = Scalar(0.0);
572 for (Eigen::Index sample_idx = 0; sample_idx < samples.rows(); ++sample_idx) {
573 accum += weights(sample_idx) * design(sample_idx, term_idx) * sample_values(sample_idx);
589template <JanusScalar Scalar>
595 if (weights.size() != samples.rows()) {
596 throw InvalidArgument(
"pce_projection_coefficients: weights size must match the number of "
599 if (sample_values.rows() != samples.rows()) {
600 throw InvalidArgument(
"pce_projection_coefficients: sample value rows must match the "
601 "number of samples");
606 for (
int term_idx = 0; term_idx < basis.
size(); ++term_idx) {
607 for (Eigen::Index col = 0; col < sample_values.cols(); ++col) {
608 Scalar accum = Scalar(0.0);
609 for (Eigen::Index sample_idx = 0; sample_idx < samples.rows(); ++sample_idx) {
610 accum += weights(sample_idx) * design(sample_idx, term_idx) *
611 sample_values(sample_idx, col);
613 coeffs(term_idx, col) = accum / basis.
squared_norms()(term_idx);
628template <JanusScalar Scalar>
648template <JanusScalar Scalar>
666 if (coefficients.size() == 0) {
667 throw InvalidArgument(
"pce_mean: coefficient vector must be non-empty");
669 return coefficients(0);
679template <JanusScalar Scalar>
681 if (coefficients.size() != basis.
size()) {
682 throw InvalidArgument(
"pce_variance: coefficient vector size must match the basis size");
685 Scalar variance = Scalar(0.0);
686 for (
int i = 1; i < coefficients.size(); ++i) {
687 variance += basis.
squared_norms()(i) * coefficients(i) * coefficients(i);
Scalar and element-wise arithmetic functions (abs, sqrt, pow, exp, log, etc.).
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.
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
const std::vector< PolynomialChaosDimension > & dimensions() const
Definition PolynomialChaos.hpp:498
bool normalized() const
Definition PolynomialChaos.hpp:494
int dimension() const
Definition PolynomialChaos.hpp:488
const std::vector< PolynomialChaosTerm > & terms() const
Definition PolynomialChaos.hpp:500
PolynomialChaosBasis(std::vector< PolynomialChaosDimension > dimensions, int order, PolynomialChaosBasisOptions options={})
Definition PolynomialChaos.hpp:460
NumericMatrix evaluate(const NumericMatrix &samples) const
Definition PolynomialChaos.hpp:525
int order() const
Definition PolynomialChaos.hpp:490
const NumericVector & squared_norms() const
Definition PolynomialChaos.hpp:502
PolynomialChaosTruncation truncation() const
Definition PolynomialChaos.hpp:496
PolynomialChaosBasis()=default
JanusVector< Scalar > evaluate(const JanusVector< Scalar > &point) const
Definition PolynomialChaos.hpp:505
int size() const
Definition PolynomialChaos.hpp:492
Smooth approximation of ReLU function: softplus(x) = (1/beta) * log(1 + exp(beta * x)).
Definition Diagnostics.hpp:131
Scalar raw_laguerre_polynomial(int degree, const Scalar &x, double alpha)
Definition PolynomialChaos.hpp:208
std::vector< std::vector< int > > generate_multi_indices(int dim, int order, PolynomialChaosTruncation truncation)
Definition PolynomialChaos.hpp:306
void tensor_product_recursive(int dim, int order, int axis, std::vector< int > ¤t, std::vector< std::vector< int > > &indices)
Definition PolynomialChaos.hpp:293
void validate_samples(const NumericMatrix &samples, int dimension, const std::string &context)
Definition PolynomialChaos.hpp:364
void validate_dimension(const PolynomialChaosDimension &dimension, const std::string &context)
Definition PolynomialChaos.hpp:112
Scalar raw_hermite_polynomial(int degree, const Scalar &x)
Definition PolynomialChaos.hpp:132
Scalar raw_legendre_polynomial(int degree, const Scalar &x)
Definition PolynomialChaos.hpp:152
Scalar raw_jacobi_polynomial(int degree, const Scalar &x, double alpha, double beta)
Definition PolynomialChaos.hpp:178
bool multi_index_less(const std::vector< int > &lhs, const std::vector< int > &rhs)
Definition PolynomialChaos.hpp:269
double squared_norm_probability(const PolynomialChaosDimension &dimension, int degree)
Definition PolynomialChaos.hpp:262
NumericMatrix regression_operator(const NumericMatrix &design_matrix, double ridge, const std::string &context)
Definition PolynomialChaos.hpp:375
void total_order_recursive(int dim, int order, int axis, std::vector< int > ¤t, std::vector< std::vector< int > > &indices)
Definition PolynomialChaos.hpp:278
double squared_norm_raw(const PolynomialChaosDimension &dimension, int degree)
Definition PolynomialChaos.hpp:232
void validate_degree(int degree, const std::string &context)
Definition PolynomialChaos.hpp:106
JanusVector< Scalar > apply_operator(const NumericMatrix &op, const JanusVector< Scalar > &values, const std::string &context)
Definition PolynomialChaos.hpp:325
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
Scalar pce_mean(const JanusVector< Scalar > &coefficients)
Extract PCE mean (zeroth coefficient).
Definition PolynomialChaos.hpp:665
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
PolynomialChaosDimension hermite_dimension()
Create a standard normal (Hermite) dimension.
Definition PolynomialChaos.hpp:57
PolynomialChaosDimension laguerre_dimension(double alpha=0.0)
Create a Gamma-family (Laguerre) dimension on [0, inf).
Definition PolynomialChaos.hpp:84
T sqrt(const T &x)
Computes the square root of a scalar.
Definition Arithmetic.hpp:46
PolynomialChaosDimension jacobi_dimension(double alpha, double beta)
Create a Beta-family (Jacobi) dimension on [-1, 1].
Definition PolynomialChaos.hpp:75
JanusVector< NumericScalar > NumericVector
Eigen::VectorXd equivalent.
Definition JanusTypes.hpp:67
PolynomialChaosTruncation
Multi-index truncation strategy for a multidimensional basis.
Definition PolynomialChaos.hpp:35
@ TensorProduct
Definition PolynomialChaos.hpp:37
@ TotalOrder
Definition PolynomialChaos.hpp:36
Scalar pce_variance(const PolynomialChaosBasis &basis, const JanusVector< Scalar > &coefficients)
Compute PCE variance from coefficients.
Definition PolynomialChaos.hpp:680
auto norm(const Eigen::MatrixBase< Derived > &x, NormType type=NormType::L2)
Computes vector/matrix norm.
Definition Linalg.hpp:607
@ Hermite
Definition AutoDiff.hpp:31
double pce_squared_norm(const PolynomialChaosDimension &dimension, int degree, bool normalized=true)
Return the probability-measure squared norm of a univariate basis term.
Definition PolynomialChaos.hpp:445
PolynomialChaosFamily
Univariate Askey-scheme family used for a PCE input dimension.
Definition PolynomialChaos.hpp:25
@ 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
JanusVector< Scalar > pce_regression_coefficients(const PolynomialChaosBasis &basis, const NumericMatrix &samples, const JanusVector< Scalar > &sample_values, double ridge=1e-12)
Compute PCE coefficients via least-squares regression (vector).
Definition PolynomialChaos.hpp:630
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > JanusVector
Dynamic-size column vector for both numeric and symbolic backends.
Definition JanusTypes.hpp:49
std::pair< double, double > legendre_poly(int n, double x)
Evaluate Legendre polynomial P_n(x) and derivative P'_n(x).
Definition OrthogonalPolynomials.hpp:77
Scalar pce_polynomial(const PolynomialChaosDimension &dimension, int degree, const Scalar &x, bool normalized=true)
Evaluate a univariate chaos basis polynomial.
Definition PolynomialChaos.hpp:413
PolynomialChaosDimension legendre_dimension()
Create a uniform (Legendre) dimension on [-1, 1].
Definition PolynomialChaos.hpp:65
Basis construction controls for multidimensional PCE.
Definition PolynomialChaos.hpp:91
bool normalized
Use orthonormal basis functions when true.
Definition PolynomialChaos.hpp:93
PolynomialChaosTruncation truncation
Definition PolynomialChaos.hpp:92
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
One multidimensional chaos basis term.
Definition PolynomialChaos.hpp:99
double squared_norm
Definition PolynomialChaos.hpp:101
std::vector< int > multi_index
Definition PolynomialChaos.hpp:100