Skip to content

Commit

Permalink
Add edge-weighted attributes and operations
Browse files Browse the repository at this point in the history
  • Loading branch information
RaiyanC authored and mtorpey committed Feb 7, 2024
1 parent f42f693 commit 88cfb38
Show file tree
Hide file tree
Showing 10 changed files with 1,686 additions and 5 deletions.
8 changes: 8 additions & 0 deletions doc/display.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ gap> Splash(DotDigraph(RandomDigraph(4)));
<ManSection>
<Attr Name="DotDigraph" Arg="digraph"/>
<Oper Name="DotColoredDigraph" Arg="digraph, vert, edge"/>
<Oper Name="DotColoredEdgeWeightedDigraph" Arg="digraph, vert, edge, weight"/>
<Oper Name="DotVertexLabelledDigraph" Arg="digraph"/>
<Oper Name="DotVertexColoredDigraph" Arg="digraph, vert"/>
<Oper Name="DotEdgeColoredDigraph" Arg="digraph, edge"/>
Expand All @@ -124,6 +125,13 @@ gap> Splash(DotDigraph(RandomDigraph(4)));
are not the appropriate size, or have holes then the function will return
an error.<P/>

<C>DotColoredEdgeWeightedDigraph</C> differs from <C>DotColoredDigraph</C> only
in that the values given in the third list is used to label the weights of the edges of
the graph when displayed. The list for weight should be a list of equal length to the list
for vertex and edge colours.If the lists
are not the appropriate size, or have holes then the function will return
an error.<P/>

<C>DotVertexColoredDigraph</C> differs from <C>DotDigraph</C> only in
that the values in given in the list are used to color the vertices
of the graph when displayed. The list for vertex colours should be
Expand Down
222 changes: 221 additions & 1 deletion doc/weights.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,224 @@ gap> EdgeWeights(g);
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
<#/GAPDoc>

<#GAPDoc Label="DigraphEdgeWeightedMinimumSpanningTree">
<ManSection>
<Attr Name="DigraphEdgeWeightedMinimumSpanningTree" Arg="digraph"/>
<Returns>A record.</Returns>
<Description>
This function returns the record with 2 components <C>total</C> and <C>mst</C>.
The first component <C>total</C> represents the sum of the edge weights of the digraph that is returns.
The second component <C>mst</C> is the edge weighted digraph representation of the mst.
<P/>

This algorithm only works on connected undirected graphs. If it is given a disconnected digraph, it will error.
The function will internally convert <A>digraph</A> representation to an undirected representation.

See <Ref Attr="EdgeWeights" Func="EdgeWeightedDigraph"/>.
<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2],[1],[1,2]], [[12],[5],[6,9]]);
<immutable digraph with 3 vertices, 4 edges>
gap> DigraphEdgeWeightedMinimumSpanningTree(g);
rec( mst := <immutable digraph with 3 vertices, 2 edges>, total := 11
)]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphEdgeWeightedShortestPath">
<ManSection>
<Oper Name="DigraphEdgeWeightedShortestPath" Arg="digraph, start"/>
<Returns>A record.</Returns>
<Description>
This operation, given a edge weighted <A>digraph</A> and a <A>start</A> vertex will return a record
with 3 components. The first component is the distances which is a list of shortest distance
to each node from the <A>start</A> node. The distance from the start node to itself is always 0.
The second component is the edges, which signifies which edge was taken to get to that vertex from the parent of that node
which is the third component; a list of vertices which are the parents of that vertex. Using both these components
together, you can find the shortest edge weighted path to all other vertices from a starting vertex. In
In cases, where a path doesn't exist and therefore there are no distances, edges or parents, the lists will
contain a fail.
<P/>

This operation can handle negative edge weights BUT it will error if a negative cycle exists.
<P/>

See <Ref Attr="EdgeWeights" Func="EdgeWeightedDigraph"/>.
<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2,3],[4],[4],[]],[[5,1],[6],[11],[]]);
<immutable digraph with 4 vertices, 4 edges>
gap> DigraphEdgeWeightedShortestPath(g,1);
rec( distances := [ 0, 5, 1, 11 ], edges := [ fail, 1, 2, 1 ],
parents := [ fail, 1, 1, 2 ] )
gap> ncg := EdgeWeightedDigraph([[2],[3],[1]],[[-1],[-2],[-3]]);
<immutable digraph with 3 vertices, 3 edges>
gap> DigraphEdgeWeightedShortestPath(ncg,1);
Error, negative cycle exists,
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphEdgeWeightedShortestPaths">
<ManSection>
<Attr Name="DigraphEdgeWeightedShortestPaths" Arg="digraph"/>
<Returns>A list of lists of integers, floats or rationals.</Returns>
<Description>
Given an edge weighted <A>digraph</A>, this returns a list of lists
of the shortest distance from one vertex to every other vertex.
If no paths exist, then fail will be returned in the 2D list.
This will return an incorrect answer if negative cycles exists.

