LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
refreshProcessRepresentations.m
1function [ph, phases] = refreshProcessRepresentations(self)
2% [PH, PHASES] = REFRESHPROCESSREPRESENTATIONS()
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7
8M = getNumberOfStations(self);
9K = getNumberOfClasses(self);
10ph = cell(M,1);
11for ist=1:M
12 ph{ist,1} = cell(1,K);
13end
14phases = zeros(M,K);
15stations = self.stations;
16for ist=1:M
17 if ist == self.getIndexSourceStation
18 ph_i = stations{ist}.getSourceRates();
19 else
20 switch class(stations{ist})
21 case 'Fork'
22 mu_i = cell(1,K);
23 phi_i = cell(1,K);
24 for r=1:K
25 mu_i{r} = NaN;
26 phi_i{r} = NaN;
27 end
28 ph_i = Coxian(mu_i,phi_i).getProcess;
29 case 'Join'
30 mu_i = cell(1,K);
31 phi_i = cell(1,K);
32 for r=1:K
33 mu_i{r} = NaN;
34 phi_i{r} = NaN;
35 ph_i{r} = Coxian(mu_i{r},phi_i{r}).getProcess;
36 end
37 otherwise
38 ph_i = stations{ist}.getServiceRates();
39 end
40 end
41 for r=1:K
42 ph{ist}{r} = ph_i{r};
43 % NHPP carries a rate schedule, not D0/D1: always 1 active phase
44 isSchedule_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 isSchedule_ir = ismethod(stations{ist}.input.sourceClasses{r}{end}, 'getRateSchedule');
49 elseif isa(stations{ist}, 'ServiceStation') ...
50 && length(stations{ist}.server.serviceProcess) >= r ...
51 && ~isempty(stations{ist}.server.serviceProcess{r})
52 isSchedule_ir = ismethod(stations{ist}.server.serviceProcess{r}{end}, 'getRateSchedule');
53 end
54 if isempty(ph{ist}{r}) % fluid fails otherwise
55 phases(ist,r) = 1;
56 elseif isSchedule_ir
57 phases(ist,r) = 1;
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
63 phases(ist,r) = 0;
64 else
65 phases(ist,r) = length(ph{ist}{r}{1});
66 end
67 end
68end
69if ~isempty(self.sn) %&& isprop(self.sn,'mu')
70 proc = ph;
71 pie = cell(size(ph));
72 for ist=1:M
73 for r=1:K
74 map_ir = ph{ist}{r};
75 % NHPP carries a rate schedule, not D0/D1: skip map_pie
76 isSchedule_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 isSchedule_ir = ismethod(stations{ist}.input.sourceClasses{r}{end}, 'getRateSchedule');
81 elseif isa(stations{ist}, 'ServiceStation') ...
82 && length(stations{ist}.server.serviceProcess) >= r ...
83 && ~isempty(stations{ist}.server.serviceProcess{r})
84 isSchedule_ir = ismethod(stations{ist}.server.serviceProcess{r}{end}, 'getRateSchedule');
85 end
86 if ~isempty(map_ir)
87 proc{ist}{r} = map_ir;
88 if isSchedule_ir
89 pie{ist}{r} = NaN;
90 else
91 pie{ist}{r} = map_pie(map_ir);
92 end
93 else
94 pie{ist}{r} = NaN;
95 end
96 end
97 end
98 self.sn.proc = proc;
99 self.sn.pie = pie;
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 % Marked (MMAP) source classes share the carrier's modulating chain: the
104 % non-carrier classes (mark index > 1) contribute a single always-zero
105 % state column rather than their own phase block.
106 if isfield(self.sn,'markidx') && ~isempty(self.sn.markidx)
107 self.sn.phasessz(self.sn.markidx > 1) = 1;
108 end
109 self.sn.phaseshift = [zeros(size(phases,1),1),cumsum(self.sn.phasessz,2)];
110end
111end
112
113function result = isMAP(proc)
114% ISMAP Check if a process representation is a valid MAP {D0, D1}
115%
116% A valid representation has at least 2 cell elements (D0, D1, plus
117% optional marked/batch matrices D_k as in BMAP/MarkedMAP), all square
118% matrices of the same size.
119result = false;
120if ~iscell(proc) || length(proc) < 2
121 return;
122end
123n0 = size(proc{1}, 1);
124for e = 1:length(proc)
125 De = proc{e};
126 if ~isnumeric(De) || ~ismatrix(De) || size(De,1) ~= size(De,2) || size(De,1) ~= n0
127 return;
128 end
129end
130result = true;
131end
132
133function MAP = convertToMAP(station, classIdx, proc)
134% CONVERTTOMAP Convert non-Markovian distribution parameters to MAP
135%
136% For non-Markovian distributions, the process representation contains
137% distribution parameters rather than {D0, D1} matrices. This function
138% converts them to an Erlang approximation.
139
140% Get the distribution object from the station
141if isa(station, 'Source')
142 dist = station.input.sourceClasses{classIdx}{end};
143else
144 dist = station.server.serviceProcess{classIdx}{end};
145end
146
147% Get mean for Erlang approximation
148targetMean = dist.getMean();
149
150% Determine number of phases based on SCV
151% For Det (SCV=0), use high number of phases; for others, match SCV
152scv = dist.getSCV();
153if scv < GlobalConstants.CoarseTol
154 % Deterministic or near-deterministic: use 20 phases
155 nPhases = 20;
156else
157 % Match SCV: for Erlang, SCV = 1/n, so n = 1/SCV
158 nPhases = max(1, ceil(1/scv));
159 nPhases = min(nPhases, 100); % Cap at 100 phases
160end
161
162% Create Erlang MAP approximation
163MAP = map_erlang(targetMean, nPhases);
164end
Definition Station.m:245