161 bool exact_available,
165 if (
opt.method !=
"auto" &&
opt.method !=
"exact" &&
opt.method !=
"fd")
166 throw InputError(
"getSensitivityTable: the method must be 'auto', 'exact' or 'fd'");
167 if (
opt.scheme !=
"forward" &&
opt.scheme !=
"central")
168 throw InputError(
"getSensitivityTable: the scheme must be 'forward' or 'central'");
171 const bool in_scope = detail::sens_exact_in_scope(
sn, why);
173 if (
opt.method ==
"exact") {
174 if (!exact_available)
176 "getSensitivityTable: exact analytic sensitivities differentiate a product-form "
177 "recursion and are available on the MVA and NC engines only; use 'fd'");
180 }
else if (
opt.method ==
"fd") {
183 use_exact = exact_available && in_scope;
189 std::vector<std::vector<bool>> mask(Mq, std::vector<bool>(R,
false));
190 Matrix<T> dT(Mq, R, zero), dR(Mq, R, zero), dQ(Mq, R, zero), dU(Mq, R, zero);
195 bool is_open =
false;
197 if (std::isinf(n)) is_open =
true;
200 for (std::size_t i = 0; i < Mq; ++i) {
202 for (std::size_t r = 0; r < R; ++r) {
203 rates(i, r) =
sn.rates(st - 1, r);
205 mask[i][r] = std::isfinite(rd) && rd > 0.0 && p.
D(i, r) > zero;
212 std::vector<std::size_t> chain_of(R, 0);
214 std::vector<T> Zc(C, zero);
215 std::vector<int> Nc(C, 0);
216 std::vector<T> lambdac(C, zero);
217 for (std::size_t c = 0; c < C; ++c) {
219 for (std::size_t r = 0; r < R; ++r) {
220 if (!
sn.chains[c][r])
continue;
222 for (std::size_t i = 0; i < Mq; ++i) Dc(i, c) = T(Dc(i, c) + p.
D(i, r));
223 for (std::size_t z = 0; z < p.
Z.rows(); ++z) Zc[c] = T(Zc[c] + p.
Z(z, r));
224 if (std::isfinite(p.
N[r])) nsum += p.
N[r];
225 lambdac[c] = T(lambdac[c] + p.
lambda[r]);
227 Nc[c] =
static_cast<int>(nsum + 0.5);
234 for (std::size_t i = 0; i < Mq; ++i)
235 for (std::size_t r = 0; r < R; ++r) {
236 if (!mask[i][r])
continue;
237 const std::size_t c = chain_of[r];
238 if (!(Dc(i, c) > zero))
continue;
239 const T rate = rates(i, r);
240 const T Dir = p.
D(i, r);
241 const T visits = T(Dir * rate);
242 const std::size_t pidx = i * C + c;
243 const T chain = T(-Dir / rate);
244 const T Xc = s.
XN[c];
245 const T Qc = s.
QN(i, c);
246 const T dXc = T(s.
dX(c, pidx) * chain);
247 const T dQc = T(s.
dQ[pidx](i, c) * chain);
249 const T alpha = T(Dir / Dc(i, c));
250 const T dalpha = T(chain * (Dc(i, c) - Dir) / (Dc(i, c) * Dc(i, c)));
251 const T Qir = T(alpha * Qc);
252 const T dQir = T(dalpha * Qc + alpha * dQc);
253 const T Tir = T(Xc * visits);
254 const T dTir = T(dXc * visits);
257 dU(i, r) = T(dXc * Dir + Xc * chain);
259 if (Tir > zero) dR(i, r) = T((dQir * Tir - Qir * dTir) / (Tir * Tir));
265 std::vector<T> Ui(Mq, zero);
266 for (std::size_t i = 0; i < Mq; ++i)
267 for (std::size_t r = 0; r < R; ++r) {
268 if (p.
D(i, r) > zero) rho(i, r) = T(lambdac[chain_of[r]] * p.
D(i, r));
269 Ui[i] = T(Ui[i] + rho(i, r));
271 for (std::size_t i = 0; i < Mq; ++i) {
272 const T denom = T(one - Ui[i]);
273 for (std::size_t r = 0; r < R; ++r) {
274 if (!mask[i][r])
continue;
275 const T rate = rates(i, r);
276 const T svct = T(one / rate);
277 const T drho = T(-rho(i, r) / rate);
279 const T dsvct = T(-svct / rate);
280 dR(i, r) = T((dsvct * denom + svct * dUi) / (denom * denom));
281 dQ(i, r) = T((drho * denom + rho(i, r) * dUi) / (denom * denom));
289 const bool central =
opt.scheme ==
"central";
291 if (!(h > 0.0)) h =
opt.simulation ? 1e-2 : 1e-4;
292 if (!std::isfinite(h) || h <= 0.0 || h >= 1.0)
293 throw InputError(
"getSensitivityTable: the finite-difference step must be in (0,1)");
296 const std::vector<std::vector<bool>> visited = detail::sens_visit_mask(
sn);
297 for (std::size_t i = 0; i < Mq; ++i) {
299 for (std::size_t r = 0; r < R; ++r) {
301 mask[i][r] = std::isfinite(rd) && rd > 0.0 && visited[st - 1][r];
306 for (std::size_t i = 0; i < Mq; ++i) {
308 for (std::size_t r = 0; r < R; ++r) {
309 if (!mask[i][r])
continue;
310 const T rate =
sn.rates(st - 1, r);
311 const Distrib<T> saved =
sn.service[st - 1][r];
318 T denom = T(rate * hT);
326 dT(i, r) = T((up.
Tp(st - 1, r) - down.
Tp(st - 1, r)) / denom);
327 dR(i, r) = T((up.
R(st - 1, r) - down.
R(st - 1, r)) / denom);
328 dQ(i, r) = T((up.
Q(st - 1, r) - down.
Q(st - 1, r)) / denom);
329 dU(i, r) = T((up.
U(st - 1, r) - down.
U(st - 1, r)) / denom);
333 sn.set_service(st, r + 1, saved);
339 for (std::size_t i = 0; i < Mq; ++i) {
341 const std::size_t nd =
sn.node_of_station(st);
342 for (std::size_t r = 0; r < R; ++r) {
343 if (!mask[i][r])
continue;
345 row.station = nd > 0 ?
sn.nodes[nd - 1].name :
sn.stations[st - 1].name;
346 row.jobclass =
sn.classes[r].name;
347 row.dTput = dT(i, r);
348 row.dRespT = dR(i, r);
349 row.dQLen = dQ(i, r);
350 row.dUtil = dU(i, r);
351 out.
rows.push_back(row);