Skip to content

Commit

Permalink
Add DigraphAbsorptionProbabilities attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
mtorpey committed Apr 11, 2023
1 parent 81a435d commit ec7cb5f
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 0 deletions.
38 changes: 38 additions & 0 deletions doc/attr.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,44 @@ gap> ArticulationPoints(D);
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphAbsorptionProbabilities">
<ManSection>
<Attr Name="DigraphAbsorptionProbabilities" Arg="digraph"/>
<Returns>A matrix of rational numbers.</Returns>
<Description>
A random walk of infinite length through a digraph may pass through several
different strongly connected components (SCCs). However, with probability 1
it must eventually reach an SCC which it can never leave, because the SCC
has no out-edges leading to other SCCs. We may say that such an SCC has
<E>absorbed</E> the random walk, and we can calculate the probability of
each SCC absorbing a walk starting at a given vertex.
<P/>

<C>DigraphAbsorptionProbabilities</C> returns an <M>m \times n</M> matrix
<C>mat</C>, where <M>m</M> is the number of vertices in <C>digraph</C> and
<M>n</M> is the number of strongly connected components. Each entry
<C>mat[i][j]</C> is a rational number representing the probability that an
unbounded random walk starting at vertex <C>i</C> will be absorbed by
strongly connected component <C>j</C> – that is, the probability that it
will reach the component and never leave.
<P/>

