1function [ph, phases] = refreshProcessRepresentations(self)
2% [PH, PHASES] = REFRESHPROCESSREPRESENTATIONS()
4% Copyright (c) 2012-2026, Imperial College London
8M = getNumberOfStations(self);
9K = getNumberOfClasses(self);
12 ph{ist,1} = cell(1,K);
15stations = self.stations;
17 if ist == self.getIndexSourceStation
18 ph_i = stations{ist}.getSourceRates();
20 switch class(stations{ist})
28 ph_i = Coxian(mu_i,phi_i).getProcess;
35 ph_i{r} = Coxian(mu_i{r},phi_i{r}).getProcess;
38 ph_i = stations{ist}.getServiceRates();
43 % CyclicPoisson stores {rates, durations}, not D0/D1: always 1 active phase
44 isCyclicPoisson_ir =
false;
45 if isa(stations{ist},
'Source') && any(ist == self.getIndexSourceStation) ...
46 && length(stations{ist}.input.sourceClasses) >= r ...
47 && ~isempty(stations{ist}.input.sourceClasses{r})
48 isCyclicPoisson_ir = isa(stations{ist}.input.sourceClasses{r}{end},
'CyclicPoisson');
49 elseif isa(stations{ist},
'ServiceStation') ...
50 && length(stations{ist}.server.serviceProcess) >= r ...
51 && ~isempty(stations{ist}.server.serviceProcess{r})
52 isCyclicPoisson_ir = isa(stations{ist}.server.serviceProcess{r}{end},
'CyclicPoisson');
54 if isempty(ph{ist}{r}) % fluid fails otherwise
56 elseif isCyclicPoisson_ir
58 elseif ~isMAP(ph{ist}{r})
59 % Non-Markovian distribution: convert to MAP representation
60 ph{ist}{r} = convertToMAP(stations{ist}, r, ph{ist}{r});
61 phases(ist,r) = length(ph{ist}{r}{1});
62 elseif any(isnan(ph{ist}{r}{1}(:))) || any(isnan(ph{ist}{r}{2}(:))) % disabled
65 phases(ist,r) = length(ph{ist}{r}{1});
69if ~isempty(self.sn) %&& isprop(self.sn,
'mu')
75 % CyclicPoisson stores {rates, durations}, not D0/D1 — skip map_pie
76 isCyclicPoisson_ir =
false;
77 if isa(stations{ist},
'Source') && any(ist == self.getIndexSourceStation) ...
78 && length(stations{ist}.input.sourceClasses) >= r ...
79 && ~isempty(stations{ist}.input.sourceClasses{r})
80 isCyclicPoisson_ir = isa(stations{ist}.input.sourceClasses{r}{end},
'CyclicPoisson');
81 elseif isa(stations{ist},
'ServiceStation') ...
82 && length(stations{ist}.server.serviceProcess) >= r ...
83 && ~isempty(stations{ist}.server.serviceProcess{r})
84 isCyclicPoisson_ir = isa(stations{ist}.server.serviceProcess{r}{end},
'CyclicPoisson');
87 proc{ist}{r} = map_ir;
91 pie{ist}{r} = map_pie(map_ir);
100 self.sn.phases = phases;
101 self.sn.phasessz = max(self.sn.phases,ones(size(self.sn.phases)));
102 self.sn.phasessz(self.sn.nodeToStation(self.sn.nodetype == NodeType.Join),:)=phases(self.sn.nodeToStation(self.sn.nodetype == NodeType.Join),:);
103 self.sn.phaseshift = [zeros(size(phases,1),1),cumsum(self.sn.phasessz,2)];
107function result = isMAP(proc)
108% ISMAP Check
if a process representation
is a valid MAP {D0, D1}
110% A valid MAP has exactly 2 cell elements, both of which are square matrices
113if ~iscell(proc) || length(proc) ~= 2
118if ~isnumeric(D0) || ~isnumeric(D1)
121if ~ismatrix(D0) || ~ismatrix(D1)
126if m0 ~= n0 || m1 ~= n1 || m0 ~= m1
132function MAP = convertToMAP(station, classIdx, proc)
133% CONVERTTOMAP Convert non-Markovian distribution parameters to MAP
135% For non-Markovian distributions, the process representation contains
136% distribution parameters rather than {D0, D1} matrices. This function
137% converts them to an Erlang approximation.
139% Get the distribution
object from the station
140if isa(station,
'Source')
141 dist = station.input.sourceClasses{classIdx}{end};
143 dist = station.server.serviceProcess{classIdx}{end};
146% Get mean
for Erlang approximation
147targetMean = dist.getMean();
149% Determine number of phases based on SCV
150% For Det (SCV=0), use high number of phases;
for others, match SCV
152if scv < GlobalConstants.CoarseTol
153 % Deterministic or near-deterministic: use 20 phases
156 % Match SCV:
for Erlang, SCV = 1/n, so n = 1/SCV
157 nPhases = max(1, ceil(1/scv));
158 nPhases = min(nPhases, 100); % Cap at 100 phases
161% Create Erlang MAP approximation
162MAP = map_erlang(targetMean, nPhases);