LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_set_service_coc.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_INFER_SN_SET_SERVICE_COC_H
6#define LINE_API_INFER_SN_SET_SERVICE_COC_H
7
8/**
9 * @file
10 * @ingroup api_infer
11 * Port of matlab/src/api/infer/sn_set_service_coc.m: overwrite the service
12 * process of one (station, class) pair of a NetworkStruct with a Markovian law
13 * fitted to a rate and an SCV, the way the inference routines (infer_fmlps,
14 * infer_mlps and the Gibbs samplers) vary a demand without rebuilding the
15 * model.
16 *
17 * The reference writes `sn.rates`, `sn.scv` and then, for a finite positive
18 * rate, the representation `sn.proc`, `sn.procid`, `sn.phases`, `sn.phasessz`,
19 * `sn.phaseshift`, `sn.mu`, `sn.phi` and `sn.pie`. The C++ NetworkStruct
20 * derives every one of those from the stored `Distrib` on demand (`procid`,
21 * `phases_of`, `phasessz_of`, `dist_to_map`), so the port stores the fitted
22 * `Distrib` through `set_service` and writes `rates`/`scv` directly, which is
23 * the same state.
24 *
25 * The fit is the reference's: SCV within 1e-10 of one (or NaN) gives an
26 * exponential; SCV below one an Erlang with k = max(1, ceil(1/scv)) phases
27 * (map_erlang); SCV above one the two-phase hyperexponential of kpctoolbox's
28 * map_hyperexp(mean, scv), whose balanced construction starts from p = 0.99 and
29 * divides p by ten until a feasible pair of rates appears, falling back to an
30 * exponential when none does.
31 */
32
33#include <cmath>
34#include <cstddef>
35#include <limits>
36
39#include "line/num/number.h"
40#include "line/util/error.h"
41
42namespace line {
43namespace api {
44
45namespace detail {
46
47/**
48 * kpctoolbox map_hyperexp(MEAN, SCV, p): the two-phase hyperexponential with
49 * mixing probability p matching the mean and SCV, trying the two roots of the
50 * second-moment equation and lowering p by a factor ten when neither is
51 * feasible. Returns false where the reference returns an empty MAP.
52 */
53inline bool map_hyperexp_fit(double mean, double scv, double p, double* mu1_out, double* mu2_out,
54 double* p_out) {
55 const double E2 = (1.0 + scv) * mean * mean;
56 const double Delta = -4.0 * p * mean * mean + 4.0 * p * p * mean * mean + 2.0 * E2 * p -
57 2.0 * E2 * p * p;
58 const double den = E2 * p - 2.0 * mean * mean;
59 auto feasible = [](double mu1, double mu2, double pp) {
60 return std::isfinite(mu1) && std::isfinite(mu2) && mu1 > 0.0 && mu2 > 0.0 &&
61 pp >= 0.0 && pp <= 1.0;
62 };
63 if (Delta >= 0.0 && den != 0.0) {
64 double mu2 = (-2.0 * mean + 2.0 * p * mean + std::sqrt(Delta)) / den;
65 double mu1 = mu2 * p / (p - 1.0 + mean * mu2);
66 if (feasible(mu1, mu2, p)) {
67 *mu1_out = mu1;
68 *mu2_out = mu2;
69 *p_out = p;
70 return true;
71 }
72 mu2 = (-2.0 * mean + 2.0 * p * mean - std::sqrt(Delta)) / den;
73 mu1 = mu2 * p / (p - 1.0 + mean * mu2);
74 if (feasible(mu1, mu2, p)) {
75 *mu1_out = mu1;
76 *mu2_out = mu2;
77 *p_out = p;
78 return true;
79 }
80 }
81 if (p > 1e-6) return map_hyperexp_fit(mean, scv, p / 10.0, mu1_out, mu2_out, p_out);
82 return false;
83}
84
85} // namespace detail
86
87/**
88 * @brief Set the service process of (stationIdx, classIdx) from a rate and an
89 * SCV, the reference's sn_set_service_coc(sn, stationIdx, classIdx, rate, scv).
90 *
91 * @param sn the network struct, modified in place
92 * @param stationIdx 1-based station
93 * @param classIdx 1-based class
94 * @param rate service rate; NaN, non-positive or infinite leaves the
95 * representation untouched after writing rates/scv
96 * @param scv squared coefficient of variation, default 1
97 */
98template <class T>
99void sn_set_service_coc(qn::NetworkStruct<T>& sn, std::size_t stationIdx, std::size_t classIdx,
100 const T& rate, const T& scv) {
101 if (stationIdx == 0 || stationIdx > sn.nstations)
102 throw InputError("sn_set_service_coc: station index out of range");
103 if (classIdx == 0 || classIdx > sn.nclasses)
104 throw InputError("sn_set_service_coc: class index out of range");
105 const std::size_t i = stationIdx - 1, r = classIdx - 1;
106 sn.rates(i, r) = rate;
107 sn.scv(i, r) = scv;
108 const double rd = num_traits<T>::to_double(rate);
109 const double sd = num_traits<T>::to_double(scv);
110 if (std::isnan(rd) || rd <= 0.0 || std::isinf(rd)) return;
111
112 const T one = num_traits<T>::from_int(1);
113 const T meanVal = T(one / rate);
115 if (std::isnan(sd) || std::fabs(sd - 1.0) < 1e-10) {
116 d = lang::Distrib<T>::exp_mean(meanVal);
117 } else if (sd < 1.0) {
118 const double kd = std::ceil(1.0 / sd);
119 const std::size_t k = static_cast<std::size_t>(kd < 1.0 ? 1.0 : kd);
120 // map_erlang(mean, k): k phases of rate k/mean
121 d = lang::Distrib<T>::erlang(T(num_traits<T>::from_int(static_cast<long>(k)) / meanVal), k);
122 } else {
123 double mu1 = 0.0, mu2 = 0.0, p = 0.99;
124 if (detail::map_hyperexp_fit(num_traits<T>::to_double(meanVal), sd, 0.99, &mu1, &mu2, &p)) {
128 } else {
129 d = lang::Distrib<T>::exp_mean(meanVal);
130 }
131 }
132 sn.set_service(stationIdx, classIdx, d);
133 if (i < sn.disabled.size() && r < sn.disabled[i].size()) sn.disabled[i][r] = false;
134}
135
136/** sn_set_service_coc with the reference's default scv = 1. */
137template <class T>
138void sn_set_service_coc(qn::NetworkStruct<T>& sn, std::size_t stationIdx, std::size_t classIdx,
139 const T& rate) {
140 sn_set_service_coc(sn, stationIdx, classIdx, rate, num_traits<T>::from_int(1));
141}
142
143} // namespace api
144} // namespace line
145
146#endif // LINE_API_INFER_SN_SET_SERVICE_COC_H
InputError(const std::string &what)
Definition error.h:105
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
void sn_set_service_coc(qn::NetworkStruct< T > &sn, std::size_t stationIdx, std::size_t classIdx, const T &rate, const T &scv)
Set the service process of (stationIdx, classIdx) from a rate and an SCV, the reference's sn_set_serv...
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
static Distrib hyperexp(const T &p, const T &lambda1, const T &lambda2)
HyperExp(p, lambda1, lambda2): phase i chosen with probability p_i.
static Distrib erlang(const T &phase_rate, std::size_t r)
Erlang(alpha, r): r phases of rate alpha, as MATLAB's Erlang(phaseRate, nphases).
static Distrib exp_mean(const T &m)
Definition lang_types.h:940