LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
moment_joint.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_MOMENT_MOMENT_JOINT_H
6#define LINE_API_MOMENT_MOMENT_JOINT_H
7
8/**
9 * @file
10 * @ingroup api_moment
11 * Joint moment conversions on the house of moments.
12 *
13 * Templated port of the matlab/src/api/moment/moment_joint_*.m family. Every
14 * edge except the cumulant and the central ones is SEPARABLE: the joint table
15 * is the Kronecker product of the univariate tables, so the conversion is one
16 * mode product per dimension. The cumulant edges are not separable and carry
17 * their own multivariate recurrence, and the central edges are separable only
18 * once the mean vector is fixed.
19 *
20 * The joint cumulant recurrence picks j as the FIRST dimension with a positive
21 * order and differentiates in that variable. Any other choice gives the same
22 * answer, but not the same summation, so a port that picks the last dimension
23 * will disagree in floating point even when it is algebraically right.
24 *
25 * Reference:
26 * A. Heindl and A. van de Liefvoort. Moment conversions for discrete
27 * distributions. PMCCS, 2003.
28 */
29
30#include <algorithm>
31#include <vector>
32
35#include "line/num/number.h"
36#include "line/util/error.h"
37#include "line/util/matrix.h"
39
40namespace line {
41namespace moment {
42
43/** Joint binomial moments from joint factorial moments. */
44template <class T>
48
49/** Joint binomial moments from joint negative-binomial moments. */
50template <class T>
54
55/** Joint binomial moments from joint tail moments. */
56template <class T>
60
61/** Joint factorial moments from joint binomial moments. */
62template <class T>
66
67/** Joint factorial moments from joint raw moments. */
68template <class T>
72
73/** Joint factorial moments from joint upper-factorial moments. */
74template <class T>
78
79/** Joint negative-binomial moments from joint binomial moments. */
80template <class T>
84
85/** Joint negative-binomial moments from joint upper-factorial moments. */
86template <class T>
90
91/** Joint raw moments from joint factorial moments. */
92template <class T>
96
97/** Joint raw moments from joint upper-factorial moments. */
98template <class T>
102
103/** Joint tail moments from joint binomial moments. */
104template <class T>
108
109/** Joint upper-factorial moments from joint factorial moments. */
110template <class T>
114
115/** Joint upper-factorial moments from joint negative-binomial moments. */
116template <class T>
120
121/** Joint upper-factorial moments from joint raw moments. */
122template <class T>
126
127namespace detail {
128
129/** Advances a column-major multi-index within the box sz, MATLAB odometer order. */
130inline void moment_odometer(std::vector<std::size_t>& ord, const std::vector<std::size_t>& sz) {
131 for (std::size_t l = 0; l < ord.size(); ++l) {
132 ++ord[l];
133 if (ord[l] < sz[l]) return;
134 ord[l] = 0;
135 }
136}
137
138/**
139 * Shared body of the joint cumulant recurrence. With include_full = false the
140 * term b = a is skipped, which turns the raw-from-cumulant sum into the
141 * cumulant-from-raw correction.
142 */
143template <class T>
144std::vector<T> moment_joint_cumulant_body(const std::vector<std::size_t>& sz,
145 const std::vector<T>& src, bool cumulant_from_raw) {
146 const std::size_t d = sz.size();
147 std::size_t nel = 1;
148 for (std::size_t l = 0; l < d; ++l) nel *= sz[l];
149 std::vector<std::size_t> stride(d, 1);
150 for (std::size_t l = 1; l < d; ++l) stride[l] = stride[l - 1] * sz[l - 1];
151 std::vector<T> dst(nel, num_traits<T>::from_int(0));
152 if (!cumulant_from_raw) dst[0] = num_traits<T>::from_int(1);
153 std::vector<std::size_t> ord(d, 0), bord(d, 0);
154 for (std::size_t ia = 0; ia < nel; ++ia) {
155 std::size_t j = d;
156 for (std::size_t l = 0; l < d; ++l)
157 if (ord[l] > 0) {
158 j = l;
159 break;
160 }
161 if (j < d) {
162 T acc = num_traits<T>::from_int(0);
163 std::size_t nb = 1;
164 for (std::size_t l = 0; l < d; ++l) nb *= ord[l] + 1;
165 std::fill(bord.begin(), bord.end(), static_cast<std::size_t>(0));
166 for (std::size_t ib = 0; ib < nb; ++ib) {
167 bool any_pos = false, equal_a = true;
168 for (std::size_t l = 0; l < d; ++l) {
169 if (bord[l] > 0) any_pos = true;
170 if (bord[l] != ord[l]) equal_a = false;
171 }
172 const bool keep =
173 any_pos && bord[j] > 0 && (!cumulant_from_raw || !equal_a);
174 if (keep) {
175 T c = num_traits<T>::from_int(1);
176 for (std::size_t l = 0; l < d; ++l) {
177 const int alpha = static_cast<int>(ord[l]) - (l == j ? 1 : 0);
178 const int beta = static_cast<int>(bord[l]) - (l == j ? 1 : 0);
179 c *= num_nck<T>(alpha, beta);
180 }
181 std::size_t ib_lin = 0, ic_lin = 0;
182 for (std::size_t l = 0; l < d; ++l) {
183 ib_lin += bord[l] * stride[l];
184 ic_lin += (ord[l] - bord[l]) * stride[l];
185 }
186 // the cumulant factor is always the one indexed by b
187 acc += cumulant_from_raw ? c * dst[ib_lin] * src[ic_lin]
188 : c * src[ib_lin] * dst[ic_lin];
189 }
190 // the inner odometer runs over the box 0..ord, not over sz
191 for (std::size_t l = 0; l < d; ++l) {
192 ++bord[l];
193 if (bord[l] <= ord[l]) break;
194 bord[l] = 0;
195 }
196 }
197 dst[ia] = cumulant_from_raw ? src[ia] - acc : acc;
198 }
199 moment_odometer(ord, sz);
200 }
201 return dst;
202}
203
204} // namespace detail
205
206/** Joint cumulants from joint raw moments. */
207template <class T>
209 MomentTensor<T> kappa(m.sz);
210 kappa.data = detail::moment_joint_cumulant_body<T>(m.sz, m.data, true);
211 return kappa;
212}
213
214/** Joint raw moments from joint cumulants. */
215template <class T>
217 MomentTensor<T> m(kappa.sz);
218 m.data = detail::moment_joint_cumulant_body<T>(kappa.sz, kappa.data, false);
219 return m;
220}
221
222/** Joint factorial cumulants from joint factorial moments. */
223template <class T>
227
228/** Joint factorial moments from joint factorial cumulants. */
229template <class T>
233
234/** Joint central moments from joint raw moments about a given mean vector. */
235template <class T>
237 const std::vector<T>& mu) {
238 const std::size_t d = m.order();
239 if (mu.size() != d)
240 throw InputError(
241 "moment_joint_central_from_raw_mean: mu must have one entry per dimension of m");
243 for (std::size_t mode = 0; mode < d; ++mode) {
244 const int n = static_cast<int>(m.sz[mode]) - 1;
245 Matrix<T> Tm(static_cast<std::size_t>(n) + 1, static_cast<std::size_t>(n) + 1,
247 const T negmu = -mu[mode];
248 for (int i = 0; i <= n; ++i)
249 for (int k = 0; k <= i; ++k)
250 Tm(i, k) = num_nck<T>(i, k) * num_pow_int(negmu, static_cast<unsigned>(i - k));
251 mc = moment_tensortrans<T>(mc, Tm, mode);
252 }
253 return mc;
254}
255
256/** Joint central moments from joint raw moments, reading the means off m. */
257template <class T>
259 const std::size_t d = m.order();
260 for (std::size_t l = 0; l < d; ++l)
261 if (m.sz[l] < 2)
262 throw InputError(
263 "moment_joint_central_from_raw: the means m_(e_j) are required, hence every "
264 "dimension of m must have at least 2 elements");
265 std::vector<T> mu(d);
266 for (std::size_t l = 0; l < d; ++l) mu[l] = m.data[m.stride(l)];
268}
269
270/** Joint raw moments from joint central moments about a given mean vector. */
271template <class T>
273 const std::vector<T>& mu) {
274 const std::size_t d = mc.order();
275 if (mu.size() != d)
276 throw InputError(
277 "moment_joint_raw_from_central: mu must have one entry per dimension of mc");
279 for (std::size_t mode = 0; mode < d; ++mode) {
280 const int n = static_cast<int>(mc.sz[mode]) - 1;
281 Matrix<T> Tm(static_cast<std::size_t>(n) + 1, static_cast<std::size_t>(n) + 1,
283 for (int i = 0; i <= n; ++i)
284 for (int k = 0; k <= i; ++k)
285 Tm(i, k) = num_nck<T>(i, k) * num_pow_int(mu[mode], static_cast<unsigned>(i - k));
286 m = moment_tensortrans<T>(m, Tm, mode);
287 }
288 return m;
289}
290
291/** Joint central moments from joint tail moments. */
292template <class T>
298
299/** Factorial moments of a total count from the joint factorial moments of its parts. */
300template <class T>
301std::vector<T> moment_joint_aggregate(const MomentTensor<T>& F) {
302 const std::size_t d = F.order();
303 std::size_t nmax = F.sz[0];
304 for (std::size_t l = 1; l < d; ++l) nmax = F.sz[l] < nmax ? F.sz[l] : nmax;
305 if (nmax == 0) throw InputError("moment_joint_aggregate: F must be nonempty");
306 --nmax;
307 std::vector<T> f(nmax + 1, num_traits<T>::from_int(0));
308 std::vector<std::size_t> ord(d, 0);
309 const std::size_t nel = F.numel();
310 for (std::size_t ia = 0; ia < nel; ++ia) {
311 std::size_t n = 0;
312 for (std::size_t l = 0; l < d; ++l) n += ord[l];
313 if (n <= nmax) {
314 T c = num_factorial<T>(static_cast<unsigned>(n));
315 for (std::size_t l = 0; l < d; ++l)
316 c = c / num_factorial<T>(static_cast<unsigned>(ord[l]));
317 f[n] += c * F.data[ia];
318 }
319 detail::moment_odometer(ord, F.sz);
320 }
321 return f;
322}
323
324/** Joint factorial moments of a multinomially marked count from the aggregate ones. */
325template <class T>
326MomentTensor<T> moment_joint_marking(const std::vector<T>& f, const std::vector<T>& p,
327 const std::vector<std::size_t>& dims) {
328 const std::size_t d = p.size();
329 if (dims.size() != d)
330 throw InputError("moment_joint_marking: p and dims must have the same length");
331 std::size_t total = 0;
332 for (std::size_t l = 0; l < d; ++l) total += dims[l];
333 if (f.empty() || total > f.size() - 1)
334 throw InputError(
335 "moment_joint_marking: the aggregate factorial moments must reach order sum(dims)");
336 std::vector<std::size_t> sz(d);
337 for (std::size_t l = 0; l < d; ++l) sz[l] = dims[l] + 1;
338 MomentTensor<T> F(sz);
339 std::vector<std::size_t> ord(d, 0);
340 const std::size_t nel = F.numel();
341 for (std::size_t ia = 0; ia < nel; ++ia) {
342 std::size_t n = 0;
344 for (std::size_t l = 0; l < d; ++l) {
345 c *= num_pow_int(p[l], static_cast<unsigned>(ord[l]));
346 n += ord[l];
347 }
348 F.data[ia] = c * f[n];
349 detail::moment_odometer(ord, sz);
350 }
351 return F;
352}
353
354} // namespace moment
355} // namespace line
356
357#endif
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Dense matrix and non-owning view.
Conversion matrix of one edge of the house of moments.
Joint moment arrays and the mode products used by every joint conversion.
std::vector< T > moment_joint_aggregate(const MomentTensor< T > &F)
Factorial moments of a total count from the joint factorial moments of its parts.
MomentTensor< T > moment_joint_marking(const std::vector< T > &f, const std::vector< T > &p, const std::vector< std::size_t > &dims)
Joint factorial moments of a multinomially marked count from the aggregate ones.
MomentTensor< T > moment_joint_negbinomial_from_binomial(const MomentTensor< T > &b)
Joint negative-binomial moments from joint binomial moments.
MomentTensor< T > moment_joint_binomial_from_negbinomial(const MomentTensor< T > &bm)
Joint binomial moments from joint negative-binomial moments.
MomentTensor< T > moment_joint_binomial_from_factorial(const MomentTensor< T > &f)
Joint binomial moments from joint factorial moments.
MomentTensor< T > moment_joint_central_from_tail(const MomentTensor< T > &t)
Joint central moments from joint tail moments.
MomentTensor< T > moment_joint_central_from_raw(const MomentTensor< T > &m)
Joint central moments from joint raw moments, reading the means off m.
MomentTensor< T > moment_tensortrans(const MomentTensor< T > &A, const Matrix< T > &Tm, std::size_t mode)
Mode product: every fibre of A along dimension mode is replaced by Tm times that fibre.
MomentTensor< T > moment_joint_central_from_raw_mean(const MomentTensor< T > &m, const std::vector< T > &mu)
Joint central moments from joint raw moments about a given mean vector.
MomentTensor< T > moment_joint_factorial_from_factcumulant(const MomentTensor< T > &kappa)
Joint factorial moments from joint factorial cumulants.
MomentTensor< T > moment_joint_upfactorial_from_raw(const MomentTensor< T > &m)
Joint upper-factorial moments from joint raw moments.
MomentTensor< T > moment_joint_factorial_from_raw(const MomentTensor< T > &m)
Joint factorial moments from joint raw moments.
MomentTensor< T > moment_joint_upfactorial_from_negbinomial(const MomentTensor< T > &bm)
Joint upper-factorial moments from joint negative-binomial moments.
MomentTensor< T > moment_joint_factorial_from_binomial(const MomentTensor< T > &b)
Joint factorial moments from joint binomial moments.
MomentTensor< T > moment_joint_factorial_from_upfactorial(const MomentTensor< T > &fp)
Joint factorial moments from joint upper-factorial moments.
MomentTensor< T > moment_jointtrans(const MomentTensor< T > &A, MomentEdge edge)
Applies the conversion matrix of one edge along every dimension of A.
MomentTensor< T > moment_joint_raw_from_cumulant(const MomentTensor< T > &kappa)
Joint raw moments from joint cumulants.
MomentTensor< T > moment_joint_upfactorial_from_factorial(const MomentTensor< T > &f)
Joint upper-factorial moments from joint factorial moments.
MomentTensor< T > moment_joint_raw_from_upfactorial(const MomentTensor< T > &fp)
Joint raw moments from joint upper-factorial moments.
MomentTensor< T > moment_joint_raw_from_factorial(const MomentTensor< T > &f)
Joint raw moments from joint factorial moments.
MomentTensor< T > moment_joint_tail_from_binomial(const MomentTensor< T > &b)
Joint tail moments from joint binomial moments.
MomentTensor< T > moment_joint_factcumulant_from_factorial(const MomentTensor< T > &f)
Joint factorial cumulants from joint factorial moments.
MomentTensor< T > moment_joint_cumulant_from_raw(const MomentTensor< T > &m)
Joint cumulants from joint raw moments.
MomentTensor< T > moment_joint_raw_from_central(const MomentTensor< T > &mc, const std::vector< T > &mu)
Joint raw moments from joint central moments about a given mean vector.
MomentTensor< T > moment_joint_binomial_from_tail(const MomentTensor< T > &t)
Joint binomial moments from joint tail moments.
MomentTensor< T > moment_joint_negbinomial_from_upfactorial(const MomentTensor< T > &fp)
Joint negative-binomial moments from joint upper-factorial moments.
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
T num_nck(int n, int k)
Binomial coefficient as a value of T, by the Pascal recurrence.
Definition population.h:87
Number-type abstraction for the templated API port.
Population-vector enumeration and combinatorics.
Joint moment array of size (n_1+1)x...x(n_d+1), stored column major.
std::size_t order() const
std::vector< std::size_t > sz
std::size_t stride(std::size_t l) const
Stride of dimension l in the column-major layout.
std::size_t numel() const