LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sym_engine.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_SYM_SYM_ENGINE_H
6#define LINE_API_SYM_SYM_ENGINE_H
7
8/**
9 * @file
10 * @ingroup api_sym
11 * Computer algebra operations LINE needs, as seen by this port.
12 *
13 * Port of jline.api.sym.SymEngine. C++ has no computer algebra system any more
14 * than Java does, so every operation here is delegated to an external engine;
15 * SageRestEngine is the SageMath implementation and is resolved by
16 * sym_engines.h. Expressions cross the interface as plain ASCII infix strings,
17 * e.g. "2*x1 - 3*x2".
18 *
19 * EXPRESSION STRINGS ARE NOT COMPARABLE ACROSS CODEBASES, and not even across
20 * engine versions: the symbol numbering x1..xE follows event enumeration order
21 * and printed normal forms depend on the engine. Compare by substituting values
22 * with eval() and comparing numbers; a string diff against the JAR's output is
23 * a false failure waiting to happen.
24 *
25 * ABSENT VALUES. Java returns null for a field the request did not ask for.
26 * Here an absent scalar is the empty string and an absent list or matrix is
27 * empty, with an explicit `has*` flag where emptiness would otherwise be
28 * ambiguous.
29 */
30
31#include <map>
32#include <string>
33#include <vector>
34
35#include "line/util/error.h"
36
37namespace line {
38namespace sym {
39
40/** The symbolic backend is unreachable, or rejected the request. */
42public:
43 explicit SymEngineError(const std::string& what) : ExternalError(ErrorKind{"Sage"}, what) {}
44};
45
46/** One weighted sum of the stationary distribution, or a ratio of two. */
48 std::string name; ///< Caller supplied name
49 std::string expr; ///< The value as a rational function, or "undefined"
50 std::string num; ///< Numerator of `expr`, empty when undefined
51 std::string den; ///< Denominator of `expr`, empty when undefined
52 /**
53 * Why the value is undefined, empty otherwise.
54 *
55 * A ratio whose denominator measure is identically zero over the whole rate
56 * space has no value. The numeric arms sweep such a 0/0 to zero with an
57 * isnan pass, which is a defensible cleanup of floating point dust and an
58 * indefensible answer for an exact one.
59 */
60 std::string reason;
61
62 /** True if this measure has a value. */
63 bool isDefined() const { return reason.empty(); }
64};
65
66/** A ratio of two measures, by their index into the weight block. */
67struct RatioSpec {
68 std::string name; ///< Name of the resulting measure
69 std::size_t num = 0; ///< Index of the numerator measure
70 std::size_t den = 0; ///< Index of the denominator measure
71};
72
73/** Stationary distribution together with the measures taken from it. */
75 std::vector<std::string> pi; ///< Stationary probability of each state
76 std::vector<std::string> num; ///< Numerator of each entry over `den`
77 std::string den = "1"; ///< Common denominator of the whole vector
78 int nConnComp = 1; ///< Weakly connected components
79 std::vector<int> connComp; ///< Component index of each state, one based
80 std::vector<CtmcMeasure> measures; ///< One per weight vector, in order
81 std::vector<CtmcMeasure> ratios; ///< One per requested ratio, in order
82};
83
84/** First passage time transform and moments. */
85struct Passage {
86 std::string lst; ///< The transform L(s), empty if not requested
87 std::string lstNum; ///< Numerator of `lst`
88 std::string lstDen; ///< Denominator of `lst`
89 std::vector<std::string> lstAll; ///< Per start state transform
90 std::vector<std::string> moments; ///< Moments 1..nmax for the initial law
91 std::vector<std::vector<std::string>> momAll; ///< Per start state moments
92 std::vector<int> unreachable; ///< States that cannot reach the target
93};
94
95/** Symbolic stationary distribution of a CTMC. */
97 std::vector<std::string> pi; ///< Stationary probability of each state, as an expression
98 std::vector<std::string> num; ///< Numerator of each entry over the common denominator
99 std::string den = "1"; ///< Common denominator of the whole vector
100 int nConnComp = 1; ///< Weakly connected components of the generator
101 std::vector<int> connComp; ///< Component index of each state, one based
102};
103
104/**
105 * Exact parametric sensitivity, following Trivedi and Bobbio (2017), Sec. 9.7.
106 *
107 * As in `@SolverCTMC/getSensitivity.m`, dr/dtheta is taken to be zero: a reward
108 * whose rates themselves depend on theta needs the second term of Eq. (9.83)
109 * and is not covered here.
110 */
112 std::vector<std::string> pi; ///< Stationary distribution
113 std::vector<std::string> dpi; ///< Derivative of the distribution with respect to theta
114 std::string Er; ///< Mean reward, empty if no reward was given
115 std::string S; ///< Unscaled sensitivity d(E[r])/dtheta, Eq. (9.79)
116 std::string SS; ///< Scaled sensitivity (theta/E[r]) d(E[r])/dtheta, Eq. (9.80)
117 bool hasReward = false; ///< Whether Er, S and SS were computed
118};
119
120/** Symbolic analysis of a fluid vector field. */
121struct FluidODEs {
122 std::vector<std::vector<std::string>> jacobian; ///< d f_i / d x_j
123 std::vector<std::string> latex; ///< LaTeX form of each right hand side
124 std::vector<std::map<std::string, std::string>> equilibria; ///< variable -> expression
125 bool hasJacobian = false;
126 bool hasLatex = false;
127 bool hasEquilibria = false;
128};
129
130/** A computer algebra backend. */
132public:
133 virtual ~SymEngine() {}
134
135 /** Name of the backing engine, e.g. "sage". */
136 virtual std::string name() const = 0;
137
138 /** True if the engine answers a health probe. */
139 virtual bool isAvailable() const = 0;
140
141 /**
142 * Symbolic stationary distribution of a CTMC, pi Q = 0 with sum(pi) = 1.
143 *
144 * @param Q generator entries as expression strings, row major and square
145 * @param symbols the symbols appearing in Q, e.g. x1..xE
146 * @return the solution, with pi also split over a common denominator
147 */
148 virtual CtmcSolution solveCTMC(const std::vector<std::vector<std::string>>& Q,
149 const std::vector<std::string>& symbols) = 0;
150
151 /**
152 * Exact parametric sensitivity of a steady-state reward.
153 *
154 * @param Q generator entries as expression strings, row major
155 * @param symbols the symbols appearing in Q
156 * @param theta the symbol to differentiate with respect to
157 * @param reward reward rate per state, empty for the distribution alone
158 * @return the sensitivity of the distribution and, if a reward is given, of its mean
159 */
160 virtual SymSensitivity ctmcSensitivity(const std::vector<std::vector<std::string>>& Q,
161 const std::vector<std::string>& symbols,
162 const std::string& theta,
163 const std::vector<std::string>& reward) = 0;
164
165 /**
166 * Stationary distribution and a set of weighted sums of it.
167 *
168 * Every mean SolverCTMC reports is a NUMERIC linear functional of pi, so
169 * the caller builds one weight vector per measure with no algebra at all
170 * and this one round trip returns each `w . pi` as a rational function: a
171 * throughput is `pi . depRates`, a queue length is `pi . stateSpaceAggr`, a
172 * marginal probability is `pi . indicator` and a mean reward is `pi . r`.
173 *
174 * @param Q generator entries as expression strings, row major
175 * @param symbols the symbols appearing in Q
176 * @param weights one row per measure, each of length Q.size(); entries are
177 * parsed in the same field as Q, so an expression is
178 * admissible and not only a number
179 * @param names a name per weight row, or empty for w0, w1, ...
180 * @param ratios ratios of two measures, or empty for none
181 */
182 virtual CtmcMeasures ctmcMeasures(const std::vector<std::vector<std::string>>& Q,
183 const std::vector<std::string>& symbols,
184 const std::vector<std::vector<std::string>>& weights,
185 const std::vector<std::string>& names,
186 const std::vector<RatioSpec>& ratios) = 0;
187
188 /**
189 * First passage time into a target set: the transform and the moments.
190 *
191 * With A the complement of the target, S = Q(A,A) the sub-generator,
192 * s0 = -S*1 the exit vector and alpha the initial law restricted to A,
193 * following Harrison and Knottenbelt (2002),
194 *
195 * L(s) = alpha (sI - S)^-1 s0 + atom Eqs. 1-2
196 * (-S) M(n) = n M(n-1), M(0) = 1 Eq. 3
197 *
198 * THE MOMENTS COME FROM THE RECURSION, not from differentiating L: it
199 * carries no transform symbol, stays in the same fraction field and is
200 * `nmax` right solves against one matrix. Differentiating would leave the
201 * fraction field, and the evaluation at s = 0 can hit 0/0 where a factor of
202 * s failed to cancel.
203 *
204 * @param S the sub-generator on the non-target states, row major
205 * @param s0 the exit vector -S*1
206 * @param alpha the initial law restricted to the non-target states
207 * @param atom the initial mass already inside the target set
208 * @param symbols the rate symbols appearing in S
209 * @param svar the transform symbol; adjoined to the field only when a
210 * transform is asked for, and required to differ from every
211 * rate symbol
212 * @param want any of lst, lstall, moments, momall
213 * @param nmax highest moment order, at least 1
214 */
215 virtual Passage ctmcPassage(const std::vector<std::vector<std::string>>& S,
216 const std::vector<std::string>& s0,
217 const std::vector<std::string>& alpha,
218 const std::string& atom,
219 const std::vector<std::string>& symbols,
220 const std::string& svar,
221 const std::vector<std::string>& want, int nmax) = 0;
222
223 /**
224 * Rewrites expressions into a normal form.
225 *
226 * @param exprs the expressions
227 * @param form one of simplify, factor, together, cancel, expand, latex
228 * @return the rewritten expressions, in the input order
229 */
230 virtual std::vector<std::string> simplify(const std::vector<std::string>& exprs,
231 const std::string& form) = 0;
232
233 /**
234 * Differentiates expressions.
235 *
236 * @param exprs the expressions
237 * @param variable the differentiation variable
238 * @param order the order of the derivative, at least 1
239 * @return the derivatives, in the input order
240 */
241 virtual std::vector<std::string> diff(const std::vector<std::string>& exprs,
242 const std::string& variable, int order) = 0;
243
244 /**
245 * Substitutes values for symbols and evaluates.
246 *
247 * @param exprs the expressions
248 * @param assignment value of each symbol
249 * @return the numeric values, NaN where a free symbol remains
250 */
251 virtual std::vector<double> eval(const std::vector<std::string>& exprs,
252 const std::map<std::string, double>& assignment) = 0;
253
254 /**
255 * Jacobian, LaTeX form and equilibria of a fluid vector field.
256 *
257 * @param rhs the right hand side of dx/dt, one expression per state variable
258 * @param vars the state variable names
259 * @param want any of jacobian, latex, equilibria
260 * @return the requested items
261 */
262 FluidODEs fluidODEs(const std::vector<std::string>& rhs, const std::vector<std::string>& vars,
263 const std::vector<std::string>& want) {
264 return fluidODEs(rhs, vars, want, std::vector<std::string>(), std::string());
265 }
266
267 /**
268 * Jacobian, LaTeX form and equilibria of a fluid vector field, the
269 * equilibria pinned by invariants the drift conserves but f(x) = 0 cannot see.
270 *
271 * A closed chain's population is such an invariant: without `sum x = N` the
272 * equilibria of a closed model are a continuum, which the engine returns as a
273 * free-parameter family or, on p-norm radicals, never finishes solving.
274 *
275 * @param rhs the right hand side of dx/dt, one expression per state variable
276 * @param vars the state variable names
277 * @param want any of jacobian, latex, equilibria
278 * @param constraints expressions g(x) that each equilibrium must also zero, e.g. "x1 + x2 - 3"
279 * @param domain empty for the engine's default (real), or "nonnegative" to keep x >= 0 only
280 * @return the requested items
281 */
282 virtual FluidODEs fluidODEs(const std::vector<std::string>& rhs,
283 const std::vector<std::string>& vars,
284 const std::vector<std::string>& want,
285 const std::vector<std::string>& constraints,
286 const std::string& domain) = 0;
287};
288
289} // namespace sym
290} // namespace line
291
292#endif // LINE_API_SYM_SYM_ENGINE_H
ExternalError(const std::string &what)
Definition error.h:126
SymEngineError(const std::string &what)
Definition sym_engine.h:43
A computer algebra backend.
Definition sym_engine.h:131
virtual bool isAvailable() const =0
True if the engine answers a health probe.
virtual std::string name() const =0
Name of the backing engine, e.g.
virtual CtmcSolution solveCTMC(const std::vector< std::vector< std::string > > &Q, const std::vector< std::string > &symbols)=0
Symbolic stationary distribution of a CTMC, pi Q = 0 with sum(pi) = 1.
virtual std::vector< std::string > diff(const std::vector< std::string > &exprs, const std::string &variable, int order)=0
Differentiates expressions.
virtual SymSensitivity ctmcSensitivity(const std::vector< std::vector< std::string > > &Q, const std::vector< std::string > &symbols, const std::string &theta, const std::vector< std::string > &reward)=0
Exact parametric sensitivity of a steady-state reward.
virtual std::vector< std::string > simplify(const std::vector< std::string > &exprs, const std::string &form)=0
Rewrites expressions into a normal form.
virtual std::vector< double > eval(const std::vector< std::string > &exprs, const std::map< std::string, double > &assignment)=0
Substitutes values for symbols and evaluates.
FluidODEs fluidODEs(const std::vector< std::string > &rhs, const std::vector< std::string > &vars, const std::vector< std::string > &want)
Jacobian, LaTeX form and equilibria of a fluid vector field.
Definition sym_engine.h:262
virtual CtmcMeasures ctmcMeasures(const std::vector< std::vector< std::string > > &Q, const std::vector< std::string > &symbols, const std::vector< std::vector< std::string > > &weights, const std::vector< std::string > &names, const std::vector< RatioSpec > &ratios)=0
Stationary distribution and a set of weighted sums of it.
virtual FluidODEs fluidODEs(const std::vector< std::string > &rhs, const std::vector< std::string > &vars, const std::vector< std::string > &want, const std::vector< std::string > &constraints, const std::string &domain)=0
Jacobian, LaTeX form and equilibria of a fluid vector field, the equilibria pinned by invariants the ...
virtual Passage ctmcPassage(const std::vector< std::vector< std::string > > &S, const std::vector< std::string > &s0, const std::vector< std::string > &alpha, const std::string &atom, const std::vector< std::string > &symbols, const std::string &svar, const std::vector< std::string > &want, int nmax)=0
First passage time into a target set: the transform and the moments.
The exception types the port throws.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
One weighted sum of the stationary distribution, or a ratio of two.
Definition sym_engine.h:47
std::string name
Caller supplied name.
Definition sym_engine.h:48
std::string num
Numerator of expr, empty when undefined.
Definition sym_engine.h:50
std::string reason
Why the value is undefined, empty otherwise.
Definition sym_engine.h:60
std::string expr
The value as a rational function, or "undefined".
Definition sym_engine.h:49
std::string den
Denominator of expr, empty when undefined.
Definition sym_engine.h:51
bool isDefined() const
True if this measure has a value.
Definition sym_engine.h:63
Stationary distribution together with the measures taken from it.
Definition sym_engine.h:74
std::vector< CtmcMeasure > ratios
One per requested ratio, in order.
Definition sym_engine.h:81
std::string den
Common denominator of the whole vector.
Definition sym_engine.h:77
std::vector< CtmcMeasure > measures
One per weight vector, in order.
Definition sym_engine.h:80
int nConnComp
Weakly connected components.
Definition sym_engine.h:78
std::vector< std::string > num
Numerator of each entry over den.
Definition sym_engine.h:76
std::vector< int > connComp
Component index of each state, one based.
Definition sym_engine.h:79
std::vector< std::string > pi
Stationary probability of each state.
Definition sym_engine.h:75
Symbolic stationary distribution of a CTMC.
Definition sym_engine.h:96
std::vector< std::string > pi
Stationary probability of each state, as an expression.
Definition sym_engine.h:97
std::vector< int > connComp
Component index of each state, one based.
Definition sym_engine.h:101
std::vector< std::string > num
Numerator of each entry over the common denominator.
Definition sym_engine.h:98
int nConnComp
Weakly connected components of the generator.
Definition sym_engine.h:100
std::string den
Common denominator of the whole vector.
Definition sym_engine.h:99
Symbolic analysis of a fluid vector field.
Definition sym_engine.h:121
std::vector< std::string > latex
LaTeX form of each right hand side.
Definition sym_engine.h:123
std::vector< std::map< std::string, std::string > > equilibria
variable -> expression
Definition sym_engine.h:124
std::vector< std::vector< std::string > > jacobian
d f_i / d x_j
Definition sym_engine.h:122
First passage time transform and moments.
Definition sym_engine.h:85
std::vector< std::vector< std::string > > momAll
Per start state moments.
Definition sym_engine.h:91
std::vector< std::string > lstAll
Per start state transform.
Definition sym_engine.h:89
std::string lstNum
Numerator of lst.
Definition sym_engine.h:87
std::string lst
The transform L(s), empty if not requested.
Definition sym_engine.h:86
std::vector< std::string > moments
Moments 1..nmax for the initial law.
Definition sym_engine.h:90
std::vector< int > unreachable
States that cannot reach the target.
Definition sym_engine.h:92
std::string lstDen
Denominator of lst.
Definition sym_engine.h:88
A ratio of two measures, by their index into the weight block.
Definition sym_engine.h:67
std::size_t num
Index of the numerator measure.
Definition sym_engine.h:69
std::string name
Name of the resulting measure.
Definition sym_engine.h:68
std::size_t den
Index of the denominator measure.
Definition sym_engine.h:70
Exact parametric sensitivity, following Trivedi and Bobbio (2017), Sec.
Definition sym_engine.h:111
bool hasReward
Whether Er, S and SS were computed.
Definition sym_engine.h:117
std::vector< std::string > dpi
Derivative of the distribution with respect to theta.
Definition sym_engine.h:113
std::vector< std::string > pi
Stationary distribution.
Definition sym_engine.h:112
std::string SS
Scaled sensitivity (theta/E[r]) d(E[r])/dtheta, Eq. (9.80).
Definition sym_engine.h:116
std::string Er
Mean reward, empty if no reward was given.
Definition sym_engine.h:114
std::string S
Unscaled sensitivity d(E[r])/dtheta, Eq. (9.79).
Definition sym_engine.h:115