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 and Prior. A marked or time-inhomogeneous process carries more than
29 * one time scale (the per-mark blocks, the breakpoint schedule), and scaling
30 * only the one the caller had in mind would silently change the others.
31 */
32
33#include <cmath>
34#include <cstddef>
35#include <vector>
36
38#include "line/num/number.h"
39#include "line/util/error.h"
40#include "line/util/matrix.h"
41
42namespace line {
43namespace lang {
44
45/** The law of X / factor, in the same family as `d`. */
46template <class T>
47Distrib<T> dist_scale_rate(const Distrib<T>& d, const T& factor) {
48 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
49 if (!(factor > zero))
50 throw InputError("dist_scale_rate: the scaling factor must be positive");
51 if (d.is_prior())
52 throw UnsupportedError(
53 "dist_scale_rate: a Prior is a set of alternative models, not one law with a time "
54 "scale; perturb the alternatives instead");
55 if (d.has_schedule())
56 throw UnsupportedError(
57 "dist_scale_rate: a MAPt / PHt / NHPP schedule carries a second time scale in its "
58 "breakpoints, which scaling the rates alone would leave inconsistent");
59 if (d.disabled) return d;
60
61 switch (d.type) {
63 return Distrib<T>::immediate();
65 return Distrib<T>::exp_rate(T(d.params[0] * factor));
67 return Distrib<T>::erlang(
68 T(d.params[0] * factor),
69 static_cast<std::size_t>(num_traits<T>::to_double(d.params[1]) + 0.5));
71 return Distrib<T>::hyperexp(d.params[0], T(d.params[1] * factor),
72 T(d.params[2] * factor));
74 case ProcessType::COX2: {
75 // params = [mu(1..n), phi(1..n)]. The completion probabilities are
76 // dimensionless, so only the phase rates carry the time scale.
77 const std::size_t n = d.params.size() / 2;
78 std::vector<T> mu(n), phi(n);
79 for (std::size_t i = 0; i < n; ++i) {
80 mu[i] = T(d.params[i] * factor);
81 phi[i] = d.params[n + i];
82 }
83 return Distrib<T>::coxian(mu, phi);
84 }
86 case ProcessType::PH: {
87 // params carries alpha; the subgenerator is D0.
88 std::vector<T> alpha(d.params.begin(), d.params.end());
89 Matrix<T> A(d.D0.rows(), d.D0.cols(), zero);
90 for (std::size_t i = 0; i < d.D0.rows(); ++i)
91 for (std::size_t j = 0; j < d.D0.cols(); ++j) A(i, j) = T(d.D0(i, j) * factor);
92 return Distrib<T>::phase_type(alpha, A, d.type == ProcessType::APH);
93 }
95 case ProcessType::MMPP2: {
96 // Every rate of the modulating chain and of the arrival process is
97 // scaled, which time-scales the whole process.
98 Matrix<T> D0(d.D0.rows(), d.D0.cols(), zero), D1(d.D1.rows(), d.D1.cols(), zero);
99 for (std::size_t i = 0; i < d.D0.rows(); ++i)
100 for (std::size_t j = 0; j < d.D0.cols(); ++j) D0(i, j) = T(d.D0(i, j) * factor);
101 for (std::size_t i = 0; i < d.D1.rows(); ++i)
102 for (std::size_t j = 0; j < d.D1.cols(); ++j) D1(i, j) = T(d.D1(i, j) * factor);
103 Distrib<T> out = Distrib<T>::map_dist(D0, D1, d.type);
104 for (const T& p : d.params) out.params.push_back(T(p * factor));
105 out.mean = T(d.mean / factor);
106 out.scv = d.scv;
107 return out;
108 }
109 case ProcessType::DET:
110 return Distrib<T>::det(T(d.params[0] / factor));
112 return Distrib<T>::uniform(T(d.params[0] / factor), T(d.params[1] / factor));
114 // Gamma(shape, scale): the shape is dimensionless.
115 return Distrib<T>::gamma_dist(d.params[0], T(d.params[1] / factor));
117 // Pareto(shape, scale): the scale is the minimum of the support.
118 return Distrib<T>::pareto(d.params[0], T(d.params[1] / factor));
120 return Distrib<T>::weibull(T(d.params[0] / factor), d.params[1]);
122 if constexpr (!num_traits<T>::has_transcendental) {
123 throw UnsupportedError(
124 "dist_scale_rate: scaling a Lognormal shifts its log-mean by log(factor), "
125 "which exact arithmetic has no representation for");
126 } else {
127 const double lf = std::log(num_traits<T>::to_double(factor));
129 d.params[1]);
130 }
131 }
133 // A trace is scaled sample by sample; the samples ARE the parameter.
134 std::vector<T> s(d.trace.size(), zero);
135 for (std::size_t i = 0; i < d.trace.size(); ++i) s[i] = T(d.trace[i] / factor);
136 return Distrib<T>::replayer(s);
137 }
138 default:
139 break;
140 }
141 (void)one;
142 throw UnsupportedError(
143 "dist_scale_rate: rate scaling is not defined for this process family; supported are "
144 "Exp, Erlang, HyperExp, Coxian, Cox2, APH, PH, MAP, MMPP2, Det, Uniform, Gamma, Pareto, "
145 "Weibull, Lognormal, Replayer and Immediate");
146}
147
148} // namespace lang
149} // namespace line
150
151#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.
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
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:814
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:797
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:734
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:736
static Distrib det(const T &m)
Definition lang_types.h:858
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)
Definition lang_types.h:956
static Distrib immediate()
The Immediate singleton.
Definition lang_types.h:846
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).
Definition lang_types.h:873
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.
Definition lang_types.h:987
bool is_prior() const
Definition lang_types.h:776