1function [pi,kmax]=ctmc_uniformization(pi0,Q,t,tol,maxiter)
2% [PI,KMAX]=CTMC_UNIFORMIZATION(PI0,Q,T,TOL,MAXITER)
4% MAXITER caps
the Poisson series truncation depth; pass a nonpositive
5% value (or omit it) to size it adaptively as max(100, q*t+10*sqrt(q*t)+20).
7% Copyright (c) 2012-2026, Imperial College London
9if nargin<4%~exist(
'tol',
'var')
12if nargin<5%~exist('maxiter','var')
15q=1.1*max(abs(diag(Q)));
16% Split
the horizon so exp(-q*t) never underflows within a segment
17% (exp(-745)==0 in
double precision): exp(Q*t)=(exp(Q*t/nSeg))^nSeg
20 nSeg = ceil(q*t/MAXQT);
24 [pi,kmax] = ctmc_uniformization(pi,Q,tSeg,tol,maxiter);
29 % The Poisson(q*t) mass concentrates around q*t with spread O(sqrt(q*t));
30 % a fixed cap silently truncates
the series for large horizons
31 maxiter = max(100, ceil(q*t+10*sqrt(q*t)+20));
33Qs=speye(size(Q))+sparse(Q)/q;
44 if (1-exp(-q*t)*s)<=tol
48 % Best-effort truncation depth if
the loop exhausts maxiter: summing k
49 % terms
is always more accurate than
the single term a stale kmax=1 gives