See <Ref Attr="EdgeWeights" Func="EdgeWeightedDigraph"/>.
<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2],[3],[1]],[[1],[2],[3]]);
<immutable digraph with 3 vertices, 3 edges>
gap> DigraphEdgeWeightedShortestPaths(g);
rec( distances := [ [ 0, 1, 3 ], [ 5, 0, 2 ], [ 3, 4, 0 ] ],
edges := [ [ fail, 1, 1 ], [ 1, fail, 1 ], [ 1, 1, fail ] ],
parents := [ [ fail, 1, 1 ], [ 2, fail, 2 ], [ 3, 3, fail ] ] )]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphMaximumFlow">
<ManSection>
<Attr Name="DigraphMaximumFlow" Arg="digraph, start, destination"/>
<Returns>A record.</Returns>
<Description>
Given an edge weighted <A>digraph</A>, this returns a record with 3 components.
The first component is the flow inbound into vertex v which is a list of lists.
If there are multiple edges, the algorithm will fill up the edges sequentially so
if there are 3 edges outbound from u to v with capacities, 5,10,15 and there is a flow of 15, it will fill the first two edges 5 and 10.
If there is a flow of 9, then the flow will contain a list with flows 5 and 4. <P/>

This can be coupled with the second component which is a list of list of the vertices that each flow comes from. Using this,
allows the path of the flow and the flow to be obtained using the first component. <P/>

The third and last component is the maximum flow value which is the highest flow that we can obtain from start to destination. <P/>

See <Ref Attr="EdgeWeights" Func="EdgeWeightedDigraph"/>.
<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2,2],[3],[]],[[3,2],[1],[]]);
<immutable multidigraph with 3 vertices, 3 edges>
gap> DigraphMaximumFlow(g, 1, 3);
rec( flows := [ [ ], [ 1, 0 ], [ 1 ] ], maxFlow := 1,
parents := [ [ ], [ 1, 1 ], [ 2 ] ] )]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="RandomUniqueEdgeWeightedDigraph">
<ManSection>
<Attr Name="RandomUniqueEdgeWeightedDigraph" Arg="[filt, ]n[,p]"/>
<Returns>An edge weighted digraph.</Returns>
<Description>
&STANDARD_FILT_TEXT;

As well as the filters implemented in <Ref Oper="RandomDigraph"/>, the following filters are implemented:
<Ref Filt="IsStronglyConnectedDigraph"/>.

For <Ref Filt="IsStronglyConnectedDigraph"/>, first a random connected tree is created which it self may have numerous
strongly connected components (scc) which are then them selves connected. For each sequential pair of strongly connected component
, a random u from the first scc and v from the second scc and given a directed edge from u to v. This is then repeated with an edge from a random vertex
in the second scc to the first scc.

If <A>n</A> is a positive integer, then this function returns a random edge weighted
digraph with <A>n</A> vertices, without multiple edges but with unique edge weights. The result
may or may not have loops. If using <Ref Filt='IsAcyclicDigraph'/>, the resulting graph
will not have any loops by definition.<P/>

If the optional second argument <A>p</A> is a float with value
<M>0 \leq </M> <A> p </A> <M> \leq 1</M>, then an edge will exist between each
pair of vertices with probability approximately <A>p</A>.
If <A>p</A> is not specified, then a random probability will be assumed
(chosen with uniform probability).<P/>
<Example><![CDATA[
gap> g := RandomUniqueEdgeWeightedDigraph(
> IsStronglyConnectedDigraph, 5, 1);
<immutable digraph with 5 vertices, 25 edges>
gap> g := RandomUniqueEdgeWeightedDigraph(5, 1);
<immutable digraph with 5 vertices, 25 edges>]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DigraphFromPaths">
<ManSection>
<Attr Name="DigraphFromPaths" Arg="digraph, record"/>
<Returns>An edge weighted digraph.</Returns>
<Description>
Given a <A>digraph</A> and a <A>record</A> of distances, edges and parents
this will compute the start vertex and will build a digraph of the shortest path from the start vertex
to all other vertices.

<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2],[3],[]],[[2],[1],[]]);
<immutable digraph with 3 vertices, 2 edges>
gap> sp := DigraphEdgeWeightedShortestPath(g, 1);
rec( distances := [ 0, 2, 3 ], edges := [ fail, 1, 1 ],
parents := [ fail, 1, 2 ] )
gap> sd := DigraphFromPaths(g, sp);
<immutable digraph with 3 vertices, 2 edges>]]></Example>
</Description>
</ManSection>
<#/GAPDoc>


