1function space = fromMarginalAndStarted(sn, ind, n, s, options)
2% SPACE = FROMMARGINALANDSTARTED(QN, IND, N, S, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
7if nargin<5 %~exist(
'options',
'var')
13% generate one initial state such that the marginal queue-lengths are as in vector n
14% n(r): number of jobs at the station in class r
15% s(r): jobs of class r that are running
20ist = sn.nodeToStation(ind);
21%isf = sn.nodeToStateful(ind);
25 if isempty(sn.proc{ist}{r})
28 K(r) = length(sn.proc{ist}{r}{1});
31else % node or stateful
32 if sn.nodetype(ind) == NodeType.Transition
33 K = zeros(1,sn.nodeparam{ind}.nmodes);
34 for m=1:sn.nodeparam{ind}.nmodes
35 if isempty(sn.nodeparam{ind}.firingproc{m})
38 K(m) = length(sn.nodeparam{ind}.firingproc{m}{1});
46if sn.isstation(ind) && any(n>sn.classcap(ist,:))
47 exceeded = n>sn.classcap(ist,:);
49 if ~isempty(sn.proc) && ~isempty(sn.proc{ist}{r}) && any(any(isnan(sn.proc{ist}{r}{1})))
50 line_warning(mfilename,'State vector at station %d (n=%s) exceeds the class capacity (classcap=%s). Some service
classes are disabled.\n',ist,mat2str(n(ist,:)),mat2str(sn.classcap(ist,:)));
52 line_warning(mfilename,'State vector at station %d (n=%s) exceeds the class capacity (classcap=%s).\n',ist,mat2str(n(ist,:)),mat2str(sn.classcap(ist,:)));
57if sn.isstation(ind) && (sn.nservers(ist)>0 && sum(s) > sn.nservers(ist))
60% generate local-state space
61switch sn.nodetype(ind)
62 case {NodeType.Queue, NodeType.Delay, NodeType.Source}
64 case SchedStrategy.EXT
66 init = State.spaceClosedSingle(K(r),0);
68 if ~isempty(sn.proc) && ~isempty(sn.proc{ist}{r}) && any(any(isnan(sn.proc{ist}{r}{1})))
69 init(1) = 0; % class
is not processed at this source
71 % init the job generation
75 state = State.cartesian(state,init);
77 space = State.cartesian(space,state);
78 space = [Inf*ones(size(space,1),1),space];
79 case {SchedStrategy.INF, SchedStrategy.PS, SchedStrategy.DPS, SchedStrategy.GPS, SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO, SchedStrategy.GPSPRIO, SchedStrategy.LPS}
80 % in these policies we only track the jobs in the servers
82 init = State.spaceClosedSingle(K(r),0);
84 state = State.cartesian(state,init);
86 space = State.cartesian(space,state);
87 case {SchedStrategy.SIRO, SchedStrategy.LEPT, SchedStrategy.SEPT, SchedStrategy.SRPT, SchedStrategy.SRPTPRIO, SchedStrategy.POLLING}
88 % in these policies we track an un-ordered buffer and
89 % the jobs in the servers
90 % build list of job
classes in the node, with repetition
93 init = State.spaceClosedSingle(K(r),0);
95 state = State.cartesian(state,init);
97 space = State.cartesian(space,[zeros(size(state,1),R),state]);
99 % si = multichoosecon(n,S(i)); % jobs of
class r that are running
101 mi_buf = repmat(n,size(si,1),1) - si; % jobs of
class r in buffer
103 % determine number of
classes r jobs running in phase j
106 init = State.spaceClosedSingle(K(r),0);
108 kstate = State.cartesian(kstate,init);
110 state = [repmat(mi_buf(k,:),size(kstate,1),1), kstate];
111 space = [space; state];
114 case {SchedStrategy.FCFS, SchedStrategy.HOL,SchedStrategy.FCFSPRIO, SchedStrategy.LCFS,SchedStrategy.LCFSPRIO}
116 space = zeros(1,1+max(R,sum(K)));
119 % in these policies we track an ordered buffer and
120 % the jobs in the servers
122 % build list of job
classes in the buffer, with repetition
126 inbuf=[inbuf, r*ones(1,n(r)-s(r))];
130 sizeEstimator = multinomialln(n);
131 sizeEstimator = round(sizeEstimator/log(10));
133 if ~isfield(options,
'force') || options.force ==
false
134 line_warning(sprintf(
'State space size is very large: 1e%d states. Cannot generate valid state space. Initializing station $d from a default state.\n',sizeEstimator,ind));
140 % gen permutation of their positions in the fcfs buffer
141 mi = uniqueperms(inbuf);
143 mi_buf = zeros(1,max(1,sum(n)-S(ist)));
144 state = zeros(1,sum(K));
145 state = [mi_buf,state];
147 % mi_buf:
class of job in buffer position i (0=empty)
149 mi_buf = mi(:,1:(sum(n)-sum(s)));
150 else % set an empty buffer
154 % mi_srv:
class of jobs running in the server of i
157 mi_srv = [mi_srv, r*ones(1,s(r))];
159 % si: number of
class r jobs that are running
161 %si = unique(si,
'rows');
162 for b=1:size(mi_buf,1)
164 % determine number of classs r jobs running in phase
165 % j in server state mi_srv(kjs,:) and build
169 % kstate = State.cartesian(kstate,State.spaceClosedSingle(K(r),si(k,r)));
170 init = State.spaceClosedSingle(K(r),0);
172 kstate = State.cartesian(kstate,init);
174 state = [state; repmat(mi_buf(b,:),size(kstate,1),1), kstate];
178 case {SchedStrategy.LCFSPR, SchedStrategy.LCFSPI, SchedStrategy.LCFSPRPRIO, SchedStrategy.LCFSPIPRIO}
181 space = zeros(1,1+sum(K));
184 % in these policies we track an ordered buffer and
185 % the jobs in the servers
187 % build list of job
classes in the buffer, with repetition
191 inbuf=[inbuf, r*ones(1,n(r)-s(r))];
195 sizeEstimator = multinomialln(n);
196 sizeEstimator = round(sizeEstimator/log(10));
198 if ~isfield(options,
'force') || options.force ==
false
199 line_warning(sprintf(
'State space size is very large: 1e%d states. Cannot generate valid state space. Initializing station $d from a default state.\n',sizeEstimator,ind));
205 % gen permutation of their positions in the fcfs buffer
206 mi = uniqueperms(inbuf);
208 mi_buf = zeros(1,max(1,sum(n)-S(ist)));
209 state = zeros(1,sum(K));
210 state = [mi_buf,state];
212 % mi_buf:
class of job in buffer position i (0=empty)
214 mi_buf = mi(:,1:(sum(n)-sum(s)));
215 else % set an empty buffer
220 % mi_srv:
class of jobs running in the server of i
223 mi_srv = [mi_srv, r*ones(1,s(r))];
225 % si: number of
class r jobs that are running
227 %si = unique(si,
'rows');
228 for b=1:size(mi_buf,1)
230 % determine number of class r jobs running in phase
231 % j in server state mi_srv(kjs,:) and build
235 % kstate = State.cartesian(kstate,State.spaceClosedSingle(K(r),si(k,r)));
236 init = State.spaceClosedSingle(K(r),0);
238 kstate = State.cartesian(kstate,init);
242 for j=mi_buf(b,:) % for each job in the buffer
244 bkstate = State.cartesian(bkstate,[1:K(j)]');
249 bufstate_tmp = State.cartesian(mi_buf(b,:), bkstate);
250 % here interleave positions of class and phases in
252 bufstate = zeros(size(bufstate_tmp));
253 bufstate(:,1:2:end)=bufstate_tmp(:,1:size(mi_buf,2));
254 bufstate(:,2:2:end)=bufstate_tmp(:,(size(mi_buf,2)+1):end);
255 state = [state; State.cartesian(bufstate, kstate)];
259 case {SchedStrategy.SJF, SchedStrategy.LJF}
260 % in these policies the state space includes continuous
261 % random variables
for the service times
262 % in these policies we only track the jobs in the servers
265 init = State.spaceClosedSingle(K(r),0);
267 state = State.cartesian(state,init);
269 space = State.cartesian(space,state);
270 %
this is not casted as an error since
this function
is
273 % called to initial models with SJF and LJF
274 line_warning(mfilename,
'The scheduling policy does not admit a discrete state space.\n');
278 case SchedStrategy.INF
279 % in
this policies we only track the jobs in the servers
281 init = State.spaceClosedSingle(K(r),n(r));
282 state = State.cartesian(state,init);
284 space = State.cartesian(space,state);
288 case NodeType.Transition
289 line_error(mfilename,
'fromMarginalAndStarted cannot be used on Petri net elements');
291 line_error(mfilename,
'fromMarginalAndStarted cannot be used on Petri net elements');
293space = unique(space,
'rows'); %
do not comment, required to sort empty state as first
294space = space(end:-1:1,:); %
this ensures that states where jobs start in phase 1 are first, which
is used eg in SSA