LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
da_fpi.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_DA_DA_FPI_H
6
#define LINE_API_DA_DA_FPI_H
7
8
/**
9
* @file
10
* @ingroup api_da
11
* Damped fixed-point iteration, the shared driver of the decomposition
12
* algorithms.
13
*
14
* Templated port of matlab/src/api/da/da_fpi.m. The iteration is
15
* x_{k+1} = (1 - omega) x_ref + omega f(x_k, k)
16
* stopping when the configured norm of the increment falls below iter_tol, or
17
* after iter_max steps. MATLAB passes the iteration function as a handle
18
* returning both the new iterate and the reference point it should be damped
19
* against; the port takes a std::function with the same contract, so a caller
20
* whose reference differs from its input (as in the Erlang fixed point) is
21
* expressible without special-casing.
22
*
23
* THE INCREMENT NORM FOLLOWS MATLAB'S max(), which OMITS NaN and answers NaN
24
* only when every entry is one. The first form of this port skipped NaN entries
25
* but left `delta` at its initial 0, so an all-NaN increment -- a diverged
26
* iterate -- reported CONVERGENCE, where MATLAB's `NaN < iter_tol` is false and
27
* the loop continues to iter_max. Corrected here; a caller that wants to stop on
28
* a divergence asks for it with `nanstop`.
29
*
30
* A tolerance-driven loop is inexact by construction, whatever the arithmetic:
31
* the answer is the fixed point only to within iter_tol. The static_assert
32
* records that, so nobody instantiates it at exact arithmetic expecting an
33
* exact fixed point.
34
*/
35
36
#include <cmath>
37
#include <cstddef>
38
#include <functional>
39
#include <limits>
40
#include <utility>
41
#include <vector>
42
43
#include "
line/num/number.h
"
44
#include "
line/util/error.h
"
45
46
namespace
line
{
47
namespace
da
{
48
49
/** Options mirroring the fields MATLAB reads off the options struct. */
50
struct
FpiOptions
{
51
std::size_t
iter_max
= 10000;
52
double
iter_tol
= 1e-8;
53
double
damping
= 1.0;
///< omega; 1 means no damping
54
std::size_t
miniter
= 1;
///< iterations before the stopping test applies
55
bool
nanstop
=
false
;
///< stop when the increment norm is NaN (the reference's isnan test)
56
/**
57
* `config.da_norm`, the increment norm. MATLAB passes a function handle;
58
* the two handles the reference actually installs are both RELATIVE --
59
* `max(|xn-xr|./xr)` in solver_mam.m and `max(|xn-xr|./(xr+FineTol))` in
60
* solver_mam_basic_mmap_inner.m -- so they are expressed here as a flag and
61
* the denominator's offset rather than as a std::function, which would have
62
* to be templated on T and would change the type of every existing caller's
63
* options object.
64
*
65
* FALSE keeps the default absolute max-norm. TRUE with `relative_eps = 0`
66
* reproduces the first handle, including its division by a zero reference
67
* (the reference divides by `xr` unguarded, and an all-zero start therefore
68
* yields NaN on the first sweep; `miniter` is what keeps that from stopping
69
* the loop, exactly as in MATLAB).
70
*/
71
bool
relative_norm
=
false
;
72
double
relative_eps
= 0.0;
73
/**
74
* `config.da_norm` as a CALLABLE, the reference's function handle: `norm`
75
* receives the increment xnew - xref (in double) and `norm2` the pair
76
* (xnew, xref), MATLAB's one- and two-argument handles. Either takes
77
* precedence over the flags above when set; `norm2` over `norm`. With this
78
* a caller can install `@(d) norm(d,1)` (da_cacheqn, lossn_erlangfp) as
79
* `[](const std::vector<double>& d) { double s = 0; for (double v : d) s += std::fabs(v); return s; }`.
80
*/
81
std::function<double(
const
std::vector<double>&)>
norm
;
82
std::function<double(
const
std::vector<double>&,
const
std::vector<double>&)>
norm2
;
83
};
84
85
template
<
class
T>
86
struct
FpiResult
{
87
std::vector<T>
x
;
88
std::size_t
iterations
= 0;
89
bool
converged
=
false
;
90
};
91
92
/**
93
* @brief Damped fixed-point iteration, the shared driver of the decomposition
94
* algorithms.
95
*
96
* @param iterfun (x, iteration) -> (xnew, xref); xref is the point the damping
97
* and the increment norm are taken against
98
* @param x0 initial iterate
99
* @param options fixed-point options (tolerance, iteration cap, damping)
100
*/
101
template
<
class
T>
102
FpiResult<T>
da_fpi
(
103
const
std::function<std::pair<std::vector<T>, std::vector<T>>(
const
std::vector<T>&, std::size_t)>&
104
iterfun,
105
const
std::vector<T>& x0,
const
FpiOptions
& options =
FpiOptions
()) {
106
static_assert
(
num_traits<T>::has_transcendental
,
107
"da_fpi requires transcendental arithmetic: it stops on a tolerance, so its "
108
"result is the fixed point only to within iter_tol whatever the arithmetic"
);
109
if
(x0.empty())
throw
InputError
(
"da_fpi: empty initial iterate"
);
110
const
T omega =
num_traits<T>::from_double
(options.damping);
111
const
T one =
num_traits<T>::from_int
(1);
112
113
FpiResult<T>
r;
114
r.
x
= x0;
115
for
(std::size_t it = 1; it <= options.iter_max; ++it) {
116
r.
iterations
= it;
117
std::pair<std::vector<T>, std::vector<T>> step = iterfun(r.
x
, it);
118
std::vector<T>& xnew = step.first;
119
const
std::vector<T>& xref = step.second;
120
if
(xnew.size() != r.
x
.size() || xref.size() != r.
x
.size())
121
throw
InputError
(
"da_fpi: the iteration function changed the vector length"
);
122
123
if
(options.damping != 1.0)
124
for
(std::size_t i = 0; i < xnew.size(); ++i)
125
xnew[i] = (one - omega) * xref[i] + omega * xnew[i];
126
127
// MATLAB's max() OMITS NaN and answers NaN only when every entry is one,
128
// which is what lets the relative norm survive a zero reference entry on
129
// the first sweep instead of stopping the loop there.
130
double
delta = 0.0;
131
if
(options.norm2 || options.norm) {
132
// the reference's normfun handle: delta = normfun(xnew, xref) or normfun(xnew - xref)
133
std::vector<double> xn(xnew.size()), xr(xnew.size());
134
for
(std::size_t i = 0; i < xnew.size(); ++i) {
135
xn[i] =
num_traits<T>::to_double
(xnew[i]);
136
xr[i] =
num_traits<T>::to_double
(xref[i]);
137
}
138
if
(options.norm2) {
139
delta = options.norm2(xn, xr);
140
}
else
{
141
std::vector<double> d(xnew.size());
142
for
(std::size_t i = 0; i < xnew.size(); ++i) d[i] = xn[i] - xr[i];
143
delta = options.norm(d);
144
}
145
}
else
{
146
bool
anynum =
false
;
147
for
(std::size_t i = 0; i < xnew.size(); ++i) {
148
double
d = std::fabs(
num_traits<T>::to_double
(T(xnew[i] - xref[i])));
149
if
(options.relative_norm)
150
d /=
num_traits<T>::to_double
(xref[i]) + options.relative_eps;
151
if
(std::isnan(d))
continue
;
152
if
(!anynum || d > delta) delta = d;
153
anynum =
true
;
154
}
155
if
(!anynum && !xnew.empty()) delta = std::numeric_limits<double>::quiet_NaN();
156
}
157
r.
x
= xnew;
158
159
if
(it >= options.miniter) {
160
if
(delta < options.iter_tol) {
161
r.
converged
=
true
;
162
break
;
163
}
164
// the reference tests isnan(delta): an infinite increment keeps iterating
165
if
(options.nanstop && std::isnan(delta))
break
;
166
}
167
}
168
return
r;
169
}
170
171
}
// namespace da
172
}
// namespace line
173
174
#endif
// LINE_API_DA_DA_FPI_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:105
error.h
The exception types the port throws.
line::da
Definition
da_cache_isolate.h:45
line::da::da_fpi
FpiResult< T > da_fpi(const std::function< std::pair< std::vector< T >, std::vector< T > >(const std::vector< T > &, std::size_t)> &iterfun, const std::vector< T > &x0, const FpiOptions &options=FpiOptions())
Damped fixed-point iteration, the shared driver of the decomposition algorithms.
Definition
da_fpi.h:102
line
Conservation laws of a layered queueing network, enumerated from its structure.
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::da::FpiOptions
Options mirroring the fields MATLAB reads off the options struct.
Definition
da_fpi.h:50
line::da::FpiOptions::iter_max
std::size_t iter_max
Definition
da_fpi.h:51
line::da::FpiOptions::norm
std::function< double(const std::vector< double > &)> norm
config.da_norm as a CALLABLE, the reference's function handle: norm receives the increment xnew - xre...
Definition
da_fpi.h:81
line::da::FpiOptions::miniter
std::size_t miniter
iterations before the stopping test applies
Definition
da_fpi.h:54
line::da::FpiOptions::damping
double damping
omega; 1 means no damping
Definition
da_fpi.h:53
line::da::FpiOptions::relative_eps
double relative_eps
Definition
da_fpi.h:72
line::da::FpiOptions::iter_tol
double iter_tol
Definition
da_fpi.h:52
line::da::FpiOptions::relative_norm
bool relative_norm
config.da_norm, the increment norm.
Definition
da_fpi.h:71
line::da::FpiOptions::norm2
std::function< double(const std::vector< double > &, const std::vector< double > &)> norm2
Definition
da_fpi.h:82
line::da::FpiOptions::nanstop
bool nanstop
stop when the increment norm is NaN (the reference's isnan test)
Definition
da_fpi.h:55
line::da::FpiResult
Definition
da_fpi.h:86
line::da::FpiResult::iterations
std::size_t iterations
Definition
da_fpi.h:88
line::da::FpiResult::x
std::vector< T > x
Definition
da_fpi.h:87
line::da::FpiResult::converged
bool converged
Definition
da_fpi.h:89
line::num_traits
Definition
number.h:123
include
line
api
da
da_fpi.h
Generated by
1.18.0