LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
spaceGenerator.m
1function [SS,SSh,sn,Adj,ST] = spaceGenerator(sn, cutoff, options)
2% SPACEGENERATOR Generate complete state space for queueing network analysis
3%
4% @brief Creates the complete state space including all possible network states
5% @param sn Network structure representing the queueing network
6% @param cutoff Population cutoff limits for open classes (scalar or matrix)
7% @param options Optional configuration structure for state generation
8% @return SS Complete state space matrix
9% @return SSh Hashed state space for efficient lookups
10% @return sn Updated network structure with state space information
11% @return Adj Adjacency matrix for state transitions (SPN support)
12% @return ST State transition information (SPN support)
13%
14% The state space generator creates all possible states for the queueing
15% network, including those not reachable from the initial state. This is
16% essential for steady-state analysis and performance metric computation.
17% For open classes, a cutoff parameter limits the maximum population.
18%
19% Copyright (c) 2012-2026, Imperial College London
20% All rights reserved.
21N = sn.njobs';
22Np = N;
23
24% Draft SPN support
25Adj = [];
26ST = [];
27
28%%
29if ~exist('cutoff','var') && any(isinf(Np)) % if the model has open classes
30 line_error(mfilename,'Unspecified cutoff for open classes in state space generator.');
31end
32
33if isscalar(cutoff)
34 cutoff = cutoff * ones(sn.nstations, sn.nclasses);
35end
36
37[~, sn, capacityc] = State.spaceGeneratorNodes(sn, cutoff, options);
38
39%%
40isOpenClass = isinf(Np);
41isClosedClass = ~isOpenClass;
42for r=1:sn.nclasses %cut-off open classes to finite capacity
43 if isOpenClass(r)
44 Np(r) = max(capacityc(:,r)); % if replaced by sum stateMarg_i can exceed capacity
45 end
46end
47
48nstatefulp = sn.nstateful - sum(sn.nodetype == NodeType.Source); % M without sources
49n = pprod(Np);
50chainStationPos=[];
51while n>=0
52 % this is rather inefficient since n ignores chains and thus it can
53 % generate state spaces such as
54 % J =
55 %
56 % 0 0 0 0
57 % 0 0 0 1
58 % 0 0 1 0
59 % 0 1 0 0
60 % 1 0 0 0
61 % 0 0 0 1
62 % 0 0 1 0
63 % 0 1 0 0
64 % 1 0 0 0
65 % 0 0 0 2
66 % 0 0 1 1
67 % 0 0 2 0
68 % 0 1 0 1
69 % 1 0 0 1
70 % 0 1 1 0
71 % 1 0 1 0
72 % 0 2 0 0
73 % 1 1 0 0
74 % 2 0 0 0
75 % that are then in the need for a call to unique
76 if all(isOpenClass) | (Np(isClosedClass) == n(isClosedClass)) %#ok<OR2>
77 chainStationPos = [chainStationPos; State.spaceClosedMultiCS(nstatefulp,n,sn.chains)];
78 end
79 n = pprod(n,Np);
80end
81
82chainStationPos = unique(chainStationPos,'rows');
83
84netstates = cell(size(chainStationPos,1), sn.nstateful);
85for j=1:size(chainStationPos,1)
86 for ind=1:sn.nnodes
87 if sn.nodetype(ind) == NodeType.Source
88 isf = sn.nodeToStateful(ind);
89 state_i = State.fromMarginal(sn,ind,[]);
90 netstates{j,isf} = State.getHash(sn,ind,state_i);
91 elseif sn.isstation(ind)
92 isf = sn.nodeToStateful(ind);
93 stateMarg_i = chainStationPos(j,(isf-sum(sn.nodetype(1:ind-1) == NodeType.Source)):nstatefulp:end);
94 if any(stateMarg_i > capacityc(ind,:))
95 netstates{j,isf} = State.getHash(sn,ind,[]);
96 else
97 state_i = State.fromMarginal(sn,ind,stateMarg_i);
98 netstates{j,isf} = State.getHash(sn,ind,state_i);
99 end
100 elseif sn.isstateful(ind)
101 isf = sn.nodeToStateful(ind);
102 stateMarg_i = chainStationPos(j,(isf-sum(sn.nodetype(1:ind-1) == NodeType.Source)):nstatefulp:end);
103 state_i = sn.space{isf};
104 if any(stateMarg_i > capacityc(ind,:))
105 netstates{j,isf} = State.getHash(sn,ind,[]);
106 elseif sn.nodetype(ind) == NodeType.Cache
107 % For cache nodes, we need to handle states with jobs in multiple classes
108 % (InitClass, HitClass, MissClass) since cache nodes support class switching
109 state_i = state_i(findrows(state_i(:,1:length(stateMarg_i)),stateMarg_i),:);
110 np = sn.nodeparam{ind};
111 if isfield(np,'retrievalSystemCapacity') && np.retrievalSystemCapacity > 0 ...
112 && isfield(np,'retrievalClasses') && ~isempty(np.retrievalClasses)
113 % Cross-node (cache <-> queue) consistency: an item recorded in the
114 % cache occupancy bitmap must correspond to a retrieval job that is
115 % either at a retrieval queue or ready to depart/arrive at the cache
116 % server, and an item being retrieved at a queue cannot also have a
117 % retrieval job sitting in the cache server. Enforced at the global
118 % composition (the local cache space alone cannot see the queues).
119 rc = np.retrievalClasses;
120 tcc = np.totalCacheCapacity;
121 lvs = length(stateMarg_i); % per-class server presence width
122 rsqi = np.retrievalSystemQueueIndices;
123 itemsInQueue = []; % 1-based item ids currently at a queue
124 validMarginal = true;
125 if ~isempty(rsqi)
126 aks = keys(rsqi);
127 for ak = 1:numel(aks)
128 arrivalClass = aks{ak}; % int32, 0-based arrival class
129 classCol = double(arrivalClass) + 1;
130 qNodes = rsqi(arrivalClass);
131 for qq = 1:numel(qNodes)
132 qIdx = qNodes(qq);
133 qOff = sn.nodeToStateful(qIdx) - sum(sn.nodetype(1:qIdx-1) == NodeType.Source);
134 for item = 1:size(rc,1)
135 if classCol > size(rc,2), continue; end
136 rClass = rc(item, classCol);
137 if rClass < 1, continue; end
138 col = qOff + (rClass-1)*nstatefulp;
139 if col > size(chainStationPos,2) || chainStationPos(j,col) == 0, continue; end
140 % item at a queue: no retrieval job may be at the cache server,
141 % and the item can be at only one queue
142 if stateMarg_i(rClass) > 0 || any(itemsInQueue == item)
143 validMarginal = false; break;
144 end
145 itemsInQueue(end+1) = item; %#ok<AGROW>
146 end
147 if ~validMarginal, break; end
148 end
149 if ~validMarginal, break; end
150 end
151 end
152 if ~validMarginal
153 state_i = zeros(0, size(state_i,2));
154 else
155 keep = false(size(state_i,1),1);
156 for row = 1:size(state_i,1)
157 st = state_i(row,:);
158 validRow = true;
159 itemsInRSstate = [];
160 for col = (lvs+tcc+1):size(st,2)
161 if st(col) == 0, continue; end
162 item = col - (lvs+tcc);
163 for jac = 1:size(rc,2)
164 rClass = rc(item, jac);
165 if rClass < 1, continue; end
166 % item in the bitmap but not at a queue requires a
167 % retrieval job ready to depart/arrive at the cache server
168 if ~any(itemsInQueue == item) && st(rClass) == 0
169 validRow = false; break;
170 end
171 end
172 if ~validRow, break; end
173 itemsInRSstate(end+1) = item; %#ok<AGROW>
174 end
175 if validRow
176 % every item at a queue must be recorded in the bitmap
177 for it = itemsInQueue
178 if ~any(itemsInRSstate == it)
179 validRow = false; break;
180 end
181 end
182 end
183 keep(row) = validRow;
184 end
185 state_i = state_i(keep,:);
186 end
187 end
188 netstates{j,isf} = State.getHash(sn,ind,state_i);
189 elseif sn.nodetype(ind) == NodeType.Transition
190 % Transition states are per-mode, not per-class; include all states
191 netstates{j,isf} = State.getHash(sn,ind,state_i);
192 else
193 state_i = state_i(findrows(state_i(:,1:length(stateMarg_i)),stateMarg_i),:);
194 netstates{j,isf} = State.getHash(sn,ind,state_i);
195 end
196 end
197 end
198end
199
200ctr = 0;
201%SS = sparse([]);
202SS = [];
203SSh = [];
204for j=1:size(chainStationPos,1)
205 % for each network state
206 v = {netstates{j,:}};
207 % cycle over lattice
208 vN = cellfun(@length,v)-1;
209 n = pprod(vN);
210 while n >=0
211 u={}; h={};
212 skip = false;
213 for isf=1:length(n)
214 h{isf} = v{isf}(1+n(isf));
215 if h{isf} < 0
216 skip = true;
217 break
218 end
219 u{isf} = sn.space{isf}(v{isf}(1+n(isf)),:);
220 end
221 if skip == false
222 ctr = ctr + 1; % do not move
223 SS(ctr,:)=cell2mat(u);
224 SSh(ctr,:)=cell2mat(h);
225 end
226 n = pprod(n,vN);
227 end
228end
229[SS,IA] = unique(SS,'rows');
230SSh = SSh(IA,:);
231end