<#GAPDoc Label="DigraphFromPath">
<ManSection>
<Attr Name="DigraphFromPath" Arg="digraph, record, dest"/>
<Returns>An edge weighted digraph.</Returns>
<Description>
Given a <A>digraph</A> and a <A>record</A> of distances, edges and parents
this will compute the start vertex and will build a digraph of the shortest path from the start vertex
to <A>dest</A> vertex.

<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2],[3],[]],[[2],[1],[]]);
<immutable digraph with 3 vertices, 2 edges>
gap> sp := DigraphEdgeWeightedShortestPath(g, 1);
rec( distances := [ 0, 2, 3 ], edges := [ fail, 1, 1 ],
parents := [ fail, 1, 2 ] )
gap> sd := DigraphFromPath(g, sp, 3);
<immutable digraph with 3 vertices, 2 edges>
gap> sd := DigraphFromPath(g, sp, 2);
<immutable digraph with 3 vertices, 1 edge>]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

<#GAPDoc Label="DotEdgeWeightedDigraph">
<ManSection>
<Attr Name="DotEdgeWeightedDigraph" Arg="digraph, subdigraph, record"/>
<Returns>A string.</Returns>
<Description>
Given an<A>digraph</A>, <A>subdigraph</A> and a <A>record</A> of a subdigraph within the original digraph,
using the record optional parameters, this will return a DOT of the subdigraph within the original digraph.<P/>

Optional parameters in the <A>record</A> include:
- highlightColour (default blue): the colour of the path of the subdigraph
- edgeColour (default black): the colour of the non subdigraph path
- vertColor (default lightpink): the colour of the vertices
- sourceColour (default green): the colour of a source vertex
- destColour (default red): the colour of a destination vertex

An empty record may be passed as a parameters, in which case the default values will be used.

