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 dist_ir = [];
46 if isa(stations{ist}, 'Source') && any(ist == self.getIndexSourceStation) ...
47 && length(stations{ist}.input.sourceClasses) >= r ...
48 && ~isempty(stations{ist}.input.sourceClasses{r})
49 dist_ir = stations{ist}.input.sourceClasses{r}{end};
50 isSchedule_ir = ismethod(dist_ir, 'getRateSchedule');
51 elseif isa(stations{ist}, 'ServiceStation') ...
52 && length(stations{ist}.server.serviceProcess) >= r ...
53 && ~isempty(stations{ist}.server.serviceProcess{r})
54 dist_ir = stations{ist}.server.serviceProcess{r}{end};
55 isSchedule_ir = ismethod(dist_ir, 'getRateSchedule');
56 end
57 % Gamma/Weibull/Lognormal/Pareto/Uniform return the raw distribution
58 % PARAMETERS from getProcess, not a (D0,D1) pair. Two 1x1 parameters
59 % satisfy isMAP's "square matrices of equal size" shape test, so they
60 % were classified as a valid single-phase MAP and convertToMAP was never
61 % reached: sn.proc then held {D0=shape, D1=scale}, phases was 1, and
62 % map_pie ran on parameters. The shape test cannot tell a 1x1 (D0,D1)
63 % from two scalars, so the process type has to be asked directly. Det
64 % returns a single parameter and already fails the length>=2 test.
65 isRawParam_ir = ~isempty(dist_ir) && (isa(dist_ir,'Gamma') ...
66 || isa(dist_ir,'Weibull') || isa(dist_ir,'Lognormal') ...
67 || isa(dist_ir,'Pareto') || isa(dist_ir,'Uniform'));
68 if isempty(ph{ist}{r}) % fluid fails otherwise
69 phases(ist,r) = 1;
70 elseif isSchedule_ir
71 phases(ist,r) = 1;
72 elseif isRawParam_ir || ~isMAP(ph{ist}{r})
73 % Non-Markovian distribution: convert to MAP representation
74 ph{ist}{r} = convertToMAP(stations{ist}, r, ph{ist}{r});
75 phases(ist,r) = length(ph{ist}{r}{1});
76 elseif any(isnan(ph{ist}{r}{1}(:))) || any(isnan(ph{ist}{r}{2}(:))) % disabled
77 phases(ist,r) = 0;
78 else
79 phases(ist,r) = length(ph{ist}{r}{1});
80 end
81 end
82end
83if ~isempty(self.sn) %&& isprop(self.sn,'mu')
84 proc = ph;
85 pie = cell(size(ph));
86 for ist=1:M
87 for r=1:K
88 map_ir = ph{ist}{r};
89 % NHPP carries a rate schedule, not D0/D1: skip map_pie
90 isSchedule_ir = false;
91 if isa(stations{ist}, 'Source') && any(ist == self.getIndexSourceStation) ...
92 && length(stations{ist}.input.sourceClasses) >= r ...
93 && ~isempty(stations{ist}.input.sourceClasses{r})
94 isSchedule_ir = ismethod(stations{ist}.input.sourceClasses{r}{end}, 'getRateSchedule');
95 elseif isa(stations{ist}, 'ServiceStation') ...
96 && length(stations{ist}.server.serviceProcess) >= r ...
97 && ~isempty(stations{ist}.server.serviceProcess{r})
98 isSchedule_ir = ismethod(stations{ist}.server.serviceProcess{r}{end}, 'getRateSchedule');
99 end
100 if ~isempty(map_ir)
101 proc{ist}{r} = map_ir;
102 if isSchedule_ir
103 pie{ist}{r} = NaN;
104 else
105 pie{ist}{r} = map_pie(map_ir);
106 end
107 else
108 pie{ist}{r} = NaN;
109 end
110 end
111 end
112 self.sn.proc = proc;
113 self.sn.pie = pie;
114 self.sn.phases = phases;
115 self.sn.phasessz = max(self.sn.phases,ones(size(self.sn.phases)));
116 self.sn.phasessz(self.sn.nodeToStation(self.sn.nodetype == NodeType.Join),:)=phases(self.sn.nodeToStation(self.sn.nodetype == NodeType.Join),:);
117 % Marked (MMAP) source classes share the carrier's modulating chain: the
118 % non-carrier classes (mark index > 1) contribute a single always-zero
119 % state column rather than their own phase block.
120 if isfield(self.sn,'markidx') && ~isempty(self.sn.markidx)
121 self.sn.phasessz(self.sn.markidx > 1) = 1;
122 end
123 self.sn.phaseshift = [zeros(size(phases,1),1),cumsum(self.sn.phasessz,2)];
124end
125end
126
127function result = isMAP(proc)
128% ISMAP Check if a process representation is a valid MAP {D0, D1}
129%
130% A valid representation has at least 2 cell elements (D0, D1, plus
131% optional marked/batch matrices D_k as in BMAP/MarkedMAP), all square
132% matrices of the same size.
133result = false;
134if ~iscell(proc) || length(proc) < 2
135 return;
136end
137n0 = size(proc{1}, 1);
138for e = 1:length(proc)
139 De = proc{e};
140 if ~isnumeric(De) || ~ismatrix(De) || size(De,1) ~= size(De,2) || size(De,1) ~= n0
141 return;
142 end
143end
144result = true;
145end
146
147function MAP = convertToMAP(station, classIdx, proc)
148% CONVERTTOMAP Convert non-Markovian distribution parameters to MAP
149%
150% For non-Markovian distributions, the process representation contains
151% distribution parameters rather than {D0, D1} matrices. This function
152% converts them to an Erlang approximation.
153
154% Get the distribution object from the station
155if isa(station, 'Source')
156 dist = station.input.sourceClasses{classIdx}{end};
157else
158 dist = station.server.serviceProcess{classIdx}{end};
159end
160
161% Get mean for Erlang approximation
162targetMean = dist.getMean();
163
164% Determine number of phases based on SCV
165% For Det (SCV=0), use high number of phases; for others, match SCV
166scv = dist.getSCV();
167if scv < GlobalConstants.CoarseTol
168 % Deterministic or near-deterministic: use 20 phases
169 nPhases = 20;
170else
171 % Match SCV: for Erlang, SCV = 1/n, so n = 1/SCV
172 nPhases = max(1, ceil(1/scv));
173 nPhases = min(nPhases, 100); % Cap at 100 phases
174end
175
176% Create Erlang MAP approximation
177MAP = map_erlang(targetMean, nPhases);
178end