1classdef Prior < Distribution
2 % Prior Discrete prior distribution over alternative distributions
4 % Prior represents parameter uncertainty by specifying a discrete set of
5 % alternative distributions with associated probabilities. When used with
6 % setService or setArrival, it causes the Posterior solver to expand the
7 % model into a family of networks, one
for each alternative.
9 % This
is NOT a mixture distribution - each alternative represents a
10 % separate model realization with its associated prior probability.
12 % @brief Discrete prior
for Bayesian-style parameter uncertainty modeling
14 % Key characteristics:
15 % - Discrete set of alternative distributions
16 % - Probability-weighted alternatives (must sum to 1)
17 % - Used with Posterior solver
for Bayesian analysis
18 % - Supports single Prior per model (current limitation)
22 % % Service time with uncertain rate
23 % prior = Prior({Exp(1.0), Exp(2.0), Erlang(2,1.5)}, [0.4, 0.35, 0.25]);
24 % queue.setService(
class, prior);
26 % % Solve with Posterior wrapper
27 % post = Posterior(model, @SolverMVA);
28 % avgTable = post.getAvgTable(); % Prior-weighted expectations
29 % postTable = post.getPosteriorTable(); % Per-alternative breakdown
32 % Copyright (c) 2012-2026, Imperial College London
33 % All rights reserved.
36 distributions; % Cell array of alternative distributions
37 probabilities; % Vector of prior probabilities (sum to 1)
41 function self = Prior(distributions, probabilities)
42 % PRIOR Create a prior distribution instance
44 % @brief Creates a Prior with specified alternatives and probabilities
45 % @param distributions Cell array of Distribution objects
46 % @param probabilities Vector of probabilities (must sum to 1)
47 % @
return self Prior instance
49 self@Distribution(
'Prior', 2, [0, Inf]);
51 % Validate distributions input
52 if ~iscell(distributions)
53 line_error(mfilename,
'distributions must be a cell array');
55 if isempty(distributions)
56 line_error(mfilename,
'distributions cannot be empty');
58 for i = 1:length(distributions)
59 if ~isa(distributions{i},
'Distribution')
60 line_error(mfilename, sprintf(
'Element %d is not a Distribution object', i));
64 % Validate probabilities input
65 if length(distributions) ~= length(probabilities)
66 line_error(mfilename,
'Number of distributions must match number of probabilities');
68 if abs(sum(probabilities) - 1) > GlobalConstants.CoarseTol
69 line_error(mfilename, sprintf(
'Probabilities must sum to 1 (current sum: %f)', sum(probabilities)));
71 if any(probabilities < 0)
72 line_error(mfilename, 'Probabilities must be non-negative');
75 self.distributions = distributions(:)'; % row cell array
76 self.probabilities = probabilities(:)'; % row vector
78 setParam(self, 1, 'distributions', distributions);
79 setParam(self, 2, 'probabilities', probabilities);
82 function n = getNumAlternatives(self)
83 % N = GETNUMALTERNATIVES()
84 % Return number of alternative distributions
85 n = length(self.distributions);
88 function dist = getAlternative(self, idx)
89 % DIST = GETALTERNATIVE(IDX)
90 % Return the distribution at index idx
91 if idx < 1 || idx > self.getNumAlternatives()
92 line_error(mfilename, 'Index out of bounds');
94 dist = self.distributions{idx};
97 function p = getProbability(self, idx)
98 %
P = GETPROBABILITY(IDX)
99 % Return the probability of alternative idx
100 if idx < 1 || idx > self.getNumAlternatives()
101 line_error(mfilename,
'Index out of bounds');
103 p = self.probabilities(idx);
106 function MEAN = getMean(self)
108 % Get prior-weighted mean (expected mean over alternatives)
110 % E[X] = sum_i p_i * E[X_i]
112 for i = 1:self.getNumAlternatives()
113 MEAN = MEAN + self.probabilities(i) * self.distributions{i}.getMean();
117 function SCV = getSCV(self)
119 % Get prior-weighted SCV using law of total variance
121 % Var(X) = E[Var(X|D)] + Var(E[X|D])
122 % SCV = Var(X) / E[X]^2
124 E_mean = 0; % E[E[X|D]]
125 E_var = 0; % E[Var(X|D)]
126 E_mean_sq = 0; % E[E[X|D]^2]
128 for i = 1:self.getNumAlternatives()
129 m = self.distributions{i}.getMean();
130 v = self.distributions{i}.getSCV() * m^2; % Var(X|D=i)
131 E_mean = E_mean + self.probabilities(i) * m;
132 E_var = E_var + self.probabilities(i) * v;
133 E_mean_sq = E_mean_sq + self.probabilities(i) * m^2;
136 % Total variance = E[Var(X|D)] + Var(E[X|D])
137 % Var(E[X|D]) = E[E[X|D]^2] - E[E[X|D]]^2
138 total_var = E_var + (E_mean_sq - E_mean^2);
139 SCV = total_var / E_mean^2;
142 function SKEW = getSkewness(self)
143 % SKEW = GETSKEWNESS()
144 % Get prior-weighted skewness (approximation using mixture formula)
146 % For mixture: use law of total cumulance (simplified)
147 % This is an approximation - exact formula is more complex
149 sigma2 = self.getVar();
150 sigma = sqrt(sigma2);
152 if sigma < GlobalConstants.FineTol
157 % E[(X - mu)^3] via mixture
159 for i = 1:self.getNumAlternatives()
160 mi = self.distributions{i}.getMean();
161 vi = self.distributions{i}.getVar();
163 skewi = self.distributions{i}.getSkewness();
165 % E[(Xi - mu)^3] = E[(Xi - mi + mi - mu)^3]
166 % Using binomial expansion
168 % Third central moment of Xi around its own mean
170 % Third central moment of Xi around global mu
171 m3_shifted = m3i + 3*vi*delta + delta^3;
173 third_central = third_central + self.probabilities(i) * m3_shifted;
176 SKEW = third_central / sigma^3;
179 function X = sample(self, n)
181 % Sample from prior (mixture sampling)
183 % Samples are drawn from the mixture distribution where each
184 % sample comes from one of the alternatives selected according
185 % to the prior probabilities.
193 cumprob = cumsum(self.probabilities);
195 % Select alternative based on probabilities
197 idx = find(r <= cumprob, 1,
'first');
198 X(i) = self.distributions{idx}.sample(1);
202 function Ft = evalCDF(self, t)
204 % Evaluate mixture CDF at t
206 % F(t) = sum_i p_i * F_i(t)
209 for i = 1:self.getNumAlternatives()
210 Ft = Ft + self.probabilities(i) * self.distributions{i}.evalCDF(t);
214 function L = evalLST(self, s)
216 % Evaluate mixture Laplace-Stieltjes transform
218 % L(s) = sum_i p_i * L_i(s)
221 for i = 1:self.getNumAlternatives()
222 L = L + self.probabilities(i) * self.distributions{i}.evalLST(s);
226 function
bool = isPrior(self)
228 % Return
true (used
for detection by Posterior solver)
234 function
bool = isPriorDistribution(dist)
235 % BOOL = ISPRIORDISTRIBUTION(DIST)
236 % Check
if a distribution
is a Prior
238 % @param dist Distribution
object to check
239 % @
return bool True
if dist
is a Prior
240 bool = isa(dist,
'Prior');