LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
map_fit_detail.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_MAM_MAP_FIT_DETAIL_H
6#define LINE_API_MAM_MAP_FIT_DETAIL_H
7
8/**
9 * @file
10 * @ingroup api_mam
11 * Scalar helpers shared by the MAP/PH moment-matching headers.
12 *
13 * These are not ports of any MATLAB function: they are the arithmetic
14 * primitives the closed forms need (square root, exponential, logarithm,
15 * integer power) resolved through argument-dependent lookup so that the same
16 * expression compiles for double and for Boost.Multiprecision types, plus a
17 * minimal complex arithmetic type.
18 *
19 * The complex type exists because aph_fit's second fitting case (the
20 * Bobbio-Horvath-Telek chain K9..K22) takes square and cube roots of
21 * quantities that are negative for many *feasible* moment sets; the imaginary
22 * parts cancel in the final f. MATLAB evaluates that chain in complex
23 * arithmetic implicitly and the JAR does it explicitly with
24 * org.apache.commons.math3.complex.Complex, so the port must too. Roots use
25 * the principal branch, matching both references.
26 */
27
28#include <cmath>
29
30#include "line/num/number.h"
31#include "line/util/error.h"
32
33namespace line {
34namespace mam {
35namespace fitdetail {
36
37template <class T>
38inline T num_sqrt(const T& v) {
39 using std::sqrt;
40 return sqrt(v);
41}
42
43template <class T>
44inline T num_exp(const T& v) {
45 using std::exp;
46 return exp(v);
47}
48
49template <class T>
50inline T num_log(const T& v) {
51 using std::log;
52 return log(v);
53}
54
55template <class T>
56inline T num_atan2(const T& y, const T& x) {
57 using std::atan2;
58 return atan2(y, x);
59}
60
61template <class T>
62inline T num_cos(const T& v) {
63 using std::cos;
64 return cos(v);
65}
66
67template <class T>
68inline T num_sin(const T& v) {
69 using std::sin;
70 return sin(v);
71}
72
73/** Shorthand for the integer powers that litter the symbolic expressions. */
74template <class T>
75inline T pw(const T& v, unsigned e) {
76 return num_pow_int(v, e);
77}
78
79/**
80 * Minimal complex number over T. Only the operations the aph_fit chain needs
81 * are provided. Division is the naive formula: the magnitudes involved stay
82 * far from the overflow limits of any of the supported backends.
83 */
84template <class T>
85struct Cplx {
86 T re;
87 T im;
88
89 Cplx() : re(num_traits<T>::from_int(0)), im(num_traits<T>::from_int(0)) {}
90 explicit Cplx(const T& r) : re(r), im(num_traits<T>::from_int(0)) {}
91 Cplx(const T& r, const T& i) : re(r), im(i) {}
92};
93
94template <class T>
95inline Cplx<T> operator+(const Cplx<T>& a, const Cplx<T>& b) {
96 return Cplx<T>(T(a.re + b.re), T(a.im + b.im));
97}
98
99template <class T>
100inline Cplx<T> operator-(const Cplx<T>& a, const Cplx<T>& b) {
101 return Cplx<T>(T(a.re - b.re), T(a.im - b.im));
102}
103
104template <class T>
105inline Cplx<T> operator-(const Cplx<T>& a) {
106 return Cplx<T>(T(-a.re), T(-a.im));
107}
108
109template <class T>
110inline Cplx<T> operator*(const Cplx<T>& a, const Cplx<T>& b) {
111 return Cplx<T>(T(a.re * b.re - a.im * b.im), T(a.re * b.im + a.im * b.re));
112}
113
114template <class T>
115inline Cplx<T> operator*(const Cplx<T>& a, const T& s) {
116 return Cplx<T>(T(a.re * s), T(a.im * s));
117}
118
119template <class T>
120inline Cplx<T> operator/(const Cplx<T>& a, const Cplx<T>& b) {
121 const T d = b.re * b.re + b.im * b.im;
122 if (d == num_traits<T>::from_int(0)) throw NumericError("Cplx: division by zero");
123 return Cplx<T>(T((a.re * b.re + a.im * b.im) / d), T((a.im * b.re - a.re * b.im) / d));
124}
125
126template <class T>
127inline Cplx<T> operator/(const Cplx<T>& a, const T& s) {
128 if (s == num_traits<T>::from_int(0)) throw NumericError("Cplx: division by zero");
129 return Cplx<T>(T(a.re / s), T(a.im / s));
130}
131
132template <class T>
133inline T cplx_abs(const Cplx<T>& a) {
134 return num_sqrt(T(a.re * a.re + a.im * a.im));
135}
136
137/** Principal square root. */
138template <class T>
139inline Cplx<T> cplx_sqrt(const Cplx<T>& a) {
140 const T zero = num_traits<T>::from_int(0);
141 if (a.im == zero) {
142 if (a.re >= zero) return Cplx<T>(num_sqrt(a.re), zero);
143 return Cplx<T>(zero, num_sqrt(T(-a.re)));
144 }
145 const T r = cplx_abs(a);
146 const T two = num_traits<T>::from_int(2);
147 const T u = num_sqrt(T((r + a.re) / two));
148 T v = num_sqrt(T((r - a.re) / two));
149 if (a.im < zero) v = -v;
150 return Cplx<T>(u, v);
151}
152
153/**
154 * Principal z^x for a real exponent x: exp(x log z) with the principal
155 * logarithm, which is what Apache Commons Math Complex.pow(double) computes.
156 */
157template <class T>
158inline Cplx<T> cplx_pow_real(const Cplx<T>& a, const T& x) {
159 const T zero = num_traits<T>::from_int(0);
160 const T r = cplx_abs(a);
161 if (r == zero) return Cplx<T>(zero, zero);
162 const T theta = num_atan2(a.im, a.re);
163 const T lr = num_log(r);
164 const T mag = num_exp(T(x * lr));
165 const T ang = x * theta;
166 return Cplx<T>(T(mag * num_cos(ang)), T(mag * num_sin(ang)));
167}
168
169/** 1/z. */
170template <class T>
171inline Cplx<T> cplx_inv(const Cplx<T>& a) {
172 return Cplx<T>(num_traits<T>::from_int(1)) / a;
173}
174
175} // namespace fitdetail
176} // namespace mam
177} // namespace line
178
179#endif // LINE_API_MAM_MAP_FIT_DETAIL_H
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
Number-type abstraction for the templated API port.