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 % Unordered buffer -- see _kb/04-networkstruct.md
73 % build list of job classes in the node, with repetition
76 init = State.spaceClosedSingle(K(r),n(r));
77 state = State.cartesian(state,init);
79 space = State.cartesian(space,[zeros(size(state,1),R),state]);
81 % si = multichoosecon(n,S(i)); % jobs of
class r that are running
83 mi_buf = repmat(n,size(si,1),1) - si; % jobs of
class r in buffer
85 % determine number of classes r jobs running in phase j
88 init = State.spaceClosedSingle(K(r),si(k,r));
89 kstate = State.cartesian(kstate,init);
91 state = [repmat(mi_buf(k,:),size(kstate,1),1), kstate];
92 space = [space; state];
95 case {SchedStrategy.FCFS, SchedStrategy.HOL, SchedStrategy.LCFS}
96 sizeEstimator = multinomialln(n-s);
97 sizeEstimator = round(sizeEstimator/log(10));
99 if ~isfield(options,
'force') || options.force ==
false
100 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))));
104 space = zeros(1,1+sum(K));
107 % Ordered buffer -- see _kb/04-networkstruct.md
109 % build list of job classes in the buffer, with repetition
113 inbuf=[inbuf, r*ones(1,n(r)-s(r))];
117 % gen permutation of their positions in the fcfs buffer
118 mi = uniqueperms(inbuf); %unique(perms(vi),
'rows) is notoriously slow
120 mi_buf = zeros(1,max(0,sum(n)-S(ist)));
122 state = State.cartesian(state,[mi_buf,state]);
124 % mi_buf: class of job in buffer position i (0=empty)
126 mi_buf = mi(:,1:(sum(n)-sum(s)));
127 else % set an empty buffer
130 % si: number of class r jobs that are running
132 %si = unique(si,'rows
');
133 for b=1:size(mi_buf,1)
135 % determine number of classs r jobs running in phase
136 % j in server state mi_srv(kjs,:) and build
140 % kstate = State.cartesian(kstate,State.spaceClosedSingle(K(r),si(k,r)));
141 init = State.spaceClosedSingle(K(r),si(k,r));
142 kstate = State.cartesian(kstate,init);
144 state = [state; repmat(mi_buf(b,:),size(kstate,1),1), kstate];
149 case SchedStrategy.LCFSPR
151 sizeEstimator = multinomialln(n-s);
152 sizeEstimator = round(sizeEstimator/log(10));
154 if ~isfield(options,'force
') || options.force == false
155 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))));
159 space = zeros(1,1+sum(K));
162 % Ordered buffer -- see _kb/04-networkstruct.md
164 % build list of job classes in the buffer, with repetition
168 inbuf=[inbuf, r*ones(1,n(r)-s(r))];
172 % gen permutation of their positions in the fcfs buffer
173 mi = uniqueperms(inbuf); %unique(perms(vi),'rows)
is notoriously slow
175 mi_buf = zeros(1,max(0,sum(n)-S(ist)));
177 state = State.cartesian(state,[mi_buf,state]);
179 % mi_buf:
class of job in buffer position i (0=empty)
181 mi_buf = mi(:,1:(sum(n)-sum(s)));
182 else % set an empty buffer
185 % si: number of
class r jobs that are running
187 %si = unique(si,
'rows');
188 for b=1:size(mi_buf,1)
190 % determine number of classs r jobs running in phase
191 % j in server state mi_srv(kjs,:) and build
195 % kstate = State.cartesian(kstate,State.spaceClosedSingle(K(r),si(k,r)));
196 init = State.spaceClosedSingle(K(r),si(k,r));
197 kstate = State.cartesian(kstate,init);
200 for j=mi_buf(b,:) % for each job in the buffer
202 bkstate = State.cartesian(bkstate,[1:K(j)]');
207 bufstate_tmp = State.cartesian(mi_buf(b,:), bkstate);
208 % here interleave positions of class and phases in
210 bufstate = zeros(size(bufstate_tmp));
211 bufstate(:,1:2:end)=bufstate_tmp(:,1:size(mi_buf,2));
212 bufstate(:,2:2:end)=bufstate_tmp(:,(size(mi_buf,2)+1):end);
213 state = [state; State.cartesian(bufstate, kstate)];
218 case {SchedStrategy.SJF, SchedStrategy.LJF}
219 % in these policies the state space includes continuous
220 % random variables
for the service times
221 line_warning(mfilename,
'The scheduling policy does not admit a discrete state space.\n');
225 case SchedStrategy.INF
226 % in
this policies we only track the jobs in the servers
228 init = State.spaceClosedSingle(K(r),n(r));
229 state = State.cartesian(state,init);
231 space = State.cartesian(space,state);
234space = unique(space,
'rows'); %
do not comment, required to sort empty state as first
235space = space(end:-1:1,:); % so that states with jobs in phase 1 comes earlier