LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
ctmc_foxglynn.m
1function [pi,left,right,w]=ctmc_foxglynn(pi0,Q,t,tol,maxiter)
2% [PI,LEFT,RIGHT,W]=CTMC_FOXGLYNN(PI0,Q,T,TOL,MAXITER)
3%
4% Transient distribution of the CTMC by uniformization, with Poisson weights
5% and truncation points obtained by the Fox-Glynn algorithm, with Jansen's
6% correction to the right tail estimate. Neither exp(-q*t) nor (q*t)^k/k! is
7% ever formed, so the method is free of overflow and underflow and needs no
8% horizon splitting. MAXITER caps the right truncation point; pass a
9% nonpositive value (or omit it) to leave it uncapped.
10
11% Copyright (c) 2012-2026, Imperial College London
12% All rights reserved.
13if nargin<4%~exist('tol','var')
14 tol = 1e-12;
15end
16if nargin<5%~exist('maxiter','var')
17 maxiter = -1;
18end
19if tol<=0
20 tol = 1e-12;
21end
22q=1.1*max(abs(diag(Q)));
23lambda=q*t;
24if q<=0 || lambda<=0
25 pi = pi0;
26 left = 0;
27 right = 0;
28 w = 1;
29 return
30end
31[left,right,w] = ctmc_foxglynn_weights(lambda,tol,maxiter);
32Qs=speye(size(Q))+sparse(Q)/q;
33pi=zeros(size(pi0));
34P=pi0;
35for k=0:right
36 if k>=left
37 pi = pi + w(k-left+1)*P;
38 end
39 if k<right
40 P = P*Qs;
41 end
42end
43end
44
45function [left,right,w] = ctmc_foxglynn_weights(lambda,tol,maxiter)
46% Fox-Glynn truncation window and normalized Poisson weights for a
47% Poisson(lambda) mixing distribution at tail-mass tolerance TOL.
48left = ctmc_foxglynn_left(lambda,tol);
49right = ctmc_foxglynn_right(lambda,tol);
50if maxiter>0 && right>maxiter
51 right = maxiter;
52 left = min(left,right);
53end
54w = ctmc_foxglynn_poisson(lambda,left,right);
55end
56
57function e = ctmc_foxglynn_chernoff(lambda,k)
58% Chernoff exponent lambda*h(k/lambda) with h(u)=u*log(u)-u+1, so that exp(-e)
59% dominates P{X>=k} for k>lambda and P{X<=k} for k<lambda under
60% X~Poisson(lambda). It certifies the Fox-Glynn estimates below, which are
61% asymptotic and valid only for lambda>=25.
62if k<=0
63 e = lambda;
64else
65 e = lambda - k + k*log(k/lambda);
66end
67end
68
69function r = ctmc_foxglynn_right(lambda,tol)
70% Right truncation point R with P{X>R}<=tol/2. The starting guess is the
71% Fox-Glynn (1988) right tail estimate: with m=floor(lambda) and shift
72% s(k)=k*sqrt(2*lambda)+3/2, the tail P{X>=m+ceil(s(k))} is bounded by
73% a*d*exp(-k^2/2)/(k*sqrt(2*pi)) with a=(1+1/lambda)*exp(1/16)*sqrt(2).
74% Jansen's correction (2011), applied here as the factor
75% d=1/(1-exp(-(2/9)*s(k))), repairs the original statement, which drops this
76% factor for the k-dependent shift and is therefore optimistic at moderate
77% lambda; d tends to one as lambda grows, so the corrected bound agrees with
78% Fox and Glynn's asymptotically. The guess is then tightened and, if needed,
79% grown until the Chernoff bound holds, so R is certified in any regime.
80target = log(2/tol);
81m = floor(lambda);
82r = m;
83if lambda >= 25
84 a = (1+1/lambda)*exp(1/16)*sqrt(2);
85 spread = sqrt(2*lambda);
86 for k=1:64
87 shift = k*spread + 1.5;
88 d = 1/(1-exp(-(2/9)*shift));
89 bound = a*d*exp(-0.5*k^2)/(k*sqrt(2*pi));
90 if bound <= 0.5*tol
91 r = m + ceil(shift);
92 break
93 end
94 end
95end
96while r>m && ctmc_foxglynn_chernoff(lambda,r)>=target
97 r = r-1;
98end
99while ctmc_foxglynn_chernoff(lambda,r+1)<target
100 r = r+1;
101end
102end
103
104function l = ctmc_foxglynn_left(lambda,tol)
105% Left truncation point L with P{X<L}<=tol/2, zero when no truncation is
106% admissible. The starting guess is the Fox-Glynn (1988) left tail estimate
107% with b=(1+1/lambda)*exp(1/(8*lambda)) and shift k*sqrt(lambda)+3/2 below the
108% mode. Unlike the right tail this one needs no Jansen factor, the left tail of
109% a Poisson being lighter than its normal approximation.
110target = log(2/tol);
111m = floor(lambda);
112if ctmc_foxglynn_chernoff(lambda,0) < target
113 l = 0;
114 return
115end
116l = 0;
117if lambda >= 25
118 b = (1+1/lambda)*exp(1/(8*lambda));
119 spread = sqrt(lambda);
120 for k=1:64
121 bound = b*exp(-0.5*k^2)/(k*sqrt(2*pi));
122 if bound <= 0.5*tol
123 l = m - floor(k*spread + 1.5);
124 break
125 end
126 end
127 l = max(l,0);
128end
129while l>0 && ctmc_foxglynn_chernoff(lambda,l-1) < target
130 l = l-1;
131end
132while l<m && ctmc_foxglynn_chernoff(lambda,l) >= target
133 l = l+1;
134end
135end
136
137function w = ctmc_foxglynn_poisson(lambda,left,right)
138% Poisson(lambda) weights on [left,right], normalized to sum to one. Following
139% Fox-Glynn they are built by the two-sided recursion w(k-1)=w(k)*k/lambda and
140% w(k+1)=w(k)*lambda/(k+1) anchored at the mode, so neither exp(-lambda) nor
141% lambda^k/k! is ever evaluated and the overflow and underflow that limit the
142% direct series cannot occur. Anchoring at w(mode)=1 keeps the extreme weights
143% near tol, far above the denormal threshold, making Fox and Glynn's rescaling
144% of the mode weight unnecessary. The normalizing sum is accumulated in
145% increasing order of magnitude.
146len = right-left+1;
147w = zeros(1,len);
148m = min(max(floor(lambda),left),right);
149w(m-left+1) = 1;
150for k=m:-1:left+1
151 w(k-1-left+1) = w(k-left+1)*k/lambda;
152end
153for k=m:right-1
154 w(k+1-left+1) = w(k-left+1)*lambda/(k+1);
155end
156w = w/sum(sort(w));
157end
Definition Station.m:245