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];
228 Nc[c] =
static_cast<int>(nsum + 0.5);
235 for (std::size_t i = 0; i < Mq; ++i)
236 for (std::size_t r = 0; r < R; ++r) {
237 if (!mask[i][r])
continue;
238 const std::size_t c = chain_of[r];
239 if (!(Dc(i, c) > zero))
continue;
240 const T rate = rates(i, r);
241 const T Dir = p.
D(i, r);
242 const T visits = T(Dir * rate);
243 const std::size_t pidx = i * C + c;
244 const T chain = T(-Dir / rate);
245 const T Xc = s.
XN[c];
246 const T Qc = s.
QN(i, c);
247 const T dXc = T(s.
dX(c, pidx) * chain);
248 const T dQc = T(s.
dQ[pidx](i, c) * chain);
250 const T alpha = T(Dir / Dc(i, c));
251 const T dalpha = T(chain * (Dc(i, c) - Dir) / (Dc(i, c) * Dc(i, c)));
252 const T Qir = T(alpha * Qc);
253 const T dQir = T(dalpha * Qc + alpha * dQc);
254 const T Tir = T(Xc * visits);
255 const T dTir = T(dXc * visits);
258 dU(i, r) = T(dXc * Dir + Xc * chain);
260 if (Tir > zero) dR(i, r) = T((dQir * Tir - Qir * dTir) / (Tir * Tir));
266 std::vector<T> Ui(Mq, zero);
267 for (std::size_t i = 0; i < Mq; ++i)
268 for (std::size_t r = 0; r < R; ++r) {
269 if (p.
D(i, r) > zero) rho(i, r) = T(lambdac[chain_of[r]] * p.
D(i, r));
270 Ui[i] = T(Ui[i] + rho(i, r));
272 for (std::size_t i = 0; i < Mq; ++i) {
273 const T denom = T(one - Ui[i]);
274 for (std::size_t r = 0; r < R; ++r) {
275 if (!mask[i][r])
continue;
276 const T rate = rates(i, r);
277 const T svct = T(one / rate);
278 const T drho = T(-rho(i, r) / rate);
280 const T dsvct = T(-svct / rate);
281 dR(i, r) = T((dsvct * denom + svct * dUi) / (denom * denom));
282 dQ(i, r) = T((drho * denom + rho(i, r) * dUi) / (denom * denom));
290 const bool central =
opt.scheme ==
"central";
292 if (!(h > 0.0)) h =
opt.simulation ? 1e-2 : 1e-4;
293 if (!std::isfinite(h) || h <= 0.0 || h >= 1.0)
294 throw InputError(
"getSensitivityTable: the finite-difference step must be in (0,1)");
297 const std::vector<std::vector<bool>> visited = detail::sens_visit_mask(
sn);
298 for (std::size_t i = 0; i < Mq; ++i) {
300 for (std::size_t r = 0; r < R; ++r) {
302 mask[i][r] = std::isfinite(rd) && rd > 0.0 && visited[st - 1][r];
307 for (std::size_t i = 0; i < Mq; ++i) {
309 for (std::size_t r = 0; r < R; ++r) {
310 if (!mask[i][r])
continue;
311 const T rate =
sn.rates(st - 1, r);
312 const Distrib<T> saved =
sn.service[st - 1][r];
319 T denom = T(rate * hT);
327 dT(i, r) = T((up.
Tp(st - 1, r) - down.
Tp(st - 1, r)) / denom);
328 dR(i, r) = T((up.
R(st - 1, r) - down.
R(st - 1, r)) / denom);
329 dQ(i, r) = T((up.
Q(st - 1, r) - down.
Q(st - 1, r)) / denom);
330 dU(i, r) = T((up.
U(st - 1, r) - down.
U(st - 1, r)) / denom);
334 sn.set_service(st, r + 1, saved);
340 for (std::size_t i = 0; i < Mq; ++i) {
342 const std::size_t nd =
sn.node_of_station(st);
343 for (std::size_t r = 0; r < R; ++r) {
344 if (!mask[i][r])
continue;
346 row.station = nd > 0 ?
sn.nodes[nd - 1].name :
sn.stations[st - 1].name;
347 row.jobclass =
sn.classes[r].name;
348 row.dTput = dT(i, r);
349 row.dRespT = dR(i, r);
350 row.dQLen = dQ(i, r);
351 row.dUtil = dU(i, r);
352 out.
rows.push_back(row);