<Example><![CDATA[
gap> g := EdgeWeightedDigraph([[2],[3],[]],[[2],[1],[]]);
<immutable digraph with 3 vertices, 2 edges>
gap> sp := DigraphEdgeWeightedShortestPath(g, 1);
rec( distances := [ 0, 2, 3 ], edges := [ fail, 1, 1 ],
parents := [ fail, 1, 2 ] )
gap> sd := DigraphFromPath(g, sp, 3);
<immutable digraph with 3 vertices, 2 edges>
gap> DotEdgeWeightedDigraph(g, sd, rec());
"//dot\ndigraph hgn{\nnode [shape=circle]\n1[color=lightpink, style=fi\
lled]\n2[color=lightpink, style=filled]\n3[color=lightpink, style=fill\
ed]\n1 -> 2[color=blue, label=2]\n2 -> 3[color=blue, label=1]\n}\n"]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
8 changes: 8 additions & 0 deletions doc/z-chap5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
<Section><Heading>Edge Weights</Heading>
<#Include Label="EdgeWeights">
<#Include Label="EdgeWeightedDigraph">
<#Include Label="DigraphEdgeWeightedMinimumSpanningTree">
<#Include Label="DigraphEdgeWeightedShortestPath">
<#Include Label="DigraphEdgeWeightedShortestPaths">
<#Include Label="DigraphMaximumFlow">
<#Include Label="RandomUniqueEdgeWeightedDigraph">
<#Include Label="DigraphFromPaths">
<#Include Label="DigraphFromPath">
<#Include Label="DotEdgeWeightedDigraph">
</Section>

<Section><Heading>Orders</Heading>
Expand Down
12 changes: 12 additions & 0 deletions gap/digraph.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,13 @@ InstallMethod(RandomDigraphCons, "for IsConnectedDigraph and an integer",
{_, n}
-> RandomDigraphCons(IsConnectedDigraph, n, Float(Random([0 .. n])) / n));

InstallMethod(RandomDigraphCons,
"for IsStronglyConnectedDigraph, an integer, and a rational",
[IsStronglyConnectedDigraph, IsInt],
function(filt, n)
return RandomDigraphCons(IsStronglyConnectedDigraph, n, Float(Random([0 .. n])) / n);

Check warning on line 1372 in gap/digraph.gi

View check run for this annotation

Codecov / codecov/patch

gap/digraph.gi#L1372

Added line #L1372 was not covered by tests
end);

InstallMethod(RandomDigraphCons, "for IsAcyclicDigraph and an integer",
[IsAcyclicDigraph, IsInt],
{_, n}
Expand Down Expand Up @@ -1405,6 +1412,11 @@ InstallMethod(RandomDigraphCons,
[IsStronglyConnectedDigraph, IsInt, IsRat],
{filt, n, p} -> RandomDigraphCons(IsStronglyConnectedDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsStronglyConnectedDigraph, an integer, and a rational",
[IsStronglyConnectedDigraph, IsInt, IsRat],
{filt, n, p} -> RandomDigraphCons(IsStronglyConnectedDigraph, n, Float(p)));

InstallMethod(RandomDigraphCons,
"for IsAcyclicDigraph, an integer, and a rational",
[IsAcyclicDigraph, IsInt, IsRat],
Expand Down
1 change: 1 addition & 0 deletions gap/display.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

DeclareAttribute("DotDigraph", IsDigraph);
DeclareOperation("DotColoredDigraph", [IsDigraph, IsList, IsList]);
DeclareOperation("DotColoredEdgeWeightedDigraph", [IsDigraph, IsList, IsList, IsList]);
DeclareOperation("DotVertexColoredDigraph", [IsDigraph, IsList]);
DeclareOperation("DotEdgeColoredDigraph", [IsDigraph, IsList]);
DeclareOperation("DotVertexLabelledDigraph", [IsDigraph]);
Expand Down
12 changes: 12 additions & 0 deletions gap/display.gi
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ function(D, vert, edge)
fi;
end);

# https://graphs.grevian.org/example
InstallMethod(DotColoredEdgeWeightedDigraph, "for a digraph by out-neighbours and three lists",
[IsDigraphByOutNeighboursRep, IsList, IsList, IsList],
function(D, vert, edge, weight)
local vert_func, edge_func;
if DIGRAPHS_ValidVertColors(D, vert) and DIGRAPHS_ValidEdgeColors(D, edge) then
vert_func := i -> StringFormatted("[color={}, style=filled]", vert[i]);
edge_func := {i, j} -> StringFormatted("[color={}, label={}]", edge[i][j], weight[i][j]);
return DIGRAPHS_DotDigraph(D, [vert_func], [edge_func]);
fi;
end);

InstallMethod(DotVertexColoredDigraph,
"for a digraph by out-neighbours and a list",
[IsDigraphByOutNeighboursRep, IsList],
Expand Down
25 changes: 24 additions & 1 deletion gap/weights.gd
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,27 @@ DeclareGlobalFunction("EdgeWeightedDigraph");
DeclareProperty("IsNegativeEdgeWeightedDigraph", IsDigraph and HasEdgeWeights);

# 2. Edge Weight Copies
DeclareOperation("EdgeWeightsMutableCopy", [IsDigraph and HasEdgeWeights]);
DeclareOperation("EdgeWeightsMutableCopy", [IsDigraph and HasEdgeWeights]);

# 3. Minimum Spanning Trees
DeclareAttribute("DigraphEdgeWeightedMinimumSpanningTree", IsDigraph and HasEdgeWeights);

# 4. Shortest Path
DeclareOperation("DigraphEdgeWeightedShortestPath", [IsDigraph and HasEdgeWeights, IsPosInt]);
DeclareAttribute("DigraphEdgeWeightedShortestPaths", IsDigraph and HasEdgeWeights);

# 5. Maximum Flow
DeclareOperation("DigraphMaximumFlow", [IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt]);
DeclareAttribute("DigraphMinimumCuts", IsDigraph);

# 6. Random Edge Weighted Digraph
DeclareOperation("RandomUniqueEdgeWeightedDigraph",[IsPosInt]);
DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt, IsFloat]);
DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsPosInt, IsRat]);
DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsFunction, IsPosInt, IsFloat]);
DeclareOperation("RandomUniqueEdgeWeightedDigraph", [IsFunction, IsPosInt, IsRat]);

# 7. Painting Edge Weighted Digraph
DeclareOperation("DigraphFromPaths", [IsDigraph, IsRecord]);
DeclareOperation("DigraphFromPath", [IsDigraph, IsRecord, IsPosInt]);
DeclareOperation("DotEdgeWeightedDigraph", [IsDigraph, IsDigraph, IsRecord]);
Loading

0 comments on commit 88cfb38

Please sign in to comment.