LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_ctmc_avg_from_pi.m
1function [QN,UN,RN,TN,CN,XN] = solver_ctmc_avg_from_pi(sn, pivec, StateSpace, StateSpaceAggr, arvRates, depRates)
2% SOLVER_CTMC_AVG_FROM_PI Map a state distribution to mean performance metrics.
3%
4% [QN,UN,RN,TN,CN,XN] = SOLVER_CTMC_AVG_FROM_PI(SN, PIVEC, STATESPACE,
5% STATESPACEAGGR, ARVRATES, DEPRATES)
6%
7% Given an arbitrary probability vector PIVEC over the enumerated CTMC state
8% space of SN (rows of STATESPACE / STATESPACEAGGR), returns the per-(station,
9% class) mean queue length QN, utilization UN, response time RN, throughput TN,
10% system response time CN and system throughput XN. The discipline-aware mapping
11% is identical to the steady-state reduction performed by solver_ctmc_analyzer;
12% it is factored here so that callers holding their own distribution (e.g. the
13% SolverENV state-vector analyzer, which time-averages a transient distribution)
14% can reuse it without re-solving for the stationary vector.
15%
16% Copyright (c) 2012-2026, Imperial College London
17% All rights reserved.
18
19M = sn.nstations;
20K = sn.nclasses;
21S = sn.nservers;
22NK = sn.njobs';
23sched = sn.sched;
24PH = sn.proc;
25
26probSysState = pivec(:)';
27probSysState(probSysState<GlobalConstants.Zero) = 0;
28if sum(probSysState) > 0
29 probSysState = probSysState/sum(probSysState);
30end
31wset = 1:size(StateSpace,1);
32
33XN = NaN*zeros(1,K);
34UN = NaN*zeros(M,K);
35QN = NaN*zeros(M,K);
36RN = NaN*zeros(M,K);
37TN = NaN*zeros(M,K);
38CN = NaN*zeros(1,K);
39
40istSpaceShift = zeros(1,M);
41for ist=1:M
42 if ist==1
43 istSpaceShift(ist) = 0;
44 else
45 istSpaceShift(ist) = istSpaceShift(ist-1) + size(sn.space{ist-1},2);
46 end
47end
48
49for k=1:K
50 refsf = sn.stationToStateful(sn.refstat(k));
51 XN(k) = probSysState*arvRates(wset,refsf,k);
52end
53
54for ist=1:M
55 isf = sn.stationToStateful(ist);
56 ind = sn.stationToNode(ist);
57 for k=1:K
58 TN(ist,k) = probSysState*depRates(wset,isf,k);
59 QN(ist,k) = probSysState*StateSpaceAggr(wset,(ist-1)*K+k);
60 end
61 if sn.nodetype(ind) ~= NodeType.Source
62 switch sched(ist)
63 case SchedStrategy.INF
64 for k=1:K
65 UN(ist,k) = QN(ist,k);
66 end
67 case {SchedStrategy.PS, SchedStrategy.DPS, SchedStrategy.GPS, SchedStrategy.LPS}
68 if isempty(sn.lldscaling) && isempty(sn.cdscaling) && ~sn_has_joint_dependence(sn)
69 for k=1:K
70 if ~isempty(PH{ist}{k})
71 % There are cases where due to remove of
72 % immediate transitions or due to cutoff the
73 % utilization estimator based on arrivals or
74 % departures can be under-estimated. E.g.:
75 % UNarv_ik doesn't work well with example_stateDependentRouting_3
76 % UNdep_ik doesn't work well with test_OQN_JMT_6
77 % Therefore, we take the maximum of the two
78 % Note: the two estimators are normally
79 % identical.
80 UNarv_ik = probSysState*arvRates(wset,isf,k)*map_mean(PH{ist}{k})/S(ist);
81 UNdep_ik = TN(ist,k)*map_mean(PH{ist}{k})/S(ist); % this is valid because CS in LINE is in a separate node
82 UN(ist,k) = max(UNarv_ik,UNdep_ik);
83 end
84 end
85 else % lld/cd/ljd cases
86 ind = sn.stationToNode(ist);
87 UN(ist,1:K) = 0;
88 for st = wset
89 [ni,nir] = State.toMarginal(sn, ind, StateSpace(st,(istSpaceShift(ist)+1):(istSpaceShift(ist)+size(sn.space{ist},2))));
90 if ni>0
91 for k=1:K
92 UN(ist,k) = UN(ist,k) + probSysState(st)*nir(k)*sn.schedparam(ist,k)/(nir*sn.schedparam(ist,:)');
93 end
94 end
95 end
96 end
97 case SchedStrategy.PAS
98 % Pass-and-swap (order-independent): utilization is the
99 % time-average number of in-service jobs per class divided by
100 % the number of servers. sir already counts the positions whose
101 % marginal rate increment is positive (toMarginal PAS branch),
102 % so a single job served by multiple server types still counts
103 % as one in-service job (not 1/rate).
104 ind = sn.stationToNode(ist);
105 UN(ist,1:K) = 0;
106 for st = wset
107 [~,~,sir] = State.toMarginal(sn, ind, StateSpace(st,(istSpaceShift(ist)+1):(istSpaceShift(ist)+size(sn.space{ist},2))));
108 for k=1:K
109 UN(ist,k) = UN(ist,k) + probSysState(st)*sir(k)/S(ist);
110 end
111 end
112 otherwise
113 if isempty(sn.lldscaling) && isempty(sn.cdscaling) && ~sn_has_joint_dependence(sn)
114 for k=1:K
115 if ~isempty(PH{ist}{k})
116 % There are cases where due to remove of
117 % immediate transitions or due to cutoff the
118 % utilization estimator based on arrivals or
119 % departures can be under-estimated. E.g.:
120 % UNarv_ik doesn't work well with example_stateDependentRouting_3
121 % UNdep_ik doesn't work well with test_OQN_JMT_6
122 % Therefore, we take the maximum of the two
123 % Note: the two estimators are normally
124 % identical.
125 UNarv_ik = probSysState*arvRates(wset,isf,k)*map_mean(PH{ist}{k})/S(ist);
126 UNdep_ik = TN(ist,k)*map_mean(PH{ist}{k})/S(ist); % this is valid because CS in LINE is in a separate node
127 UN(ist,k) = max(UNarv_ik,UNdep_ik);
128 end
129 end
130 else % lld/cd/ljd cases
131 ind = sn.stationToNode(ist);
132 UN(ist,1:K) = 0;
133 for st = wset
134 [ni,~,sir] = State.toMarginal(sn, ind, StateSpace(st,(istSpaceShift(ist)+1):(istSpaceShift(ist)+size(sn.space{ist},2))));
135 if ni>0
136 for k=1:K
137 UN(ist,k) = UN(ist,k) + probSysState(st)*sir(k)/S(ist);
138 end
139 end
140 end
141 end
142 end
143 end
144end
145
146for k=1:K
147 for ist=1:M
148 if TN(ist,k)>0
149 RN(ist,k) = QN(ist,k)./TN(ist,k);
150 else
151 RN(ist,k)=0;
152 end
153 end
154 CN(k) = NK(k)./XN(k);
155end
156
157QN(isnan(QN))=0;
158CN(isnan(CN))=0;
159RN(isnan(RN))=0;
160UN(isnan(UN))=0;
161XN(isnan(XN))=0;
162TN(isnan(TN))=0;
163end