LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
me_sample.m
1function sample = me_sample(ME, n, xs)
2% SAMPLE = ME_SAMPLE(ME, N, XS) - Generate random samples from ME distribution
3%
4% Generate N independent samples from a Matrix Exponential (ME) distribution
5% by numerical inversion of its exact CDF.
6%
7% Input:
8% ME: ME distribution as either:
9% - ME object
10% - Process cell array {D0, D1}
11% N: Number of samples to generate (default: 1)
12% XS: Optional pre-computed grid for CDF evaluation. When supplied, the
13% grid is used as given and no horizon extension is performed, so the
14% caller is responsible for covering the tail.
15%
16% Output:
17% SAMPLE: Column vector of N samples from the ME distribution
18%
19% Algorithm:
20% Inversion of F(t) = 1 - alpha*expm(A*t)*e, which is valid for every ME
21% representation. The CTMC walk of map_sample is not applicable here: it
22% presumes a phase-type reading of the representation, which fails when
23% alpha has negative entries or A has negative off-diagonal entries.
24% 1. The horizon is doubled until the survival function falls below
25% TAILTOL, so the tabulated range covers all but a negligible mass.
26% A fixed horizon of mean + 10*sigma is not enough for a heavy tail:
27% on an MMPP with SCV 4.21 it leaves 5e-04 of the mass untabulated.
28% 2. The CDF is tabulated on that horizon and forced nondecreasing to
29% absorb roundoff.
30% 3. Each variate is located by binary search, then polished by Newton
31% steps on the exact CDF and density, so the result is not limited by
32% the linear interpolation of the table.
33% 4. Variates beyond the last tabulated CDF value are placed by
34% exponential extrapolation using the dominant (least negative)
35% eigenvalue of D0, which governs the decay of the tail. Extrapolating
36% with a unit rate instead, as this function previously did, inflates
37% the tail: the same MMPP then samples an SCV of 5.27 against an exact
38% 4.21. Clamping to the grid endpoint truncates it and biases the mean
39% the other way.
40%
41% Note:
42% Passing a MAP or a RAP samples its stationary marginal independently,
43% which is a deliberate renewal approximation: the autocorrelation is
44% discarded. Use rap_sample to retain it.
45%
46% Examples:
47% me = ME([0.3, 0.7], [-2, 1; 0.5, -1.5]);
48% samples = me_sample(me, 10000);
49%
50% % Or use process representation directly
51% ME_proc = {[-2, 1; 0.5, -1.5], [0.4, 0.6; 0.3, 0.7]};
52% samples = me_sample(ME_proc, 10000);
53%
54% Copyright (c) 2012-2026, Imperial College London
55% All rights reserved.
56
57% Handle input arguments
58if nargin < 2
59 n = 1;
60end
61
62% Extract process representation
63if isa(ME, 'ME')
64 ME_proc = ME.getProcess();
65elseif iscell(ME)
66 ME_proc = ME;
67else
68 error('ME must be either an ME object or a cell array {D0, D1}');
69end
70
71TAILTOL = 1e-12; % survival mass left beyond the horizon
72MAXDBL = 40; % cap on horizon doublings
73GRIDPTS = 1000;
74NEWTON = 3;
75
76% Auto-generate grid if not provided
77if nargin < 3 || isempty(xs)
78 mean_val = map_mean(ME_proc);
79 var_val = map_var(ME_proc);
80 std_val = sqrt(max(var_val, 0));
81
82 horizon = mean_val + 10*std_val;
83 if ~isfinite(horizon) || horizon <= 0
84 horizon = 1;
85 end
86 % Extend until the tabulated range covers all but TAILTOL of the mass.
87 for k = 1:MAXDBL
88 if (1 - map_cdf(ME_proc, horizon)) < TAILTOL
89 break;
90 end
91 horizon = 2*horizon;
92 end
93
94 xs = linspace(0, horizon, GRIDPTS);
95end
96
97% Compute CDF at grid points
98Fxs = map_cdf(ME_proc, xs);
99Fxs = Fxs(:)';
100xs = xs(:)';
101
102% Force the tabulated CDF to be nondecreasing (roundoff can break monotonicity
103% near the tail, where consecutive values differ by less than eps).
104for i = 2:length(Fxs)
105 if Fxs(i) < Fxs(i-1)
106 Fxs(i) = Fxs(i-1);
107 end
108end
109
110% Dominant eigenvalue of D0 sets the decay rate of the tail.
111eta = max(real(eig(full(ME_proc{1}))));
112if ~(eta < 0) || ~isfinite(eta)
113 if isfinite(mean_val) && mean_val > 0
114 eta = -1/mean_val;
115 else
116 eta = -1;
117 end
118end
119
120xEnd = xs(end);
121sEnd = max(1 - Fxs(end), 0);
122
123sample = zeros(n, 1);
124for i = 1:n
125 u = rand();
126
127 if u <= Fxs(1)
128 sample(i) = xs(1);
129 continue;
130 end
131 if u >= Fxs(end)
132 % Exponential tail: S(x) ~ S(xEnd)*exp(eta*(x-xEnd)).
133 tailProb = 1 - u;
134 if sEnd <= 0 || tailProb <= 0
135 sample(i) = xEnd;
136 else
137 sample(i) = xEnd + log(sEnd/tailProb)/(-eta);
138 end
139 continue;
140 end
141
142 % Binary search for the bracketing interval.
143 lo = 1;
144 hi = length(Fxs);
145 while hi - lo > 1
146 mid = floor((lo + hi)/2);
147 if Fxs(mid) <= u
148 lo = mid;
149 else
150 hi = mid;
151 end
152 end
153
154 den = Fxs(lo+1) - Fxs(lo);
155 if den > 0
156 x = xs(lo) + (u - Fxs(lo))/den*(xs(lo+1) - xs(lo));
157 else
158 x = xs(lo);
159 end
160
161 % Newton polish on the exact CDF and density, kept inside the bracket.
162 for k = 1:NEWTON
163 fx = map_pdf(ME_proc, x);
164 fx = fx(1);
165 if ~(fx > 0)
166 break;
167 end
168 err = map_cdf(ME_proc, x) - u;
169 if abs(err) < 1e-14
170 break;
171 end
172 xn = x - err/fx;
173 if xn <= xs(lo) || xn >= xs(lo+1)
174 break;
175 end
176 if abs(xn - x) <= 1e-14*max(1, abs(x))
177 x = xn;
178 break;
179 end
180 x = xn;
181 end
182
183 sample(i) = x;
184end
185
186end
Definition Station.m:245