LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
ctmc_solve.m
1%{ @file ctmc_solve.m
2 % @brief Equilibrium distribution of the continuous-time Markov chain
3 %
4 % @author LINE Development Team
5%}
6
7%{
8 % @brief Equilibrium distribution of the continuous-time Markov chain
9 %
10 % @details
11 % Calculates the equilibrium distribution of a continuous-time Markov chain given its infinitesimal generator matrix.
12 %
13 % @par Syntax:
14 % @code
15 % p = ctmc_solve(Q)
16 % [p, Q, nConnComp, connComp] = ctmc_solve(Q, options)
17 % @endcode
18 %
19 % @par Parameters:
20 % <table>
21 % <tr><th>Name<th>Description
22 % <tr><td>Q<td>Infinitesimal generator matrix of the continuous-time Markov chain
23 % <tr><td>options<td>(Optional) Solver options (method: 'gpu' or default, force: boolean, verbose: 2 for debug)
24 % </table>
25 %
26 % @par Returns:
27 % <table>
28 % <tr><th>Name<th>Description
29 % <tr><td>p<td>Equilibrium distribution vector
30 % <tr><td>Q<td>Processed generator matrix (e.g., after removing spurious zeros)
31 % <tr><td>nConnComp<td>Number of connected components found (if reducible)
32 % <tr><td>connComp<td>Vector assigning each state to a connected component
33 % </table>
34 %
35 % @par Examples:
36 % @code
37 % Q = [-0.5, 0.5; 0.2, -0.2];
38 % p = ctmc_solve(Q);
39 % @endcode
40%}
41function [p, Q, nConnComp, connComp]=ctmc_solve(Q,options)
42
43if length(Q) > 6000 && (nargin==1 || ~options.force)
44 % do not switch to line_printf to force printout
45 fprintf(1,'The order of Q is greater than 6000, i.e., %d elements. Press key to continue.\n',length(Q));
46 pause;
47end
48
49if size(Q)==1
50 p = 1;
51 nConnComp = 1;
52 connComp = 1:length(Q);
53 return
54end
55
56Q = ctmc_makeinfgen(Q); % so that spurious diagonal elements are set to 0
57n = length(Q);
58
59if issym(Q)
60 symvariables = symvar(Q); % find all symbolic variables
61 B = double(subs(Q+Q',symvariables,ones(size(symvariables)))); % replace all symbolic variables with 1.0
62else
63 B = abs(Q+Q')>0;
64end
65[nConnComp, connComp] = weaklyconncomp(B);
66if nConnComp > 1
67 % reducible generator - solve each component recursively
68 line_warning(mfilename,'Reducible generator. No initial vector available, decomposing and solving each component recursively.\n');
69 if issym(Q)
70 p = sym(zeros(1,n));
71 else
72 p = zeros(1,n);
73 end
74
75 for c=1:nConnComp
76 Qc = Q(connComp==c,connComp==c);
77 Qc = ctmc_makeinfgen(Qc);
78 p(connComp==c) = ctmc_solve(Qc);
79 end
80 p = p /sum(p);
81 return
82end
83
84if all(Q==0)
85 p = ones(1,n)/n;
86 return
87end
88p = zeros(1,n);
89b = zeros(n,1);
90
91nnzel = 1:n;
92Qnnz = Q; bnnz = b;
93Qnnz_1 = Qnnz; bnnz_1 = bnnz;
94
95isReducible = false;
96goon = true;
97while goon
98 nnzel = find(sum(abs(Qnnz),1)~=0 & sum(abs(Qnnz),2)'~=0);
99 if length(nnzel) < n && ~isReducible
100 isReducible = true;
101 if (nargin > 1 && options.verbose == 2) % debug
102 line_warning(mfilename,'The infinitesimal generator is reducible.\n');
103 end
104 end
105 Qnnz = Qnnz(nnzel, nnzel);
106 bnnz = bnnz(nnzel);
107 Qnnz = ctmc_makeinfgen(Qnnz);
108 if all(size(Qnnz_1(:)) == size(Qnnz(:))) && all(size(bnnz_1(:)) == size(bnnz(:)))
109 goon = false;
110 else
111 Qnnz_1 = Qnnz; bnnz_1 = bnnz; nnzel = 1:length(Qnnz);
112 end
113end
114
115if isempty(Qnnz)
116 p = ones(1,n)/n;
117 return
118end
119Qnnz_1 = Qnnz;
120Qnnz(:,end) = 1;
121bnnz_1 = Qnnz;
122bnnz(end) = 1;
123
124if ~isdeployed
125 if issym(Q)
126 p = sym(p);
127 end
128end
129
130warning('off','MATLAB:singularMatrix');
131if nargin == 1
132 p(nnzel)=Qnnz'\ bnnz;
133 if any(isnan(p))
134 % verify if this has become reducible
135 if issym(Qnnz)
136 symvariables = symvar(Qnnz); % find all symbolic variables
137 B = double(subs(Qnnz+Qnnz',symvariables,ones(size(symvariables)))); % replace all symbolic variables with 1.0
138 else
139 B = abs(Qnnz+Qnnz')>0;
140 end
141 [nConnComp, connComp] = weaklyconncomp(B);
142 if nConnComp > 1
143 % reducible generator - solve each component recursively
144 if issym(Qnnz)
145 p(nnzel) = sym(zeros(1,n));
146 else
147 p(nnzel) = zeros(1,n);
148 end
149
150 for c=1:nConnComp
151 Qc = Q(connComp==c,connComp==c);
152 Qc = ctmc_makeinfgen(Qc);
153 p(intersect(find(connComp==c),nnzel)) = ctmc_solve(Qc);
154 end
155 p = p /sum(p);
156 return
157 end
158 end
159else
160 switch options.method
161 case 'gpu'
162 try
163 gQnnz = gpuArray(Qnnz');
164 gbnnz = gpuArray(bnnz);
165 pGPU = gQnnz \ gbnnz;
166 gathered_pGPU = gather(pGPU);
167 p(nnzel) = gathered_pGPU; % transfer from GPU to local env
168 catch
169 warning('ctmc_solve: GPU either not available or execution failed. Switching to default method.');
170 p(nnzel) = Qnnz'\ bnnz;
171 end
172 otherwise
173 p(nnzel)=Qnnz'\ bnnz;
174 end
175end
176
177if issym(Q)
178 Q=simplify(Q);
179end
180warning('on','MATLAB:singularMatrix');
181end