Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
JointDistribution.cpp
Go to the documentation of this file.
1#include "JointDistribution.h"
2
3namespace {
4
5constexpr double kUClip = 1e-13;
6
7double finite_or_throw(double v, const std::string& label, std::size_t i) {
8 if (!std::isfinite(v)) {
9 std::ostringstream oss;
10 oss << "JointDistribution::curvature non-finite " << label
11 << " at index " << i;
12 throw std::runtime_error(oss.str());
13 }
14 return v;
15}
16
17} // namespace
18
20 std::vector<std::unique_ptr<IMarginalDistribution>> marginals,
21 std::unique_ptr<ICopula> copula) :
22 marginals_(std::move(marginals)), copula_(std::move(copula))
23{}
24
25std::vector<std::vector<double>> JointDistribution::sample(std::size_t n) const {
26 std::vector<std::vector<double>> u = this->copula_->sample_u(n);
27 std::vector<std::vector<double>> x (n, std::vector<double>(u[0].size(), 0.0));
28
29 for (size_t i = 0; i < marginals_.size(); i++) {
30 for (size_t j = 0; j < n; j++) {
31 x[j][i] = marginals_.at(i)->ppf(u[j][i]);
32 }
33 }
34
35 return x;
36}
37
38std::vector<double> JointDistribution::sample() const {
39 std::vector<double> u = this->copula_->sample_u();
40 std::vector<double> x (u.size(), 0.0);
41
42 for (size_t i = 0; i < marginals_.size(); i++) {
43 x[i] = marginals_.at(i)->ppf(u[i]);
44 }
45
46 return x;
47}
48
49double JointDistribution::logpdf(std::vector<double> x) const {
50 if (x.size() != marginals_.size())
51 throw std::invalid_argument("Wrong size of random vector.");
52
53 std::vector<double> u = std::vector<double>(x.size(), 0.0);
54 double log_marg {0.0};
55
56 for (size_t i = 0; i < marginals_.size(); i++) {
57 u[i] = std::clamp(marginals_[i]->cdf(x[i]), kUClip, 1.0 - kUClip);
58 log_marg += marginals_[i]->logpdf(x[i]);
59 }
60
61 return log_marg + copula_->log_density(u);
62}
63
64RealMatrix JointDistribution::curvature(std::vector<double> x) const {
65 if (x.size() != marginals_.size()) {
66 throw std::invalid_argument("Wrong size of random vector.");
67 }
68
69 const std::size_t d = x.size();
70
71 std::vector<double> u(d, 0.0);
72 RealMatrix W(d, d);
73
74 Vector f_i(d, 0.0);
75 Vector df_i(d, 0.0);
76 Vector d2logf_i(d, 0.0);
77
78 for (std::size_t i = 0; i < d; ++i) {
79 const double raw_u = marginals_[i]->cdf(x[i]);
80 u[i] = std::clamp(raw_u, kUClip, 1.0 - kUClip);
81
82 // Important numerical detail:
83 // If raw_u is far outside the clipped range, evaluating f, f', f''
84 // at the original x can underflow to zero while the copula derivatives
85 // are evaluated at the clipped u. That creates terms like 0 * inf or 0/0.
86 //
87 // Therefore the curvature uses the same effective point as the clipped u.
88 // For normal marginals this is equivalent to saturating the local curvature
89 // in extreme tails, and prevents non-finite W entries.
90 const double x_eff = marginals_[i]->ppf(u[i]);
91 PDFDiff fdf = marginals_[i]->f_df_ddf(x_eff);
92
93 if (!std::isfinite(fdf.f) || !(fdf.f > 0.0)) {
94 std::ostringstream oss;
95 oss << "JointDistribution::curvature invalid marginal density"
96 << " at index " << i
97 << " x=" << x[i]
98 << " x_eff=" << x_eff
99 << " u=" << u[i]
100 << " f=" << fdf.f;
101 throw std::runtime_error(oss.str());
102 }
103
104 finite_or_throw(fdf.df, "marginal df", i);
105 finite_or_throw(fdf.ddf, "marginal ddf", i);
106
107 f_i[i] = fdf.f;
108 df_i[i] = fdf.df;
109
110 const double dlogf = fdf.df / fdf.f;
111 d2logf_i[i] = fdf.ddf / fdf.f - dlogf * dlogf;
112
113 finite_or_throw(f_i[i], "marginal f", i);
114 finite_or_throw(df_i[i], "marginal df", i);
115 finite_or_throw(d2logf_i[i], "marginal d2logf", i);
116 }
117
118 LogDensityDiff cdc = copula_->log_c_dc_ddc(u);
119
120 for (std::size_t i = 0; i < d; ++i) {
121 finite_or_throw(cdc.dlog_c.at(i, 0), "copula dlog_c", i);
122
123 for (std::size_t j = 0; j < d; ++j) {
124 const double ddlogc = cdc.ddlog_c.at(i, j);
125 if (!std::isfinite(ddlogc)) {
126 std::ostringstream oss;
127 oss << "JointDistribution::curvature non-finite copula ddlog_c"
128 << " at (" << i << "," << j << ")";
129 throw std::runtime_error(oss.str());
130 }
131
132 double wij = -ddlogc * f_i[i] * f_i[j];
133
134 if (i == j) {
135 wij += -cdc.dlog_c.at(i, 0) * df_i[i] - d2logf_i[i];
136 }
137
138 if (!std::isfinite(wij)) {
139 std::ostringstream oss;
140 oss << "JointDistribution::curvature produced non-finite W"
141 << " at (" << i << "," << j << ")"
142 << " u_i=" << u[i]
143 << " u_j=" << u[j]
144 << " f_i=" << f_i[i]
145 << " f_j=" << f_i[j]
146 << " df_i=" << df_i[i]
147 << " d2logf_i=" << d2logf_i[i]
148 << " dlogc_i=" << cdc.dlog_c.at(i, 0)
149 << " ddlogc_ij=" << ddlogc;
150 throw std::runtime_error(oss.str());
151 }
152
153 W.at(i, j) = wij;
154 }
155 }
156
157 // Force exact symmetry. Curvature is a Hessian, so asymmetries here are numerical.
158 for (std::size_t i = 0; i < d; ++i) {
159 for (std::size_t j = i + 1; j < d; ++j) {
160 const double v = 0.5 * (W.at(i, j) + W.at(j, i));
161 W.at(i, j) = v;
162 W.at(j, i) = v;
163 }
164 }
165
166 return W;
167}
168
170 return marginals_.size();
171}
172
173std::vector<double> JointDistribution::get_stds() {
174 std::vector<double> stds;
175 for (auto& m : this->marginals_)
176 stds.emplace_back(m->std());
177
178 return stds;
179}
Joint probability distribution built from marginals and a copula.
std::vector< double > get_stds()
Returns the standard deviations of all marginals.
RealMatrix curvature(std::vector< double > x) const
Computes the negative Hessian of the joint log-density.
std::size_t dim()
Returns the dimension of the random vector.
double logpdf(std::vector< double > x) const
Evaluates the joint log-density at a given point.
std::vector< std::vector< double > > sample(std::size_t n) const
Draws multiple samples from the joint distribution.
double & at(size_t i, size_t j)
Returns a mutable reference to element (i,j) with bounds checking.
Definition Matrix.cpp:587
std::vector< double > Vector
csl::Expr v
Definition sm.h:110
Hash specialization for SymbolId<Tag>.
Definition BlockName.h:353
RealMatrix dlog_c
Definition ICopula.h:8
RealMatrix ddlog_c
Definition ICopula.h:9