LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
infer_fluid_ps_rt_likelihood.m
1function [ode_h, q_indices, augPhases, LIKE] = infer_fluid_ps_rt_likelihood(sn, taggedClass, y0_levels, Rsampled)
2% INFER_FLUID_PS_RT_LIKELIHOOD Fluid-based response time likelihood.
3%
4% Builds an augmented model with tagged class K+1 by expanding the
5% NetworkStruct arrays (following solver_fluid_passage_time's pattern),
6% then uses solver_fluid_odes to obtain the ODE handle.
7%
8% Usage modes:
9% [ode_h, q_indices, augPhases] = infer_fluid_ps_rt_likelihood(sn, taggedClass)
10% Build augmented model and return ODE handle + indexing info.
11%
12% [~, ~, ~, LIKE] = infer_fluid_ps_rt_likelihood(sn, taggedClass, y0_levels, Rsampled)
13% Build augmented model, solve ODE, and return likelihood.
14%
15% Inputs:
16% sn - NetworkStruct (from model.getStruct())
17% taggedClass - class index of the tagged job
18% y0_levels - M x K matrix of fluid levels per station per class (optional)
19% Rsampled - observed response time (optional)
20%
21% The augmented model adds class K+1 (tagged) with the same service rates
22% as taggedClass. At the reference (queue) station, the tagged class absorbs
23% by switching to taggedClass and routing to the delay station. The likelihood
24% is extracted from the ODE derivative at the terminal state.
25%
26% Reference: Casale, G. et al., "Fluid Analysis of Queueing in
27% Processor Sharing Systems"
28%
29% Copyright (c) 2012-2026, Imperial College London
30% All rights reserved.
31% This code is released under the 3-Clause BSD License.
32
33M = sn.nstations;
34K = sn.nclasses;
35N = sn.nclosedjobs;
36Kc = K + 1;
37
38Lambda = sn.mu;
39Pi = sn.phi;
40PH = sn.proc;
41rt = sn.rt;
42S = sn.nservers;
43
44% Find reference station (PS queue) and set inf servers to population
45refIdx = 0;
46for i = 1:M
47 if sn.sched(i) ~= SchedStrategy.INF
48 refIdx = i;
49 end
50 if isinf(S(i))
51 S(i) = N;
52 end
53end
54
55%% Build augmented arrays at struct level (following solver_fluid_passage_time)
56new_mu = cell(M, 1);
57new_pi = cell(M, 1);
58new_proc = PH;
59for j = 1:M
60 new_mu{j} = cell(1, Kc);
61 new_pi{j} = cell(1, Kc);
62 for k = 1:K
63 new_mu{j}{k} = Lambda{j}{k};
64 new_pi{j}{k} = Pi{j}{k};
65 end
66 % Tagged class: copy from taggedClass
67 new_mu{j}{Kc} = Lambda{j}{taggedClass};
68 new_pi{j}{Kc} = Pi{j}{taggedClass};
69 new_proc{j}{Kc} = PH{j}{taggedClass};
70end
71
72% Handle NaN/disabled entries
73for j = 1:M
74 for c = 1:Kc
75 if isscalar(new_mu{j}{c}) && isnan(new_mu{j}{c})
76 new_mu{j}{c} = [];
77 new_pi{j}{c} = [];
78 end
79 end
80end
81
82%% Expand routing table from M*K to M*Kc
83new_rt = zeros(M * Kc, M * Kc);
84
85% Copy original routing among base classes
86for l = 1:K
87 for m = 1:K
88 new_rt(l:Kc:end, m:Kc:end) = rt(l:K:end, m:K:end);
89 end
90end
91
92% Tagged class routes like taggedClass at all stations
93new_rt(Kc:Kc:end, Kc:Kc:end) = rt(taggedClass:K:end, taggedClass:K:end);
94
95% Absorption at refIdx: tagged class switches back to original classes
96chainIdx = find(sn.chains(:, taggedClass) == 1, 1);
97idxClassesInChain = find(sn.chains(chainIdx, :) == 1);
98for l = idxClassesInChain
99 for j = 1:M
100 new_rt((refIdx-1)*Kc + Kc, (j-1)*Kc + l) = rt((refIdx-1)*K + taggedClass, (j-1)*K + l);
101 end
102end
103% Zero out tagged->tagged transitions at refIdx
104for j = 1:M
105 new_rt((refIdx-1)*Kc + Kc, (j-1)*Kc + Kc) = 0;
106end
107
108%% Build ODE using solver_fluid_odes
109ode_opts = struct('method', 'default', 'config', struct('hide_immediate', false));
110[ode_h, q_indices] = solver_fluid_odes(sn, N, new_mu', new_pi', new_proc, new_rt, S, sn.sched, sn.schedparam, ode_opts);
111
112% Compute augmented phases
113augPhases = zeros(M, Kc);
114for i = 1:M
115 for c = 1:Kc
116 if ~isempty(new_mu{i}{c})
117 augPhases(i, c) = length(new_mu{i}{c});
118 end
119 end
120end
121
122%% If y0_levels and Rsampled provided, solve ODE and compute likelihood
123LIKE = NaN;
124if nargin >= 4
125 newFluid = 1;
126
127 % Build initial condition from fluid levels
128 totalPhases = sum(augPhases(:));
129 y0 = zeros(1, totalPhases);
130
131 % Set fluid levels for original classes
132 for i = 1:M
133 for k = 1:K
134 y0(q_indices(i, k)) = y0_levels(i, k);
135 end
136 end
137
138 % Move fluid from taggedClass to Tagged at refNode
139 y0(q_indices(refIdx, taggedClass)) = y0(q_indices(refIdx, taggedClass)) - newFluid;
140 y0(q_indices(refIdx, Kc)) = newFluid;
141
142 % Solve ODE from 0 to Rsampled
143 refTagIdx = q_indices(refIdx, Kc);
144 opt = odeset('AbsTol', 1e-8, 'RelTol', 1e-5, 'NonNegative', 1:length(y0), ...
145 'Events', @(t,y) ode_events(t, y, refTagIdx));
146 [t, yt] = ode15s(ode_h, [0 Rsampled], y0, opt);
147
148 % Extract likelihood from terminal state derivative
149 if Rsampled <= t(end)
150 lastState = yt(end,:);
151 lastRates = ode_h(t(end), lastState');
152 LIKE = -lastRates(refTagIdx) / newFluid;
153 else
154 LIKE = 0;
155 end
156end
157
158end
159
160function [value, isterminal, direction] = ode_events(~, y, idx)
161 value = y(idx);
162 isterminal = 1;
163 direction = 0;
164end