Strongly connected components are indexed in the order given by
<Ref Attr="DigraphStronglyConnectedComponents"/>. See also
<Ref Oper="DigraphRandomWalk"/>.
<P/>
<Example><![CDATA[
gap> gr := Digraph([[2, 3, 4], [3], [2], []]);
<immutable digraph with 4 vertices, 5 edges>
gap> DigraphStronglyConnectedComponents(gr).comps;
[ [ 2, 3 ], [ 4 ], [ 1 ] ]
gap> DigraphAbsorptionProbabilities(gr);
[ [ 2/3, 1/3, 0 ], [ 1, 0, 0 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="StrongOrientation">
<ManSection>
<Oper Name="StrongOrientation" Arg="D"/>
Expand Down
1 change: 1 addition & 0 deletions doc/z-chap4.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<#Include Label="DigraphPath">
<#Include Label="DigraphShortestPath">
<#Include Label="DigraphRandomWalk">
<#Include Label="DigraphAbsorptionProbabilities">
<#Include Label="Dominators">
<#Include Label="DominatorTree">
<#Include Label="IteratorOfPaths">
Expand Down
1 change: 1 addition & 0 deletions gap/attr.gd
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ DeclareAttribute("DigraphOddGirth", IsDigraph);
DeclareAttribute("DigraphUndirectedGirth", IsDigraph);
DeclareAttribute("ArticulationPoints", IsDigraph);
DeclareSynonymAttr("CutVertices", ArticulationPoints);
DeclareAttribute("DigraphAbsorptionProbabilities", IsDigraph);

DeclareAttribute("DigraphAllSimpleCircuits", IsDigraph);
DeclareAttribute("DigraphLongestSimpleCircuit", IsDigraph);
Expand Down
90 changes: 90 additions & 0 deletions gap/attr.gi
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,96 @@ function(I, subtracted_set, size_bound)
end
);

InstallMethod(DigraphAbsorptionProbabilities,
"for a digraph",
[IsDigraph],
function(D)
local scc, is_sink_comp, transient_vertices, i, comp, v, sink_comps,
nr_transients, transient_mat, absorption_mat, neighbours, chance, w,
w_comp, sink_comp_index, j, N, chances_of_absorption, output, comp_no,
c;
# No vertices: trivial matrix
if DigraphNrVertices(D) = 0 then
return [];
fi;

scc := DigraphStronglyConnectedComponents(D);

# One SCC only: all vertices stay in it with probability 1.
if Length(scc.comps) = 1 then
return ListWithIdenticalEntries(DigraphNrVertices(D), [1]);
fi;

# Find the "sink components" (components from which there is no escape)
is_sink_comp := ListWithIdenticalEntries(Length(scc.comps), true);
transient_vertices := [];
for i in [1 .. Length(scc.comps)] do
comp := scc.comps[i];
for v in comp do
if ForAny(OutNeighboursOfVertex(D, v), w -> not w in comp) then
is_sink_comp[i] := false;
Append(transient_vertices, comp);
break;
fi;
od;
od;
sink_comps := Positions(is_sink_comp, true);
nr_transients := Length(transient_vertices);

# transient_mat[i][j] is the chance of going
# from transient vertex i to transient vertex j
transient_mat := NullMat(nr_transients, nr_transients);

# absorption_mat[i][j] is the chance of going
# from transient vertex i to sink component j
absorption_mat := NullMat(nr_transients, Length(sink_comps));

for i in [1 .. Length(transient_vertices)] do
v := transient_vertices[i];
neighbours := OutNeighboursOfVertex(D, v);
chance := 1 / Length(neighbours); # chance of following each edge
for w in neighbours do
w_comp := scc.id[w];
if is_sink_comp[w_comp] then
# w is in a sink component
sink_comp_index := Position(sink_comps, w_comp);
Assert(1, w in scc.comps[sink_comps[sink_comp_index]]);
absorption_mat[i][sink_comp_index] :=
absorption_mat[i][sink_comp_index] + chance;
else
# w is a transient vertex
j := Position(transient_vertices, w);
transient_mat[i][j] := transient_mat[i][j] + chance;
fi;
od;
od;

# Compute the chances as t tends to infinity using formula (I - Q)^-1 * R
N := Inverse(IdentityMat(nr_transients) - transient_mat);
chances_of_absorption := N * absorption_mat;

# Rows are vertices, columns are SCCs
output := NullMat(DigraphNrVertices(D), Length(scc.comps));

# Non-transient vertices stay in their own SCC with probability 1
for comp_no in sink_comps do
for v in scc.comps[comp_no] do
output[v][comp_no] := 1;
od;
od;

# Transient vertices have chances as calculated above
for i in [1 .. Length(transient_vertices)] do
v := transient_vertices[i];
for j in [1 .. Length(sink_comps)] do
c := sink_comps[j];
output[v][c] := chances_of_absorption[i][j];
od;
od;

return output;
end);

BindGlobal("DIGRAPHS_ChromaticNumberLawler",
function(D)
local n, vertices, subset_colours, s, S, i, I, subset_iter, x,
Expand Down
45 changes: 45 additions & 0 deletions tst/standard/attr.tst
Original file line number Diff line number Diff line change
Expand Up @@ -2876,6 +2876,48 @@ true
gap> B[14, 15] = z;
true

# DigraphAbsorptionProbabilities
gap> gr := Digraph([[2, 3, 4], [3], [2], []]);
<immutable digraph with 4 vertices, 5 edges>
gap> DigraphStronglyConnectedComponents(gr).comps; # this ordering is used
[ [ 2, 3 ], [ 4 ], [ 1 ] ]
gap> DigraphAbsorptionProbabilities(gr);
[ [ 2/3, 1/3, 0 ], [ 1, 0, 0 ], [ 1, 0, 0 ], [ 0, 1, 0 ] ]
gap> soccer := Digraph([[7, 2, 3, 5, 1, 4], # Motivating example:
> [7, 2, 3, 5, 5, 4], # game of 'Soccer Dice'
> [7, 5, 5, 5, 5, 1],
> [7, 7, 2, 5, 5, 5],
> [6, 6, 7, 7, 7, 4],
> [], []]);;
gap> DigraphStronglyConnectedComponents(soccer).comps; # this ordering is used
[ [ 7 ], [ 6 ], [ 1, 2, 3, 5, 4 ] ]
gap> DigraphAbsorptionProbabilities(soccer) =
> [[3473 / 4493, 1020 / 4493, 0],
> [3365 / 4493, 1128 / 4493, 0],
> [3211 / 4493, 1282 / 4493, 0],
> [3471 / 4493, 1022 / 4493, 0],
> [2825 / 4493, 1668 / 4493, 0],
> [0, 1, 0],
> [1, 0, 0]];
true
gap> DigraphAbsorptionProbabilities(EmptyDigraph(0));
[ ]
gap> DigraphAbsorptionProbabilities(EmptyDigraph(1));
[ [ 1 ] ]
gap> DigraphAbsorptionProbabilities(CompleteDigraph(5));
[ [ 1 ], [ 1 ], [ 1 ], [ 1 ], [ 1 ] ]
gap> DigraphAbsorptionProbabilities(ChainDigraph(4));
[ [ 0, 0, 0, 1 ], [ 0, 0, 0, 1 ], [ 0, 0, 0, 1 ], [ 0, 0, 0, 1 ] ]
gap> gr := ChainDigraph(250);;
gap> probs := DigraphAbsorptionProbabilities(gr);;
gap> scc := DigraphStronglyConnectedComponents(gr);;
gap> sink := DigraphSinks(gr)[1];;
gap> ForAll(probs, # all zeros except for the sink
> v -> ForAll([1 .. 250],
> comp -> v[comp] = 0
> or (v[comp] = 1 and scc.id[comp] = sink)));
true

# DIGRAPHS_UnbindVariables
gap> Unbind(adj);
gap> Unbind(adj1);
Expand All @@ -2894,12 +2936,15 @@ gap> Unbind(j);
gap> Unbind(mat);
gap> Unbind(multiple);
gap> Unbind(nbs);
gap> Unbind(probs);
gap> Unbind(r);
gap> Unbind(rd);
gap> Unbind(reflextrans);
gap> Unbind(reflextrans1);
gap> Unbind(reflextrans2);
gap> Unbind(scc);
gap> Unbind(sink);
gap> Unbind(soccer);
gap> Unbind(str);
gap> Unbind(topo);
gap> Unbind(trans);
Expand Down

0 comments on commit ec7cb5f

Please sign in to comment.