1function [SS,SSh,sn,Adj,ST] = spaceGenerator(sn, cutoff, options)
2% SPACEGENERATOR Generate complete state space
for queueing network analysis
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)
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.
19% Copyright (c) 2012-2026, Imperial College London
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.
');
34 cutoff = cutoff * ones(sn.nstations, sn.nclasses);
37[~, sn, capacityc] = State.spaceGeneratorNodes(sn, cutoff, options);
40isOpenClass = isinf(Np);
41isClosedClass = ~isOpenClass;
42for r=1:sn.nclasses %cut-off open classes to finite capacity
44 Np(r) = max(capacityc(:,r)); % if replaced by sum stateMarg_i can exceed capacity
48nstatefulp = sn.nstateful - sum(sn.nodetype == NodeType.Source); % M without sources
52 % this is rather inefficient since n ignores chains and thus it can
53 % generate state spaces such as
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)];
82chainStationPos = unique(chainStationPos,'rows
');
84netstates = cell(size(chainStationPos,1), sn.nstateful);
85for j=1:size(chainStationPos,1)
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,[]);
97 state_i = State.fromMarginal(sn,ind,stateMarg_i);
98 netstates{j,isf} = State.getHash(sn,ind,state_i);
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 netstates{j,isf} = State.getHash(sn,ind,state_i);
112 state_i = state_i(findrows(state_i(:,1:length(stateMarg_i)),stateMarg_i),:);
113 netstates{j,isf} = State.getHash(sn,ind,state_i);
123for j=1:size(chainStationPos,1)
124 % for each network state
125 v = {netstates{j,:}};
127 vN = cellfun(@length,v)-1;
133 h{isf} = v{isf}(1+n(isf));
138 u{isf} = sn.space{isf}(v{isf}(1+n(isf)),:);
141 ctr = ctr + 1; % do not move
142 SS(ctr,:)=cell2mat(u);
143 SSh(ctr,:)=cell2mat(h);
148[SS,IA] = unique(SS,'rows
');