LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_setters.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_SN_SN_SETTERS_H
6#define LINE_API_SN_SN_SETTERS_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * Ports of the sn_set_* family and of sn_refresh_process_fields
12 * (matlab/src/api/sn).
13 *
14 * These edit an ALREADY REFRESHED struct in place. The model layer is the
15 * normal way to change a network, but a sensitivity sweep, a design-of-
16 * experiments driver or an optimiser wants to move one number and re-solve
17 * without rebuilding and re-refreshing the whole struct -- rebuilding would
18 * also renumber chains and visits, which is exactly what such a sweep must
19 * hold fixed.
20 *
21 * WHAT `refresh` MEANS HERE. Only the caller knows whether the edit invalidated
22 * the derived fields, so each setter takes the flag rather than deciding: a
23 * rate or SCV change invalidates the process representation, a population
24 * change invalidates the visits, and everything else invalidates nothing.
25 *
26 * sn_refresh_process_fields rebuilds the (D0, D1) representation from the first
27 * two moments alone, by the reference's rule: SCV one is exponential, SCV below
28 * one is Erlang-ceil(1/SCV), SCV above one is a two-phase hyperexponential, and
29 * a hyperexponential that comes out infeasible falls back to exponential. A
30 * non-positive, infinite or undefined rate is left alone -- there is no
31 * representation to build.
32 *
33 * ARITHMETIC: field for everything but map_hyperexp, which needs a square root
34 * and therefore refuses under exact arithmetic; an SCV above one is the only
35 * path that reaches it.
36 */
37
38#include <cmath>
39#include <cstddef>
40#include <vector>
41
45#include "line/num/number.h"
46#include "line/util/error.h"
47
48namespace line {
49namespace api {
50
51/**
52 * Port of sn_refresh_process_fields: rebuild `sn.service[ist][r]` from the
53 * (rate, SCV) pair currently in `sn.rates` and `sn.scv`.
54 *
55 * Indices are 1-based, as everywhere in this api layer.
56 */
57template <class T>
58void sn_refresh_process_fields(qn::NetworkStruct<T>& sn, std::size_t ist, std::size_t r) {
59 const double rate = num_traits<T>::to_double(sn.rates(ist - 1, r - 1));
60 if (!std::isfinite(rate) || !(rate > 0.0)) return;
61 const T mean = T(num_traits<T>::from_int(1) / sn.rates(ist - 1, r - 1));
62 const double scv = num_traits<T>::to_double(sn.scv(ist - 1, r - 1));
63 lang::Distrib<T>& d = sn.service[ist - 1][r - 1];
66 if (std::isnan(scv) || std::fabs(scv - 1.0) < 1e-10) {
67 m = mam::map_exponential(sn.rates(ist - 1, r - 1));
68 } else if (scv < 1.0) {
69 const unsigned k = static_cast<unsigned>(std::max(1.0, std::ceil(1.0 / scv)));
70 m = mam::map_erlang(mean, k);
72 } else {
73 bool ok = true;
74 try {
75 m = mam::map_hyperexp(mean, sn.scv(ist - 1, r - 1),
78 } catch (const Error&) {
79 ok = false;
80 }
81 if (!ok || m.D0.rows() == 0) {
82 m = mam::map_exponential(sn.rates(ist - 1, r - 1));
84 }
85 }
86 d.type = pt;
87 d.disabled = false;
88 d.D0 = m.D0;
89 d.D1 = m.D1;
90 d.mean = mean;
91 d.scv = sn.scv(ist - 1, r - 1);
92 sn.disabled[ist - 1][r - 1] = false;
93}
94
95/** Port of sn_set_service: write a (rate, SCV) pair at one (station, class). */
96template <class T>
97void sn_set_service(qn::NetworkStruct<T>& sn, std::size_t ist, std::size_t r, const T& rate,
98 const T& scv, bool auto_refresh = false) {
99 sn.rates(ist - 1, r - 1) = rate;
100 sn.scv(ist - 1, r - 1) = scv;
101 if (auto_refresh) sn_refresh_process_fields(sn, ist, r);
102}
103
104/** Port of sn_set_arrival: the same, at whichever station is the Source. */
105template <class T>
106void sn_set_arrival(qn::NetworkStruct<T>& sn, std::size_t r, const T& rate, const T& scv,
107 bool auto_refresh = false) {
108 std::size_t ist = 0;
109 for (std::size_t a = 0; a < sn.nodes.size(); ++a)
110 if (sn.nodes[a].nodetype == qn::NodeType::Source) ist = sn.nodes[a].station;
111 if (ist == 0) throw InputError("sn_set_arrival: no Source station found in network");
112 sn_set_service(sn, ist, r, rate, scv, auto_refresh);
113}
114
115/**
116 * Port of sn_set_service_batch: write a whole (rate, SCV) table, skipping the
117 * entries the caller left undefined.
118 *
119 * MATLAB marks "leave this one alone" with NaN; this port takes an explicit
120 * mask, because a struct instantiated at Rational has no NaN to mark it with.
121 */
122template <class T>
124 const std::vector<std::vector<bool>>& set_rate,
125 const std::vector<std::vector<bool>>& set_scv,
126 bool auto_refresh = false) {
127 std::vector<std::pair<std::size_t, std::size_t>> touched;
128 for (std::size_t i = 0; i < sn.nstations; ++i)
129 for (std::size_t r = 0; r < sn.nclasses; ++r) {
130 if (i < set_rate.size() && r < set_rate[i].size() && set_rate[i][r]) {
131 sn.rates(i, r) = rates(i, r);
132 touched.push_back(std::make_pair(i + 1, r + 1));
133 }
134 if (i < set_scv.size() && r < set_scv[i].size() && set_scv[i][r])
135 sn.scv(i, r) = scvs(i, r);
136 }
137 if (!auto_refresh) return;
138 for (std::size_t k = 0; k < touched.size(); ++k)
139 sn_refresh_process_fields(sn, touched[k].first, touched[k].second);
140}
141
142/**
143 * Port of sn_set_population: change a class population and the closed total.
144 *
145 * `auto_refresh` re-solves the visit ratios, which the reference does because a
146 * class that becomes open (or closed) changes which chains are closed and
147 * therefore how the visits are normalised.
148 */
149template <class T>
150void sn_set_population(qn::NetworkStruct<T>& sn, std::size_t r, double njobs,
151 bool auto_refresh = false) {
152 sn.classes[r - 1].population = njobs;
153 if (auto_refresh) sn.refresh_chains();
154}
155
156/** Port of sn_set_servers. */
157template <class T>
158void sn_set_servers(qn::NetworkStruct<T>& sn, std::size_t ist, double nservers) {
159 sn.stations[ist - 1].nservers = nservers;
160}
161
162/** Port of sn_set_priority. */
163template <class T>
164void sn_set_priority(qn::NetworkStruct<T>& sn, std::size_t r, int priority) {
165 sn.classes[r - 1].prio = priority;
166}
167
168/**
169 * Port of sn_set_fork_fanout.
170 *
171 * The reference writes `sn.nodeparam{f}.fanOut`, a field no MATLAB solver reads
172 * back; the quantity this struct carries and the fork-join machinery does read
173 * is the per-branch multiplicity `tasks_per_link`, so that is what is written.
174 */
175template <class T>
176void sn_set_fork_fanout(qn::NetworkStruct<T>& sn, std::size_t fork_node, double fanout) {
177 if (fork_node == 0 || fork_node > sn.nodes.size() ||
178 sn.nodes[fork_node - 1].nodetype != qn::NodeType::Fork)
179 throw InputError("sn_set_fork_fanout: node " + std::to_string(fork_node) +
180 " is not a Fork node");
181 sn.nodes[fork_node - 1].tasks_per_link = fanout;
182}
183
184/** Port of sn_set_routing: replace the class-expanded stateful routing wholesale. */
185template <class T>
186void sn_set_routing(qn::NetworkStruct<T>& sn, const Matrix<T>& rt, bool auto_refresh = false) {
187 sn.rt = rt;
188 if (auto_refresh) sn.refresh_chains();
189}
190
191/** Port of sn_set_routing_prob: one entry of the class-expanded stateful routing. */
192template <class T>
193void sn_set_routing_prob(qn::NetworkStruct<T>& sn, std::size_t from_stateful,
194 std::size_t from_class, std::size_t to_stateful, std::size_t to_class,
195 const T& prob, bool auto_refresh = false) {
196 const std::size_t K = sn.nclasses;
197 sn.rt((from_stateful - 1) * K + (from_class - 1), (to_stateful - 1) * K + (to_class - 1)) =
198 prob;
199 if (auto_refresh) sn.refresh_chains();
200}
201
202} // namespace api
203} // namespace line
204
205#endif // LINE_API_SN_SN_SETTERS_H
Base error for the multiprecision C++ port.
Definition error.h:31
InputError(const std::string &what)
Definition error.h:39
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
MAP constructors and structural transformations.
void sn_set_service(qn::NetworkStruct< T > &sn, std::size_t ist, std::size_t r, const T &rate, const T &scv, bool auto_refresh=false)
Port of sn_set_service: write a (rate, SCV) pair at one (station, class).
Definition sn_setters.h:97
void sn_set_routing_prob(qn::NetworkStruct< T > &sn, std::size_t from_stateful, std::size_t from_class, std::size_t to_stateful, std::size_t to_class, const T &prob, bool auto_refresh=false)
Port of sn_set_routing_prob: one entry of the class-expanded stateful routing.
Definition sn_setters.h:193
void sn_set_servers(qn::NetworkStruct< T > &sn, std::size_t ist, double nservers)
Port of sn_set_servers.
Definition sn_setters.h:158
void sn_set_fork_fanout(qn::NetworkStruct< T > &sn, std::size_t fork_node, double fanout)
Port of sn_set_fork_fanout.
Definition sn_setters.h:176
void sn_refresh_process_fields(qn::NetworkStruct< T > &sn, std::size_t ist, std::size_t r)
Port of sn_refresh_process_fields: rebuild sn.service[ist][r] from the (rate, SCV) pair currently in ...
Definition sn_setters.h:58
void sn_set_arrival(qn::NetworkStruct< T > &sn, std::size_t r, const T &rate, const T &scv, bool auto_refresh=false)
Port of sn_set_arrival: the same, at whichever station is the Source.
Definition sn_setters.h:106
void sn_set_service_batch(qn::NetworkStruct< T > &sn, const Matrix< T > &rates, const Matrix< T > &scvs, const std::vector< std::vector< bool > > &set_rate, const std::vector< std::vector< bool > > &set_scv, bool auto_refresh=false)
Port of sn_set_service_batch: write a whole (rate, SCV) table, skipping the entries the caller left u...
Definition sn_setters.h:123
void sn_set_population(qn::NetworkStruct< T > &sn, std::size_t r, double njobs, bool auto_refresh=false)
Port of sn_set_population: change a class population and the closed total.
Definition sn_setters.h:150
void sn_set_routing(qn::NetworkStruct< T > &sn, const Matrix< T > &rt, bool auto_refresh=false)
Port of sn_set_routing: replace the class-expanded stateful routing wholesale.
Definition sn_setters.h:186
void sn_set_priority(qn::NetworkStruct< T > &sn, std::size_t r, int priority)
Port of sn_set_priority.
Definition sn_setters.h:164
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
Definition lang_types.h:483
Map< T > map_hyperexp(const T &mean, const T &scv, const T &p_in)
Two-phase hyperexponential renewal MAP matching a mean and an SCV >= 1, with branching probability p ...
Map< T > map_exponential(const T &lambda)
Two-phase MAP constructor for a Poisson process of rate lambda.
Definition map_moment.h:213
Map< T > map_erlang(const T &mean, unsigned k)
Erlang-k renewal MAP with the given mean (map_erlang.m).
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
Definition lang_types.h:759
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54