1%{ @file dtmc_stochcomp.m
2 % @brief Computes the stochastic complement of a DTMC partition
4 % @author LINE Development Team
8 % @brief Computes the stochastic complement of a DTMC partition
11 % Performs stochastic complementation on a partitioned DTMC.
15 % S = dtmc_stochcomp(
P)
16 % [S, P11, P12, P21, P22] = dtmc_stochcomp(
P, I)
21 % <tr><th>Name<th>Description
22 % <tr><td>
P<td>Stochastic transition matrix
23 % <tr><td>I<td>(Optional) Vector of indices representing the subset of states to retain
28 % <tr><th>Name<th>Description
29 % <tr><td>S<td>Stochastic complement matrix
for the subset I
30 % <tr><td>P11<td>Submatrix corresponding to transition within I
31 % <tr><td>P12<td>Submatrix corresponding to transitions from I to complement of I
32 % <tr><td>P21<td>Submatrix corresponding to transitions from complement of I to I
33 % <tr><td>P22<td>Submatrix corresponding to transitions within complement of I
36function [S,P11,P12,P21,P22]=dtmc_stochcomp(
P,I)
38 I=1:ceil(length(
P)/2);
41Ic = Iall(~ismember(Iall,I)); % slightly faster than setdiff
42sparsity = nnz(
P)/prod(size(
P));
49 S2 = sparse(eye(size(P22))-P22);
50 S=P11+P12*local_solve(S2,P21);
57 S2 = eye(size(P22))-P22;
58 S=P11+P12*local_solve(S2,P21);
62function T = local_solve(S2,P21)
63% Iterative path. The backslash below factorizes the whole complement block,
64% so its fill-in
is what limits the model above the dispatch threshold rather
65% than the arithmetic. One ILUT factorization serves every
column of P21. The
66% direct solve stays the default and remains the fallback.
67GMRES_MIN_STATES = 6000;
69if size(S2,1) > GMRES_MIN_STATES
70 [T,gflag] = ctmc_gmres_multi(sparse(S2), P21);
77 if ~all(isfinite(T(:)))
78 % Backslash returns NaN on a rank-deficient complement, which then
79 % contaminates every downstream metric. Fall back to the minimum-norm
80 % least-squares solution, as the Java and Python kernels do.
81 T = lsqminnorm(full(S2), full(P21));