@@ -192,26 +192,16 @@ function(I, subtracted_set, size_bound)
192192end
193193);
194194
195- InstallMethod(DigraphAbsorptionProbabilities ,
195+ InstallMethod(DIGRAPHS_AbsorbingMarkovChain ,
196196" for a digraph" ,
197197[ IsDigraph] ,
198198function (D )
199+ # Helper for DigraphAbsorptionProbabilities and DigraphAbsorptionExpectedSteps
199200 local scc, is_sink_comp, transient_vertices, i, comp, v, sink_comps,
200201 nr_transients, transient_mat, absorption_mat, neighbours, chance, w,
201- w_comp, sink_comp_index, j, N, chances_of_absorption, output, comp_no,
202- c;
203- # No vertices: trivial matrix
204- if DigraphNrVertices(D) = 0 then
205- return [] ;
206- fi ;
207-
202+ w_comp, sink_comp_index, j, fundamental_mat;
208203 scc := DigraphStronglyConnectedComponents(D);
209204
210- # One SCC only: all vertices stay in it with probability 1.
211- if Length(scc.comps) = 1 then
212- return ListWithIdenticalEntries(DigraphNrVertices(D), [ 1 ] );
213- fi ;
214-
215205 # Find the "sink components" (components from which there is no escape)
216206 # We could use QuotientDigraph and DigraphSinks here, but this avoids copying.
217207 is_sink_comp := ListWithIdenticalEntries(Length(scc.comps), true );
@@ -229,6 +219,14 @@ function(D)
229219 sink_comps := Positions(is_sink_comp, true );
230220 nr_transients := Length(transient_vertices);
231221
222+ # If no transient vertices, then we can't return anything interesting.
223+ if nr_transients = 0 then
224+ return rec (transient_vertices := transient_vertices,
225+ fundamental_mat := [] ,
226+ sink_comps := sink_comps,
227+ absorption_mat := [] );
228+ fi ;
229+
232230 # transient_mat[i][j] is the chance of going
233231 # from transient vertex i to transient vertex j
234232 transient_mat := NullMat(nr_transients, nr_transients);
@@ -257,21 +255,57 @@ function(D)
257255 od ;
258256 od ;
259257
260- # Compute the chances as t tends to infinity using formula (I - Q)^-1 * R
261- N := Inverse(IdentityMat(nr_transients) - transient_mat);
262- chances_of_absorption := N * absorption_mat;
258+ # "Fundamental matrix": mat[i][j] is the expected number of visits to each
259+ # transient vertex j given a random walk starting at transient vertex i.
260+ # Compute this using formula (I - Q)^-1
261+ fundamental_mat := Inverse(IdentityMat(nr_transients) - transient_mat);
263262
264- # Rows are vertices, columns are SCCs
263+ # Return all info needed for further computation in DigraphAbsorption* methods
264+ return rec (transient_vertices := transient_vertices,
265+ fundamental_mat := fundamental_mat,
266+ sink_comps := sink_comps,
267+ absorption_mat := absorption_mat);
268+ end );
269+
270+ InstallMethod(DigraphAbsorptionProbabilities,
271+ " for a digraph" ,
272+ [ IsDigraph] ,
273+ function (D )
274+ local scc, markov, chances_of_absorption, output, sink_comps, comp_no, v,
275+ transient_vertices, i, j, c;
276+ # Strongly connected components are an important part of definition
277+ scc := DigraphStronglyConnectedComponents(D);
278+
279+ # One SCC only (or none): all vertices stay in it with probability 1.
280+ if Length(scc.comps) <= 1 then
281+ return ListWithIdenticalEntries(DigraphNrVertices(D), [ 1 ] );
282+ fi ;
283+
284+ # Get data about absorbing Markov chain represented by this digraph
285+ markov := DIGRAPHS_AbsorbingMarkovChain(D);
286+
287+ # Calculate chances of absorption
288+ # Rows are transient vertices, columns are absorbing SCCs
289+ if Length(markov.transient_vertices) = 0 then
290+ chances_of_absorption := [] ;
291+ else
292+ chances_of_absorption := markov.fundamental_mat * markov.absorption_mat;
293+ fi ;
294+
295+ # Convert to output format
296+ # Rows are all vertices, columns are all SCCs
265297 output := NullMat(DigraphNrVertices(D), Length(scc.comps));
266298
267299 # Non-transient vertices stay in their own SCC with probability 1
300+ sink_comps := markov.sink_comps;
268301 for comp_no in sink_comps do
269302 for v in scc.comps[ comp_no] do
270303 output[ v][ comp_no] := 1 ;
271304 od ;
272305 od ;
273306
274- # Transient vertices have chances as calculated above
307+ # Transient vertices have chances as calculated in Markov code
308+ transient_vertices := markov.transient_vertices;
275309 for i in [ 1 .. Length(transient_vertices)] do
276310 v := transient_vertices[ i] ;
277311 for j in [ 1 .. Length(sink_comps)] do
@@ -283,6 +317,28 @@ function(D)
283317 return output;
284318end );
285319
320+ InstallMethod(DigraphAbsorptionExpectedSteps,
321+ " for a digraph" ,
322+ [ IsDigraph] ,
323+ function (D )
324+ local markov, N, transient_expected_steps, out, i;
325+ if DigraphNrVertices(D) = 0 then
326+ return [] ;
327+ fi ;
328+ markov := DIGRAPHS_AbsorbingMarkovChain(D);
329+ N := markov.fundamental_mat;
330+ if Length(N) = 0 then # no transient vertices
331+ transient_expected_steps := [] ;
332+ else
333+ transient_expected_steps := N * ListWithIdenticalEntries(Length(N), 1 );
334+ fi ;
335+ out := ListWithIdenticalEntries(DigraphNrVertices(D), 0 );
336+ for i in [ 1 .. Length(transient_expected_steps)] do
337+ out[ markov.transient_vertices[ i]] := transient_expected_steps[ i] ;
338+ od ;
339+ return out;
340+ end );
341+
286342BindGlobal(" DIGRAPHS_ChromaticNumberLawler" ,
287343function (D )
288344 local n, vertices, subset_colours, s, S, i, I, subset_iter, x,
0 commit comments