LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
moment_tensor.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_TENSOR_H
6#define LINE_API_MOMENT_MOMENT_TENSOR_H
7
8/**
9 * @file
10 * @ingroup api_moment
11 * Joint moment arrays and the mode products used by every joint conversion.
12 *
13 * Templated port of matlab/src/api/moment/moment_tensorsize.m,
14 * moment_tensortrans.m and moment_jointtrans.m.
15 *
16 * The storage is COLUMN MAJOR, matching the MATLAB linear index exactly, so the
17 * stride of dimension l is prod(sz(1:l-1)). Trailing singleton dimensions are
18 * dropped on construction, which is the MATLAB convention that
19 * moment_tensorsize enforces: a column vector of n+1 entries and an (n+1)x1
20 * array of a degenerate second class are the same object, and both are read as
21 * the d = 1 case.
22 *
23 * The mode argument here is 0-BASED, unlike the 1-based MATLAB argument.
24 *
25 * Reference:
26 * A. Heindl and A. van de Liefvoort. Moment conversions for discrete
27 * distributions. PMCCS, 2003.
28 */
29
30#include <vector>
31
33#include "line/num/number.h"
34#include "line/util/error.h"
35#include "line/util/matrix.h"
36
37namespace line {
38namespace moment {
39
40/** Joint moment array of size (n_1+1)x...x(n_d+1), stored column major. */
41template <class T>
43 std::vector<std::size_t> sz;
44 std::vector<T> data;
45
46 MomentTensor() : sz(1, 0) {}
47
48 /** A plain moment vector, i.e. the d = 1 case. */
49 explicit MomentTensor(const std::vector<T>& v) : sz(1, v.size()), data(v) {}
50
51 /** A zero-filled array of the given extents, with trailing singletons dropped. */
52 explicit MomentTensor(const std::vector<std::size_t>& extents) : sz(extents) {
53 normalize();
55 }
56
57 std::size_t order() const { return sz.size(); }
58
59 std::size_t numel() const {
60 std::size_t n = 1;
61 for (std::size_t l = 0; l < sz.size(); ++l) n *= sz[l];
62 return n;
63 }
64
65 /** Stride of dimension l in the column-major layout. */
66 std::size_t stride(std::size_t l) const {
67 std::size_t s = 1;
68 for (std::size_t k = 0; k < l; ++k) s *= sz[k];
69 return s;
70 }
71
72 /** Linear index of the multi-index ord, whose entries are the moment orders. */
73 std::size_t index(const std::vector<std::size_t>& ord) const {
74 if (ord.size() != sz.size()) throw InputError("MomentTensor: wrong multi-index length");
75 std::size_t ia = 0, s = 1;
76 for (std::size_t l = 0; l < sz.size(); ++l) {
77 if (ord[l] >= sz[l]) throw InputError("MomentTensor: multi-index out of range");
78 ia += ord[l] * s;
79 s *= sz[l];
80 }
81 return ia;
82 }
83
84 const T& at(const std::vector<std::size_t>& ord) const { return data[index(ord)]; }
85 T& at(const std::vector<std::size_t>& ord) { return data[index(ord)]; }
86
87 /** Drops the trailing singleton dimensions, keeping at least one. */
88 void normalize() {
89 while (sz.size() > 1 && sz.back() == 1) sz.pop_back();
90 if (sz.empty()) sz.assign(1, 0);
91 }
92};
93
94/** Extents with the trailing singleton dimensions removed. */
95template <class T>
96std::vector<std::size_t> moment_tensorsize(const MomentTensor<T>& A) {
97 return A.sz;
98}
99
100/** Mode product: every fibre of A along dimension mode is replaced by Tm times that fibre. */
101template <class T>
103 std::size_t mode) {
104 if (mode >= A.order())
105 throw InputError("moment_tensortrans: the mode must be a dimension index of A");
106 const std::size_t n = A.sz[mode];
107 if (Tm.cols() != n || Tm.rows() != n)
108 throw InputError(
109 "moment_tensortrans: T must be square with as many columns as the extent of the "
110 "transformed dimension");
111 const std::size_t inner = A.stride(mode);
112 const std::size_t outer = A.numel() / (inner * n);
113 MomentTensor<T> B(A.sz);
114 std::vector<T> fibre(n);
115 for (std::size_t o = 0; o < outer; ++o)
116 for (std::size_t i = 0; i < inner; ++i) {
117 const std::size_t base = o * inner * n + i;
118 for (std::size_t k = 0; k < n; ++k) fibre[k] = A.data[base + k * inner];
119 for (std::size_t r = 0; r < n; ++r) {
120 T acc = num_traits<T>::from_int(0);
121 for (std::size_t k = 0; k < n; ++k) acc += Tm(r, k) * fibre[k];
122 B.data[base + r * inner] = acc;
123 }
124 }
125 return B;
126}
127
128/** Applies the conversion matrix of one edge along every dimension of A. */
129template <class T>
131 MomentTensor<T> B = A;
132 for (std::size_t mode = 0; mode < A.order(); ++mode)
133 B = moment_tensortrans<T>(B, moment_housematrix<T>(edge, static_cast<int>(A.sz[mode]) - 1),
134 mode);
135 return B;
136}
137
138/** String overload matching the MATLAB call signature. */
139template <class T>
140MomentTensor<T> moment_jointtrans(const MomentTensor<T>& A, const std::string& edge) {
142}
143
144} // namespace moment
145} // namespace line
146
147#endif
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
The exception types the port throws.
Dense matrix and non-owning view.
Conversion matrix of one edge of the house of moments.
std::vector< std::size_t > moment_tensorsize(const MomentTensor< T > &A)
Extents with the trailing singleton dimensions removed.
Matrix< T > moment_housematrix(MomentEdge edge, int n)
(n+1)x(n+1) conversion table of the given edge.
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_jointtrans(const MomentTensor< T > &A, MomentEdge edge)
Applies the conversion matrix of one edge along every dimension of A.
MomentEdge
Edge labels of the house of moments, one per MATLAB edge string.
MomentEdge moment_edge_from_string(const std::string &edge)
Parses the MATLAB edge string into its label.
Number-type abstraction for the templated API port.
Joint moment array of size (n_1+1)x...x(n_d+1), stored column major.
T & at(const std::vector< std::size_t > &ord)
std::size_t order() const
MomentTensor(const std::vector< T > &v)
A plain moment vector, i.e.
std::vector< std::size_t > sz
std::size_t stride(std::size_t l) const
Stride of dimension l in the column-major layout.
MomentTensor(const std::vector< std::size_t > &extents)
A zero-filled array of the given extents, with trailing singletons dropped.
const T & at(const std::vector< std::size_t > &ord) const
std::size_t numel() const
std::size_t index(const std::vector< std::size_t > &ord) const
Linear index of the multi-index ord, whose entries are the moment orders.
void normalize()
Drops the trailing singleton dimensions, keeping at least one.