1function sens = closedSensitivities(model)
2% closedSensitivities Analytic d(metric)/d(rate)
for closed single-server
3% product-
form networks with unit visit ratios, via
the differentiated-MVA
4% primitive pfqn_sens. Mirrors native-Python _closed_sensitivities. Returns []
5%
for open, multiserver, or non-unit-visit networks.
11if isempty(N) || any(~isfinite(N))
15[~, D, ~, Zmat, ~, S] = sn_get_product_form_params(sn); % D: Mq x R
16S = S(:); % server counts per queue
17if any(S(isfinite(S)) > 1),
return; end % single-server only
23 if numel(zsum) == R, Z = zsum(:).
'; end
26res = pfqn_sens(D, N(:).', Z);
29nodeToStation = sn.nodeToStation;
30queueNodes = find(sn.nodetype(:).
' == double(NodeType.Queue));
31if numel(queueNodes) ~= Mq, return; end
33params = {}; % each {j, s, p, chain, pkey}
34for j = 1:numel(queueNodes)
35 nodeJ = queueNodes(j);
36 sj = nodeToStation(nodeJ);
39 if ~isfinite(ratej) || ratej <= 0 || D(j, s) <= 0, continue; end
40 p = (j - 1) * R + s; % 1-based param index into pfqn_sens outputs
41 params{end+1} = {j, s, p, -D(j, s) / ratej, ...
42 opt.SensitivityData.paramKey(sn.nodenames{nodeJ}, sn.classnames{s})}; %#ok<AGROW>
46sens = opt.SensitivityData();
47for i = 1:numel(queueNodes)
48 nodeI = queueNodes(i);
49 nameI = sn.nodenames{nodeI};
51 if D(i, r) <= 0, continue; end
52 mkey = opt.SensitivityData.metricKey(nameI, sn.classnames{r});
53 for pk = 1:numel(params)
55 p = pr{3}; chain = pr{4}; pkey = pr{5};
56 sens.add('RespT
', mkey, pkey, res.dR(i, r, p) * chain);
57 sens.add('QLen
', mkey, pkey, res.dQ(i, r, p) * chain);
58 sens.add('Tput
', mkey, pkey, res.dX(r, p) * chain);
59 sens.add('Util
', nameI, pkey, res.dU(i, r, p) * chain);