LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
ctmc_gmres.m
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)
3%
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.
9%
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
16% removes it.
17%
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
20% aggregation kernels.
21%
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.
25%
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.
29
30% Copyright (c) 2012-2026, Imperial College London
31% All rights reserved.
32
33% Relative threshold below which ILUT discards a fill-in entry.
34ILUT_DROP_TOL = 1e-4;
35
36n = size(A,1);
37if nargin<3 || isempty(tol) || tol<=0
38 tol = 1e-12;
39end
40if nargin<4 || isempty(restart) || restart<=0
41 restart = min(n,50);
42end
43restart = min(restart,n);
44if nargin<5 || isempty(maxit) || maxit<=0
45 maxit = ceil(n/restart);
46end
47maxit = max(1,min(maxit,n));
48if nargin<6 || isempty(x0)
49 x0 = ones(n,1)/n;
50end
51x0 = reshape(x0,n,1);
52b = reshape(b,n,1);
53
54if ~issparse(A)
55 A = sparse(A);
56end
57
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;
62b = b./rownorm;
63
64% Reverse Cuthill-McKee on the symmetrized pattern.
65p = symrcm(spones(A)+spones(A)');
66A = A(p,p);
67b = b(p);
68x0 = x0(p);
69
70L = [];
71U = [];
72try
73 [L,U] = ilu(A,struct('type','ilutp','droptol',ILUT_DROP_TOL,'udiag',1));
74 if any(~isfinite(nonzeros(L))) || any(~isfinite(nonzeros(U)))
75 L = [];
76 U = [];
77 end
78catch
79 L = [];
80 U = [];
81end
82
83if isempty(L)
84 % Jacobi fallback. A zero diagonal entry would make the preconditioner
85 % singular, so those rows are left unscaled rather than inverted.
86 d = full(diag(A));
87 d(d==0) = 1;
88 L = spdiags(1./d,0,n,n);
89 U = speye(n);
90end
91
92warnstate = warning('off','MATLAB:gmres:tooSmallTolerance');
93try
94 [xp,flag,relres,iterpair] = gmres(A,b,restart,tol,maxit,L,U,x0);
95catch
96 % A breakdown inside GMRES is reported as non-convergence rather than
97 % propagated, so the caller falls back to the direct solve.
98 warning(warnstate);
99 x = zeros(n,1);
100 x(p) = x0;
101 flag = 3;
102 relres = Inf;
103 iter = 0;
104 return
105end
106warning(warnstate);
107
108x = zeros(n,1);
109x(p) = xp;
110
111% GMRES returns ITER as [outer,inner]; report the total inner iteration count so
112% that the three codebases agree on a single scalar.
113if numel(iterpair)>1
114 iter = max(0,(iterpair(1)-1)*restart + iterpair(2));
115else
116 iter = iterpair;
117end
118end
Definition Station.m:245