1function space = fromMarginalAndRunning(sn, ind, n, s, options)
2% SPACE = FROMMARGINALANDRUNNING(QN, IND, N, S, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
7if nargin<5 %~exist(
'options',
'var')
14ist = sn.nodeToStation(ind);
15isf = sn.nodeToStateful(ind);
17% generate one initial state such that the marginal queue-lengths are as in vector n
18% n(r): number of jobs at the station in class r
19% s(r): jobs of class r that are running
24 if isempty(sn.proc{ist}{r})
27 K(r) = length(sn.proc{ist}{r}{1});
32if any(n>sn.classcap(ist,:))
33 exceeded = n>sn.classcap(ist,:);
35 if ~isempty(sn.proc) && ~isempty(sn.proc{ist}{r}) && any(any(isnan(sn.proc{ist}{r}{1})))
36 line_warning(mfilename,sprintf('State vector at station %d (n=%s) exceeds the class capacity (classcap=%s). Some service
classes are disabled.\n',ist,mat2str(n),mat2str(sn.classcap(ist,:))));
38 line_warning(mfilename,sprintf('State vector at station %d (n=%s) exceeds the class capacity (classcap=%s).\n',ist,mat2str(n),mat2str(sn.classcap(ist,:))));
43if (sn.nservers(ist)>0 && sum(s) > sn.nservers(ist))
46% generate local-state space
47switch sn.nodetype(ind)
48 case {NodeType.Queue, NodeType.Delay, NodeType.Source}
50 case SchedStrategy.EXT
52 init = State.spaceClosedSingle(K(r),0);
54 if isnan(sn.rates(ist,r))
55 init(1) = 0; %
class is not processed at this source
60 state = State.cartesian(state,init);
62 space = State.cartesian(space,state);
63 space = [Inf*ones(size(space,1),1),space];
64 case {SchedStrategy.INF, SchedStrategy.PS, SchedStrategy.DPS, SchedStrategy.GPS, SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO, SchedStrategy.GPSPRIO, SchedStrategy.LPS}
65 % in these policies we only track the jobs in the servers
67 init = State.spaceClosedSingle(K(r),n(r));
68 state = State.cartesian(state,init);
70 space = State.cartesian(space,state);
71 case {SchedStrategy.SIRO, SchedStrategy.LEPT, SchedStrategy.SEPT, SchedStrategy.SRPT, SchedStrategy.SRPTPRIO}
72 % in these policies we track an un-ordered buffer and
73 % the jobs in the servers
74 % build list of job
classes in the node, with repetition
77 init = State.spaceClosedSingle(K(r),n(r));
78 state = State.cartesian(state,init);
80 space = State.cartesian(space,[zeros(size(state,1),R),state]);
82 % si = multichoosecon(n,S(i)); % jobs of
class r that are running
84 mi_buf = repmat(n,size(si,1),1) - si; % jobs of
class r in buffer
86 % determine number of
classes r jobs running in phase j
89 init = State.spaceClosedSingle(K(r),si(k,r));
90 kstate = State.cartesian(kstate,init);
92 state = [repmat(mi_buf(k,:),size(kstate,1),1), kstate];
93 space = [space; state];
96 case {SchedStrategy.FCFS, SchedStrategy.HOL, SchedStrategy.LCFS}
97 sizeEstimator = multinomialln(n-s);
98 sizeEstimator = round(sizeEstimator/log(10));
100 if ~isfield(options,
'force') || options.force ==
false
101 line_warning(mfilename,sprintf(
'State space size is very large: 1e%d states. Stopping execution. Set options.force=true to bypass this control.\n',round(sizeEstimator/log(10))));
105 space = zeros(1,1+sum(K));
108 % in these policies we track an ordered buffer and
109 % the jobs in the servers
111 % build list of job
classes in the buffer, with repetition
115 inbuf=[inbuf, r*ones(1,n(r)-s(r))];
119 % gen permutation of their positions in the fcfs buffer
120 mi = uniqueperms(inbuf); %unique(perms(vi),
'rows) is notoriously slow
122 mi_buf = zeros(1,max(0,sum(n)-S(ist)));
124 state = State.cartesian(state,[mi_buf,state]);
126 % mi_buf: class of job in buffer position i (0=empty)
128 mi_buf = mi(:,1:(sum(n)-sum(s)));
129 else % set an empty buffer
132 % si: number of class r jobs that are running
134 %si = unique(si,'rows
');
135 for b=1:size(mi_buf,1)
137 % determine number of classs r jobs running in phase
138 % j in server state mi_srv(kjs,:) and build
142 % kstate = State.cartesian(kstate,State.spaceClosedSingle(K(r),si(k,r)));
143 init = State.spaceClosedSingle(K(r),si(k,r));
144 kstate = State.cartesian(kstate,init);
146 state = [state; repmat(mi_buf(b,:),size(kstate,1),1), kstate];
151 case SchedStrategy.LCFSPR
153 sizeEstimator = multinomialln(n-s);
154 sizeEstimator = round(sizeEstimator/log(10));
156 if ~isfield(options,'force
') || options.force == false
157 line_warning(mfilename,sprintf('State space size
is very large: 1e%d states. Stopping execution. Set options.force=true to bypass this control.\n
',round(sizeEstimator/log(10))));
161 space = zeros(1,1+sum(K));
164 % in these policies we track an ordered buffer and
165 % the jobs in the servers
167 % build list of job classes in the buffer, with repetition
171 inbuf=[inbuf, r*ones(1,n(r)-s(r))];
175 % gen permutation of their positions in the fcfs buffer
176 mi = uniqueperms(inbuf); %unique(perms(vi),'rows)
is notoriously slow
178 mi_buf = zeros(1,max(0,sum(n)-S(ist)));
180 state = State.cartesian(state,[mi_buf,state]);
182 % mi_buf:
class of job in buffer position i (0=empty)
184 mi_buf = mi(:,1:(sum(n)-sum(s)));
185 else % set an empty buffer
188 % si: number of
class r jobs that are running
190 %si = unique(si,
'rows');
191 for b=1:size(mi_buf,1)
193 % determine number of classs r jobs running in phase
194 % j in server state mi_srv(kjs,:) and build
198 % kstate = State.cartesian(kstate,State.spaceClosedSingle(K(r),si(k,r)));
199 init = State.spaceClosedSingle(K(r),si(k,r));
200 kstate = State.cartesian(kstate,init);
203 for j=mi_buf(b,:) % for each job in the buffer
205 bkstate = State.cartesian(bkstate,[1:K(j)]');
210 bufstate_tmp = State.cartesian(mi_buf(b,:), bkstate);
211 % here interleave positions of class and phases in
213 bufstate = zeros(size(bufstate_tmp));
214 bufstate(:,1:2:end)=bufstate_tmp(:,1:size(mi_buf,2));
215 bufstate(:,2:2:end)=bufstate_tmp(:,(size(mi_buf,2)+1):end);
216 state = [state; State.cartesian(bufstate, kstate)];
221 case {SchedStrategy.SJF, SchedStrategy.LJF}
222 % in these policies the state space includes continuous
223 % random variables
for the service times
224 line_warning(mfilename,
'The scheduling policy does not admit a discrete state space.\n');
228 case SchedStrategy.INF
229 % in
this policies we only track the jobs in the servers
231 init = State.spaceClosedSingle(K(r),n(r));
232 state = State.cartesian(state,init);
234 space = State.cartesian(space,state);
237space = unique(space,
'rows'); %
do not comment, required to sort empty state as first
238space = space(end:-1:1,:); % so that states with jobs in phase 1 comes earlier