1function [x,flag,relres,iter] = ctmc_gmres(A,b,tol,restart,maxit,x0)
2% [X,FLAG,RELRES,ITER]=CTMC_GMRES(A,B,TOL,RESTART,MAXIT,X0)
4% Solve
the sparse nonsymmetric linear system A*x=b by restarted GMRES with an
5% ILUT right preconditioner, falling back to a Jacobi preconditioner when
the
6% incomplete factorization breaks down. This
is the iterative counterpart of
the
7% direct sparse solve used by CTMC_SOLVE, intended
for generators whose LU
8% fill-in exceeds available memory.
10% Two preparation steps are not optional on a generator. Rows are equilibrated
11% to unit max norm, so
the O(1) normalization row does not mix with rows
12% carrying rates of a different magnitude. The states are then reordered by
13% reverse Cuthill-McKee: in
the natural ordering of a birth-death chain
the
14% unpivoted elimination has growth
factor (mu/lambda)^n, which overflows by a
15% few thousand states, and a fill-reducing ordering rather than pivoting
is what
18% A
is the already-assembled coefficient matrix; no CTMC-specific processing
is
19% performed here, so
the same kernel serves
the stochastic complementation and
22% Defaults: TOL=1e-12, RESTART=min(n,50), MAXIT=ceil(n/RESTART), X0=ones(n,1)/n.
23% TOL
is a linear-solve residual and
is therefore much tighter than
the
24% fixed-point tolerance options.iter_tol.
26% FLAG follows
the MATLAB GMRES convention: 0 converged, 1 iteration limit
27% reached, 2 preconditioner ill-conditioned, 3 stagnation. Callers must check
28% it and fall back to
the direct solve when it
is nonzero.
30% Copyright (c) 2012-2026, Imperial College London
33% Relative threshold below which ILUT discards a fill-in entry.
37if nargin<3 || isempty(tol) || tol<=0
40if nargin<4 || isempty(restart) || restart<=0
43restart = min(restart,n);
44if nargin<5 || isempty(maxit) || maxit<=0
45 maxit = ceil(n/restart);
47maxit = max(1,min(maxit,n));
48if nargin<6 || isempty(x0)
58% Row equilibration. Row scaling leaves
the solution unchanged.
59rownorm = full(max(abs(A),[],2));
60rownorm(rownorm==0) = 1;
61A = spdiags(1./rownorm,0,n,n)*A;
64% Reverse Cuthill-McKee on
the symmetrized pattern.
65p = symrcm(spones(A)+spones(A)');
73 [L,U] = ilu(A,struct('type','ilutp','droptol',ILUT_DROP_TOL,'udiag',1));
74 if any(~isfinite(nonzeros(L))) || any(~isfinite(nonzeros(U)))
84 % Jacobi fallback. A zero diagonal entry would make
the preconditioner
85 % singular, so those rows are left unscaled rather than inverted.
88 L = spdiags(1./d,0,n,n);
92warnstate = warning('off','MATLAB:gmres:tooSmallTolerance');
94 [xp,flag,relres,iterpair] = gmres(A,b,restart,tol,maxit,L,U,x0);
96 % A breakdown inside GMRES
is reported as non-convergence rather than
97 % propagated, so
the caller falls back to
the direct solve.
111% GMRES returns ITER as [outer,inner]; report
the total inner iteration
count so
112% that
the three codebases agree on a single scalar.
114 iter = max(0,(iterpair(1)-1)*restart + iterpair(2));