LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
dist_scale_rate.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_LANG_DIST_SCALE_RATE_H
6#define LINE_LANG_DIST_SCALE_RATE_H
7
8/**
9 * @file
10 * @ingroup line_lang
11 * Rate-scaled copy of a distribution, preserving its shape.
12 *
13 * Port of `matlab/src/api/dist/dist_scale_rate.m`. The result is the law of
14 * X / factor: every moment of order n is divided by factor^n, so the mean moves
15 * and the SCV, the skewness and the whole shape do not.
16 *
17 * REBUILT FROM THE PARAMETERS, not by rescaling (D0, D1). Both routes give the
18 * same law, but only the first leaves the parameter list coherent with the
19 * family, and the parameters are what a serializing consumer writes out. The
20 * reference gives the same reason.
21 *
22 * THIS IS THE PERTURBATION PRIMITIVE of the finite-difference branch of
23 * getSensitivityTable: scaling the rate at a (station, class) by (1 + h) is
24 * exactly the perturbation d(.)/d(rate) is taken along, which is why the
25 * scaling has to be exact in every moment and not merely in the mean.
26 *
27 * REFUSED BY NAME, as the reference's `otherwise` arm refuses them: MMAP, BMAP,
28 * MAPt, PHt, the marked and batch schedules (MMAPt, MPHt, BMMAPt) and Prior. A
29 * marked, batched or time-inhomogeneous process carries more than one time
30 * scale (the per-mark blocks, the batch blocks, the breakpoint schedule), and
31 * scaling only the one the caller had in mind would silently change the others.
32 */
33
34#include <cmath>
35#include <cstddef>
36#include <vector>
37
39#include "line/num/number.h"
40#include "line/util/error.h"
41#include "line/util/matrix.h"
42
43namespace line {
44namespace lang {
45
46/** The law of X / factor, in the same family as `d`. */
47template <class T>
48Distrib<T> dist_scale_rate(const Distrib<T>& d, const T& factor) {
49 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
50 if (!(factor > zero))
51 throw InputError("dist_scale_rate: the scaling factor must be positive");
52 if (d.is_prior())
53 throw UnsupportedError(
54 "dist_scale_rate: a Prior is a set of alternative models, not one law with a time "
55 "scale; perturb the alternatives instead");
56 if (d.has_schedule())
57 throw UnsupportedError(
58 "dist_scale_rate: a MAPt / PHt / NHPP / MMAPt / MPHt / BMMAPt schedule carries a "
59 "second time scale in its breakpoints, which scaling the rates alone would leave "
60 "inconsistent");
61 if (d.disabled) return d;
62
63 switch (d.type) {
65 return Distrib<T>::immediate();
67 return Distrib<T>::exp_rate(T(d.params[0] * factor));
69 return Distrib<T>::erlang(
70 T(d.params[0] * factor),
71 static_cast<std::size_t>(num_traits<T>::to_double(d.params[1]) + 0.5));
73 return Distrib<T>::hyperexp(d.params[0], T(d.params[1] * factor),
74 T(d.params[2] * factor));
76 case ProcessType::COX2: {
77 // params = [mu(1..n), phi(1..n)]. The completion probabilities are
78 // dimensionless, so only the phase rates carry the time scale.
79 const std::size_t n = d.params.size() / 2;
80 std::vector<T> mu(n), phi(n);
81 for (std::size_t i = 0; i < n; ++i) {
82 mu[i] = T(d.params[i] * factor);
83 phi[i] = d.params[n + i];
84 }
85 return Distrib<T>::coxian(mu, phi);
86 }
88 case ProcessType::PH: {
89 // params carries alpha; the subgenerator is D0.
90 std::vector<T> alpha(d.params.begin(), d.params.end());
91 Matrix<T> A(d.D0.rows(), d.D0.cols(), zero);
92 for (std::size_t i = 0; i < d.D0.rows(); ++i)
93 for (std::size_t j = 0; j < d.D0.cols(); ++j) A(i, j) = T(d.D0(i, j) * factor);
94 return Distrib<T>::phase_type(alpha, A, d.type == ProcessType::APH);
95 }
97 case ProcessType::MMPP2: {
98 // Every rate of the modulating chain and of the arrival process is
99 // scaled, which time-scales the whole process.
100 Matrix<T> D0(d.D0.rows(), d.D0.cols(), zero), D1(d.D1.rows(), d.D1.cols(), zero);
101 for (std::size_t i = 0; i < d.D0.rows(); ++i)
102 for (std::size_t j = 0; j < d.D0.cols(); ++j) D0(i, j) = T(d.D0(i, j) * factor);
103 for (std::size_t i = 0; i < d.D1.rows(); ++i)
104 for (std::size_t j = 0; j < d.D1.cols(); ++j) D1(i, j) = T(d.D1(i, j) * factor);
105 Distrib<T> out = Distrib<T>::map_dist(D0, D1, d.type);
106 for (const T& p : d.params) out.params.push_back(T(p * factor));
107 out.mean = T(d.mean / factor);
108 out.scv = d.scv;
109 return out;
110 }
111 case ProcessType::DET:
112 return Distrib<T>::det(T(d.params[0] / factor));
114 return Distrib<T>::uniform(T(d.params[0] / factor), T(d.params[1] / factor));
116 // Gamma(shape, scale): the shape is dimensionless.
117 return Distrib<T>::gamma_dist(d.params[0], T(d.params[1] / factor));
119 // Pareto(shape, scale): the scale is the minimum of the support.
120 return Distrib<T>::pareto(d.params[0], T(d.params[1] / factor));
122 return Distrib<T>::weibull(T(d.params[0] / factor), d.params[1]);
124 if constexpr (!num_traits<T>::has_transcendental) {
125 throw UnsupportedError(
126 "dist_scale_rate: scaling a Lognormal shifts its log-mean by log(factor), "
127 "which exact arithmetic has no representation for");
128 } else {
129 const double lf = std::log(num_traits<T>::to_double(factor));
131 d.params[1]);
132 }
133 }
135 // A trace is scaled sample by sample; the samples ARE the parameter.
136 std::vector<T> s(d.trace.size(), zero);
137 for (std::size_t i = 0; i < d.trace.size(); ++i) s[i] = T(d.trace[i] / factor);
138 return Distrib<T>::replayer(s);
139 }
140 default:
141 break;
142 }
143 (void)one;
144 throw UnsupportedError(
145 "dist_scale_rate: rate scaling is not defined for this process family; supported are "
146 "Exp, Erlang, HyperExp, Coxian, Cox2, APH, PH, MAP, MMPP2, Det, Uniform, Gamma, Pareto, "
147 "Weibull, Lognormal, Replayer and Immediate");
148}
149
150} // namespace lang
151} // namespace line
152
153#endif // LINE_LANG_DIST_SCALE_RATE_H
InputError(const std::string &what)
Definition error.h:39
UnsupportedError(const std::string &what)
Definition error.h:51
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense matrix and non-owning view.
Distrib< T > dist_scale_rate(const Distrib< T > &d, const T &factor)
The law of X / factor, in the same family as d.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
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:853
static Distrib replayer(const std::vector< T > &samples)
Replayer / Trace: the samples, with their empirical first two moments.
static Distrib exp_rate(const T &r)
Definition lang_types.h:945
static Distrib phase_type(const std::vector< T > &alpha, const Matrix< T > &A, bool acyclic)
PH / APH given by (alpha, A): D0 = A and D1 = (-A e) alpha.
static Distrib weibull(const T &scale, const T &shape)
bool has_schedule() const
Definition lang_types.h:922
static Distrib pareto(const T &shape, const T &scale)
Pareto(shape, scale), with the MATLAB parameter order (alpha, k).
std::vector< T > params
Constructor arguments, in MATLAB getParam order.
Definition lang_types.h:826
static Distrib gamma_dist(const T &shape, const T &scale)
Gamma(shape, scale), Weibull(scale, shape) and Lognormal(mu, sigma).
static Distrib map_dist(const Matrix< T > &D0, const Matrix< T > &D1, ProcessType tag)
A MAP given by its two matrices; the moments are those of its stationary phase.
std::vector< T > trace
Replayer / Trace samples; empty for every other type.
Definition lang_types.h:828
static Distrib det(const T &m)
Definition lang_types.h:989
static Distrib uniform(const T &a, const T &b)
Uniform(a, b).
static Distrib lognormal(const T &logmean, const T &logsigma)
static Distrib hyperexp(const T &p, const T &lambda1, const T &lambda2)
static Distrib immediate()
The Immediate singleton.
Definition lang_types.h:977
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 coxian(const std::vector< T > &mu, const std::vector< T > &phi)
Coxian(mu, phi): phase i completes with probability phi(i) and otherwise moves to phase i+1.
bool is_prior() const
Definition lang_types.h:878