diff --git a/doc/display.xml b/doc/display.xml index f064d9a05..2e2b702bf 100644 --- a/doc/display.xml +++ b/doc/display.xml @@ -102,6 +102,7 @@ gap> Splash(DotDigraph(RandomDigraph(4))); + @@ -124,6 +125,13 @@ gap> Splash(DotDigraph(RandomDigraph(4))); are not the appropriate size, or have holes then the function will return an error.

+ DotColoredEdgeWeightedDigraph differs from DotColoredDigraph 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.

+ DotVertexColoredDigraph differs from DotDigraph 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 diff --git a/doc/weights.xml b/doc/weights.xml index 6816f17c7..f5e1844d4 100644 --- a/doc/weights.xml +++ b/doc/weights.xml @@ -69,4 +69,224 @@ gap> EdgeWeights(g); ]]> -<#/GAPDoc> \ No newline at end of file +<#/GAPDoc> + +<#GAPDoc Label="DigraphEdgeWeightedMinimumSpanningTree"> + + + A record. + + This function returns the record with 2 components total and mst. + The first component total represents the sum of the edge weights of the digraph that is returns. + The second component mst is the edge weighted digraph representation of the mst. +

+ + This algorithm only works on connected undirected graphs. If it is given a disconnected digraph, it will error. + The function will internally convert digraph representation to an undirected representation. + + See . + g := EdgeWeightedDigraph([[2],[1],[1,2]], [[12],[5],[6,9]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(g); +rec( mst := , total := 11 + )]]> + + +<#/GAPDoc> + +<#GAPDoc Label="DigraphEdgeWeightedShortestPath"> + + + A record. + + This operation, given a edge weighted digraph and a start 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 start 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. +

+ + This operation can handle negative edge weights BUT it will error if a negative cycle exists. +

+ + See . + g := EdgeWeightedDigraph([[2,3],[4],[4],[]],[[5,1],[6],[11],[]]); + +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]]); + +gap> DigraphEdgeWeightedShortestPath(ncg,1); +Error, negative cycle exists, +]]> + + +<#/GAPDoc> + +<#GAPDoc Label="DigraphEdgeWeightedShortestPaths"> + + + A list of lists of integers, floats or rationals. + + Given an edge weighted digraph, 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 . + g := EdgeWeightedDigraph([[2],[3],[1]],[[1],[2],[3]]); + +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 ] ] )]]> + + +<#/GAPDoc> + +<#GAPDoc Label="DigraphMaximumFlow"> + + + A record. + + Given an edge weighted digraph, 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.

+ + 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.

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

+ + See . + g := EdgeWeightedDigraph([[2,2],[3],[]],[[3,2],[1],[]]); + +gap> DigraphMaximumFlow(g, 1, 3); +rec( flows := [ [ ], [ 1, 0 ], [ 1 ] ], maxFlow := 1, + parents := [ [ ], [ 1, 1 ], [ 2 ] ] )]]> + + +<#/GAPDoc> + +<#GAPDoc Label="RandomUniqueEdgeWeightedDigraph"> + + + An edge weighted digraph. + + &STANDARD_FILT_TEXT; + + As well as the filters implemented in , the following filters are implemented: + . + + For , 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 n is a positive integer, then this function returns a random edge weighted + digraph with n vertices, without multiple edges but with unique edge weights. The result + may or may not have loops. If using , the resulting graph + will not have any loops by definition.

+ + If the optional second argument p is a float with value + 0 \leq p \leq 1, then an edge will exist between each + pair of vertices with probability approximately p. + If p is not specified, then a random probability will be assumed + (chosen with uniform probability).

+ g := RandomUniqueEdgeWeightedDigraph( +> IsStronglyConnectedDigraph, 5, 1); + +gap> g := RandomUniqueEdgeWeightedDigraph(5, 1); +]]> + + +<#/GAPDoc> + +<#GAPDoc Label="DigraphFromPaths"> + + + An edge weighted digraph. + + Given a digraph and a record 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. + + g := EdgeWeightedDigraph([[2],[3],[]],[[2],[1],[]]); + +gap> sp := DigraphEdgeWeightedShortestPath(g, 1); +rec( distances := [ 0, 2, 3 ], edges := [ fail, 1, 1 ], + parents := [ fail, 1, 2 ] ) +gap> sd := DigraphFromPaths(g, sp); +]]> + + +<#/GAPDoc> + + +<#GAPDoc Label="DigraphFromPath"> + + + An edge weighted digraph. + + Given a digraph and a record 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 dest vertex. + + g := EdgeWeightedDigraph([[2],[3],[]],[[2],[1],[]]); + +gap> sp := DigraphEdgeWeightedShortestPath(g, 1); +rec( distances := [ 0, 2, 3 ], edges := [ fail, 1, 1 ], + parents := [ fail, 1, 2 ] ) +gap> sd := DigraphFromPath(g, sp, 3); + +gap> sd := DigraphFromPath(g, sp, 2); +]]> + + +<#/GAPDoc> + +<#GAPDoc Label="DotEdgeWeightedDigraph"> + + + A string. + + Given andigraph, subdigraph and a record of a subdigraph within the original digraph, + using the record optional parameters, this will return a DOT of the subdigraph within the original digraph.

+ + Optional parameters in the record 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. + + g := EdgeWeightedDigraph([[2],[3],[]],[[2],[1],[]]); + +gap> sp := DigraphEdgeWeightedShortestPath(g, 1); +rec( distances := [ 0, 2, 3 ], edges := [ fail, 1, 1 ], + parents := [ fail, 1, 2 ] ) +gap> sd := DigraphFromPath(g, sp, 3); + +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"]]> + + +<#/GAPDoc> diff --git a/doc/z-chap5.xml b/doc/z-chap5.xml index bde06bf94..a3a09a685 100644 --- a/doc/z-chap5.xml +++ b/doc/z-chap5.xml @@ -27,6 +27,14 @@

Edge Weights <#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">
Orders diff --git a/gap/digraph.gi b/gap/digraph.gi index 4516135ab..e69156476 100644 --- a/gap/digraph.gi +++ b/gap/digraph.gi @@ -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); +end); + InstallMethod(RandomDigraphCons, "for IsAcyclicDigraph and an integer", [IsAcyclicDigraph, IsInt], {_, n} @@ -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], diff --git a/gap/display.gd b/gap/display.gd index 5916ef9b9..2874616f6 100644 --- a/gap/display.gd +++ b/gap/display.gd @@ -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]); diff --git a/gap/display.gi b/gap/display.gi index f55429169..feca28eff 100644 --- a/gap/display.gi +++ b/gap/display.gi @@ -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], diff --git a/gap/weights.gd b/gap/weights.gd index 33fed4801..5599adfa4 100644 --- a/gap/weights.gd +++ b/gap/weights.gd @@ -14,4 +14,27 @@ DeclareGlobalFunction("EdgeWeightedDigraph"); DeclareProperty("IsNegativeEdgeWeightedDigraph", IsDigraph and HasEdgeWeights); # 2. Edge Weight Copies -DeclareOperation("EdgeWeightsMutableCopy", [IsDigraph and HasEdgeWeights]); \ No newline at end of file +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]); diff --git a/gap/weights.gi b/gap/weights.gi index 9ddd888c2..4a0623ff0 100644 --- a/gap/weights.gi +++ b/gap/weights.gi @@ -90,4 +90,1125 @@ end); InstallMethod(EdgeWeightsMutableCopy, "for a digraph with edge weights", [IsDigraph and HasEdgeWeights], -D -> List(EdgeWeights(D), ShallowCopy)); \ No newline at end of file +D -> List(EdgeWeights(D), ShallowCopy)); + +############################################################################# +# 3. Minimum Spanning Trees +############################################################################# + +DIGRAPHS_Find := function(parent, i) + if parent[i] = i then + return i; + fi; + + parent[i] := DIGRAPHS_Find(parent, parent[i]); + return parent[i]; +end; + +DIGRAPHS_Union := function(parent, rank, x, y) + local xroot, yroot; + + xroot := DIGRAPHS_Find(parent, x); + yroot := DIGRAPHS_Find(parent, y); + + if rank[xroot] < rank[yroot] then + parent[xroot] := yroot; + elif rank[xroot] > rank[yroot] then + parent[yroot] := xroot; + else + parent[yroot] := xroot; + rank[xroot] := rank[xroot] + 1; + fi; +end; + +InstallMethod(DigraphEdgeWeightedMinimumSpanningTree, +"for an edge weighted digraph", +[IsDigraph and HasEdgeWeights], +function(digraph) + local weights, numberOfVertices, edgeList, u, + outNeigbours, idx, v, w, mst, mstWeights, i, e, + parent, rank, total, node, x, y; + + # check graph is connected + if not IsConnectedDigraph(digraph) then + ErrorNoReturn("digraph must be connected,"); + fi; + + weights := EdgeWeights(digraph); + + # create a list of edges containining u-v + # w: the weight of the edge + # u: the start vertex + # v: the finishing vertex of that edge + numberOfVertices := DigraphNrVertices(digraph); + + edgeList := []; + for u in DigraphVertices(digraph) do + outNeigbours := OutNeighbors(digraph)[u]; + for idx in [1 .. Size(outNeigbours)] do + v := outNeigbours[idx]; # the out neighbour + w := weights[u][idx]; # the weight to the out neighbour + + Add(edgeList, [w, u, v]); + od; + od; + + mst := []; + mstWeights := []; + + i := 1; + e := 1; + + # sort edge weights by their weight + StableSortBy(edgeList, x -> x[1]); + + parent := []; + rank := []; + + for v in [1 .. numberOfVertices] do + Add(parent, v); + Add(rank, 1); + Add(mst, []); + Add(mstWeights, []); + od; + + total := 0; + while e < (numberOfVertices) do + node := edgeList[i]; + + w := node[1]; + u := node[2]; + v := node[3]; + + i := i + 1; + + x := DIGRAPHS_Find(parent, u); + y := DIGRAPHS_Find(parent, v); + + # if cycle doesn't exist + if x <> y then + e := e + 1; + total := total + w; + + Add(mst[u], v); + Add(mstWeights[u], w); + + DIGRAPHS_Union(parent, rank, x, y); + fi; + od; + + return rec(total := total, mst := EdgeWeightedDigraph(mst, mstWeights)); +end); + +############################################################################# +# 4. Shortest Path +############################################################################# + +DIGRAPHS_Edge_Weighted_Dijkstra := function(digraph, source) + local weights, digraphVertices, nrVertices, adj, u, outNeighbours, idx, v, w, + distances, parents, edges, vertex, visited, queue, node, currDist, neighbour, + edgeInfo, distance, i, d; + + weights := EdgeWeights(digraph); + + digraphVertices := DigraphVertices(digraph); + nrVertices := Size(digraphVertices); + + # Create an adjacancy map for the edges with their associated weight + adj := HashMap(); + for u in digraphVertices do + adj[u] := HashMap(); + outNeighbours := OutNeighbors(digraph)[u]; + for idx in [1 .. Size(outNeighbours)] do + v := outNeighbours[idx]; # the out neighbour + w := weights[u][idx]; # the weight to the out neighbour + + # an edge to v already exists + if v in adj[u] then + # check if edge weight is less than current weight, + # and keep track of edge idx + if w < adj[u][v][1] then + adj[u][v] := [w, idx]; + fi; + else # edge doesn't exist already, so add it + adj[u][v] := [w, idx]; + fi; + od; + + od; + + distances := EmptyPlist(nrVertices); + parents := EmptyPlist(nrVertices); + edges := EmptyPlist(nrVertices); + + for vertex in digraphVertices do + distances[vertex] := infinity; + od; + + distances[source] := 0; + parents[source] := fail; + edges[source] := fail; + + visited := BlistList(digraphVertices, []); + + # make binary heap by priority of + # index 1 of each element (the cost to get to the node) + queue := BinaryHeap({x, y} -> x[1] > y[1]); + Push(queue, [0, source]); # the source vertex with cost 0 + + while not IsEmpty(queue) do + node := Pop(queue); + + currDist := node[1]; + u := node[2]; + + if visited[u] then + continue; + fi; + + visited[u] := true; + + for neighbour in KeyValueIterator(adj[u]) do + v := neighbour[1]; + edgeInfo := neighbour[2]; + w := edgeInfo[1]; + idx := edgeInfo[2]; + + distance := currDist + w; + + if Float(distance) < Float(distances[v]) then + distances[v] := distance; + + parents[v] := u; + edges[v] := idx; + + if not visited[v] then + Push(queue, [distance, v]); + fi; + fi; + od; + od; + + # fill lists with -1 if no path is possible + for i in [1 .. Size(distances)] do + d := distances[i]; + if Float(d) = Float(infinity) then + distances[i] := fail; + parents[i] := fail; + edges[i] := fail; + fi; + od; + + return rec(distances := distances, parents := parents, edges := edges); +end; + +DIGRAPHS_Edge_Weighted_Bellman_Ford := function(digraph, source) + local edgeList, weights, digraphVertices, distances, u, + outNeighbours, idx, v, w, _, + vertex, edge, parents, edges, d, i, flag; + + weights := EdgeWeights(digraph); + + digraphVertices := DigraphVertices(digraph); + edgeList := []; + for u in DigraphVertices(digraph) do + outNeighbours := OutNeighbors(digraph)[u]; + for idx in [1 .. Size(outNeighbours)] do + v := outNeighbours[idx]; # the out neighbour + w := weights[u][idx]; # the weight to the out neighbour + + Add(edgeList, [w, u, v, idx]); + od; + od; + + distances := [digraphVertices]; + parents := [digraphVertices]; + edges := [digraphVertices]; + + for vertex in digraphVertices do + distances[vertex] := infinity; + od; + + distances[source] := 0; + parents[source] := fail; + edges[source] := fail; + + flag := true; + + # relax all edges: update weight with smallest edges + for _ in digraphVertices do + for edge in edgeList do + w := edge[1]; + u := edge[2]; + v := edge[3]; + idx := edge[4]; + + if Float(distances[u]) <> Float(infinity) + and Float(distances[u]) + Float(w) < Float(distances[v]) then + distances[v] := distances[u] + w; + + parents[v] := u; + edges[v] := idx; + flag := false; + fi; + od; + + if flag then + break; + fi; + od; + + # check for negative cycles + for edge in edgeList do + w := edge[1]; + u := edge[2]; + v := edge[3]; + + if Float(distances[u]) <> Float(infinity) + and Float(distances[u]) + Float(w) < Float(distances[v]) then + ErrorNoReturn("negative cycle exists,"); + fi; + od; + + # fill lists with fail if no path is possible + for i in [1 .. Size(distances)] do + d := distances[i]; + if Float(d) = Float(infinity) then + distances[i] := fail; + parents[i] := fail; + edges[i] := fail; + fi; + od; + + return rec(distances := distances, parents := parents, edges := edges); +end; + +InstallMethod(DigraphEdgeWeightedShortestPath, "for an edge weighted digraph", +[IsDigraph and HasEdgeWeights, IsPosInt], +function(digraph, source) + local nrVertices; + # must be strongly connected + # if not IsStronglyConnectedDigraph(digraph) then + # ErrorNoReturn("digraph must be strongly connected,"); + # fi; + + # sources must exist in graph + nrVertices := DigraphNrVertices(digraph); + if source < 1 or source > nrVertices then + ErrorNoReturn("source vertex does not exist within digraph"); + fi; + + if IsNegativeEdgeWeightedDigraph(digraph) then + return DIGRAPHS_Edge_Weighted_Bellman_Ford(digraph, source); + else + return DIGRAPHS_Edge_Weighted_Dijkstra(digraph, source); + fi; +end); + +DIGRAPHS_Edge_Weighted_FloydWarshall := function(digraph) + local weights, adjMatrix, digraphVertices, + nrVertices, u, v, edges, outs, idx, + outNeighbours, w, i, k, distances, parents, pathParents; + + weights := EdgeWeights(digraph); + digraphVertices := DigraphVertices(digraph); + nrVertices := Size(digraphVertices); + outs := OutNeighbors(digraph); + + # Create adjacancy matrix + adjMatrix := EmptyPlist(nrVertices); + parents := EmptyPlist(nrVertices); + edges := EmptyPlist(nrVertices); + + for u in digraphVertices do + adjMatrix[u] := EmptyPlist(nrVertices); + outNeighbours := outs[u]; + for idx in [1 .. Size(outNeighbours)] do + v := outNeighbours[idx]; # the out neighbour + w := weights[u][idx]; # the weight to the out neighbour + + # only put min edge in if multiple edges exists + if IsBound(adjMatrix[u][v]) then + if w < adjMatrix[u][v][1] then + adjMatrix[u][v] := [w, idx]; + fi; + else + adjMatrix[u][v] := [w, idx]; + fi; + od; + od; + + # Create distances adj matrix + distances := EmptyPlist(nrVertices); + for u in digraphVertices do + distances[u] := EmptyPlist(nrVertices); + parents[u] := EmptyPlist(nrVertices); + edges[u] := EmptyPlist(nrVertices); + + for v in digraphVertices do + distances[u][v] := infinity; + parents[u][v] := fail; + edges[u][v] := fail; + + if u = v then + distances[u][v] := 0; + # if the same node, then the node has no parents + parents[u][v] := fail; + edges[u][v] := fail; + elif IsBound(adjMatrix[u][v]) then + w := adjMatrix[u][v][1]; + idx := adjMatrix[u][v][2]; + + distances[u][v] := w; + parents[u][v] := u; + edges[u][v] := idx; + + fi; + od; + od; + + for k in [1 .. nrVertices] do + for u in [1 .. nrVertices] do + for v in [1 .. nrVertices] do + if distances[u][k] < infinity and distances[k][v] < infinity then + if distances[u][k] + distances[k][v] < distances[u][v] then + distances[u][v] := distances[u][k] + distances[k][v]; + parents[u][v] := parents[u][k]; + edges[u][v] := edges[k][v]; + fi; + fi; + od; + od; + od; + + # detect negative cycles + for i in [1 .. nrVertices] do + if distances[i][i] < 0 then + ErrorNoReturn("negative cycle exists,"); + fi; + od; + + # replace infinity with fails + for u in [1 .. nrVertices] do + for v in [1 .. nrVertices] do + if distances[u][v] = infinity then + distances[u][v] := fail; + fi; + od; + od; + + pathParents := EmptyPlist(nrVertices); + + for u in [1 .. nrVertices] do + pathParents[u] := EmptyPlist(nrVertices); + for v in [1 .. nrVertices] do + pathParents[u][v] := parents[u][v]; + od; + od; + + return rec(distances := distances, parents := pathParents, edges := edges); +end; + +DIGRAPHS_Edge_Weighted_Johnson := function(digraph) + local digraphVertices, nrVertices, u, v, edges, + idx, outNeighbours, w, distances, + mutableWeights, mutableOuts, bellmanDistances, + distance, parents, dijkstra, bellman; + + mutableWeights := EdgeWeightsMutableCopy(digraph); + + digraphVertices := DigraphVertices(digraph); + nrVertices := Size(digraphVertices); + mutableOuts := OutNeighborsMutableCopy(digraph); + + # add new u that connects to all other v with weight 0 + Add(mutableOuts, [], 1); + Add(mutableWeights, [], 1); + + # fill new u + for v in [1 .. nrVertices] do + Add(mutableOuts[1], v + 1); + Add(mutableWeights[1], 0); + od; + + # update v to v + 1 + for u in [2 .. nrVertices + 1] do + for v in [1 .. Size(mutableOuts[u])] do + mutableOuts[u][v] := mutableOuts[u][v] + 1; + od; + od; + + digraph := EdgeWeightedDigraph(mutableOuts, mutableWeights); + bellman := DIGRAPHS_Edge_Weighted_Bellman_Ford(digraph, 1); + bellmanDistances := bellman.distances; + + mutableWeights := EdgeWeightsMutableCopy(digraph); + digraphVertices := DigraphVertices(digraph); + nrVertices := Size(digraphVertices); + mutableOuts := OutNeighborsMutableCopy(digraph); + + # set weight(u, v) + # equal to weight(u, v) + bell_dist(u) - bell_dist(v) for each edge (u, v) + for u in digraphVertices do + outNeighbours := mutableOuts[u]; + for idx in [1 .. Size(outNeighbours)] do + v := outNeighbours[idx]; + w := mutableWeights[u][idx]; + mutableWeights[u][idx] := w + + bellmanDistances[u] - bellmanDistances[v]; + od; + od; + + Remove(mutableOuts, 1); + Remove(mutableWeights, 1); + + # update v to v - 1 + for u in [1 .. Size(mutableOuts)] do + for v in [1 .. Size(mutableOuts[u])] do + mutableOuts[u][v] := mutableOuts[u][v] - 1; + od; + od; + + digraph := EdgeWeightedDigraph(mutableOuts, mutableWeights); + digraphVertices := DigraphVertices(digraph); + + distance := EmptyPlist(nrVertices); + parents := EmptyPlist(nrVertices); + edges := EmptyPlist(nrVertices); + + # run dijkstra + for u in digraphVertices do + dijkstra := DIGRAPHS_Edge_Weighted_Dijkstra(digraph, u); + distance[u] := dijkstra.distances; + parents[u] := dijkstra.parents; + edges[u] := dijkstra.edges; + od; + + # correct distances + for u in digraphVertices do + for v in digraphVertices do + if distance[u][v] = fail then + continue; + fi; + distance[u][v] := distance[u][v] + + (bellmanDistances[v + 1] - bellmanDistances[u + 1]); + od; + od; + + return rec(distances := distance, parents := parents, edges := edges); +end; + +InstallMethod(DigraphEdgeWeightedShortestPaths, "for an edge weighted digraph", +[IsDigraph and HasEdgeWeights], +function(digraph) + local maxNodes, threshold, digraphVertices, nrVertices, nrEdges; + + digraphVertices := DigraphVertices(digraph); + nrVertices := Size(digraphVertices); + nrEdges := DigraphNrEdges(digraph); + + maxNodes := nrVertices * (nrVertices - 1); + + # the boundary for performance is edge weight 0.125 + # so if nr edges for vertices v is less + # than total number of edges in a connected + # graph we use johnson's algorithm + # which performs better on sparse graphs, otherwise + # we use floyd warshall algorithm. + # This information is gathered from benchmarking tests. + threshold := Int(maxNodes / 8); + if nrEdges <= threshold then + return DIGRAPHS_Edge_Weighted_Johnson(digraph); + else + return DIGRAPHS_Edge_Weighted_FloydWarshall(digraph); + fi; +end); + +############################################################################# +# 5. Maximum Flow +############################################################################# + +InstallMethod(DigraphMaximumFlow, "for an edge weighted digraph", +[IsDigraph and HasEdgeWeights, IsPosInt, IsPosInt], +function(digraph, source, sink) + local push, relabel, discharge, GetFlowInformation, PushRelabel; + + push := function(capacityMatrix, flowMatrix, excess, queue, u, v) + local d; + + d := Minimum(excess[u], capacityMatrix[u][v] - flowMatrix[u][v]); + + flowMatrix[u][v] := flowMatrix[u][v] + d; + flowMatrix[v][u] := flowMatrix[v][u] - d; + excess[u] := excess[u] - d; + excess[v] := excess[v] + d; + + if d = 1 and excess[v] = d then + PlistDequePushBack(queue, v); + fi; + end; + + relabel := function(capacityMatrix, flowMatrix, height, u) + local d, v; + + d := infinity; + for v in [1 .. Size(capacityMatrix)] do + if capacityMatrix[u][v] - flowMatrix[u][v] > 0 then + d := Minimum(d, height[v]); + fi; + od; + if d < infinity then + height[u] := d + 1; + fi; + + end; + + discharge := function( + capacityMatrix, flowMatrix, excess, seen, height, queue, u) + local v; + + while excess[u] > 0 do + if seen[u] <= Size(capacityMatrix) then + v := seen[u]; + if capacityMatrix[u][v] - flowMatrix[u][v] > 0 + and height[u] > height[v] then + push(capacityMatrix, flowMatrix, excess, queue, u, v); + else + seen[u] := seen[u] + 1; + fi; + else + relabel(capacityMatrix, flowMatrix, height, u); + seen[u] := 1; + fi; + od; + end; + + GetFlowInformation := function(digraph, flowMatrix, source) + local parents, flows, u, v, f, outs, outNeighbours, + nrVertices, maxFlow, _, idx, weights, w; + + outs := OutNeighbors(digraph); + weights := EdgeWeights(digraph); + + nrVertices := Size(flowMatrix); + + parents := EmptyPlist(nrVertices); + flows := EmptyPlist(nrVertices); + maxFlow := 0; + + # create empty 2D list for output + for _ in [1 .. nrVertices] do + Add(parents, []); + Add(flows, []); + od; + + # initialise source values + parents[source] := []; + flows[source] := []; + + for u in [1 .. nrVertices] do + for v in [1 .. nrVertices] do + f := flowMatrix[u][v]; + if Float(f) > Float(0) then + outNeighbours := outs[u]; + if u = source then + maxFlow := maxFlow + f; + fi; + + for idx in [1 .. Size(outNeighbours)] do + w := weights[u][idx]; + if outNeighbours[idx] = v then + if f >= w then + Add(flows[v], w); + f := f - w; + elif f >= 0 then + Add(flows[v], f); + f := 0; + fi; + Add(parents[v], u); + fi; + od; + + fi; + od; + od; + + return [parents, flows, maxFlow]; + end; + + PushRelabel := function(digraph, source, sink) + local weights, capacityMatrix, digraphVertices, + nrVertices, u, v, outs, idx, outNeighbours, w, queue, flowMatrix, seen, + excess, height, flowInformation; + + weights := EdgeWeights(digraph); + digraphVertices := DigraphVertices(digraph); + nrVertices := Size(digraphVertices); + outs := OutNeighbors(digraph); + capacityMatrix := EmptyPlist(nrVertices); + flowMatrix := EmptyPlist(nrVertices); + seen := EmptyPlist(nrVertices); + height := EmptyPlist(nrVertices); + excess := EmptyPlist(nrVertices); + queue := PlistDeque(); + + if source < 1 or source > nrVertices then + ErrorNoReturn("invalid source,"); + fi; + + if sink < 1 or sink > nrVertices then + ErrorNoReturn("invalid sink,"); + fi; + + # fill adj and max flow with zeroes + for u in digraphVertices do + capacityMatrix[u] := EmptyPlist(nrVertices); + flowMatrix[u] := EmptyPlist(nrVertices); + seen[u] := 1; + height[u] := 0; + excess[u] := 0; + + if u <> source and u <> sink then + PlistDequePushBack(queue, u); + fi; + + for v in digraphVertices do + capacityMatrix[u][v] := 0; + flowMatrix[u][v] := 0; + od; + od; + + for u in digraphVertices do + outNeighbours := outs[u]; + for idx in [1 .. Size(outNeighbours)] do + v := outNeighbours[idx]; # the out neighbour + w := weights[u][idx]; # the weight to the out neighbour + + capacityMatrix[u][v] := capacityMatrix[u][v] + w; + od; + od; + + height[source] := nrVertices; + excess[source] := infinity; + + for v in [1 .. nrVertices] do + if v <> source then + push(capacityMatrix, flowMatrix, excess, queue, source, v); + fi; + od; + + while not IsEmpty(queue) do + u := PlistDequePopFront(queue); + if u <> source and u <> sink then + discharge(capacityMatrix, flowMatrix, + excess, seen, height, queue, u); + fi; + od; + + flowInformation := GetFlowInformation(digraph, flowMatrix, source); + + return rec(parents := flowInformation[1], + flows := flowInformation[2], + maxFlow := flowInformation[3]); + end; + + return PushRelabel(digraph, source, sink); +end); + +############################################################################# +# 6. Random Edge Weighted Digraph +############################################################################# + +DIGRAPHS_Generate_Unique_Weights := function(digraph) + local weights, digraphVertices, + nrEdges, randomWeights, outNeighbours, u, idx, randWeightIdx; + + digraphVertices := DigraphVertices(digraph); + nrEdges := DigraphNrEdges(digraph) + 1; + + randomWeights := Shuffle([1 .. nrEdges]); + weights := []; + randWeightIdx := 1; + + # Create random weights for each edge. + # weights are unique [1..number of edges + 1] + for u in digraphVertices do + outNeighbours := OutNeighbors(digraph)[u]; + Add(weights, []); + for idx in [1 .. Size(outNeighbours)] do + weights[u][idx] := randomWeights[randWeightIdx]; + randWeightIdx := randWeightIdx + 1; + od; + od; + + return weights; +end; + +DIGRAPHS_Random_Edge_Weighted_Digraph_N := function(n) + local digraph, weights; + + digraph := RandomDigraphCons(IsImmutableDigraph, n); + weights := DIGRAPHS_Generate_Unique_Weights(digraph); + + return EdgeWeightedDigraph(digraph, weights); +end; + +DIGRAPHS_Random_Edge_Weighted_Digraph_N_P := function(n, p) + local digraph, weights; + + digraph := RandomDigraphCons(IsImmutableDigraph, n, p); + weights := DIGRAPHS_Generate_Unique_Weights(digraph); + + return EdgeWeightedDigraph(digraph, weights); +end; + +DIGRAPHS_Random_Edge_Weighted_Digraph_Filt_N_P := function(filt, n, p) + local digraph, weights; + + digraph := RandomDigraphCons(filt, n, p); + weights := DIGRAPHS_Generate_Unique_Weights(digraph); + + return EdgeWeightedDigraph(digraph, weights); +end; + +InstallMethod(RandomUniqueEdgeWeightedDigraph, +"for a pos int", [IsPosInt], +function(n) + return DIGRAPHS_Random_Edge_Weighted_Digraph_N(n); +end); + +InstallMethod(RandomUniqueEdgeWeightedDigraph, +"for a pos int and a float", [IsPosInt, IsFloat], +function(n, p) + return DIGRAPHS_Random_Edge_Weighted_Digraph_N_P(n, p); +end); + +InstallMethod(RandomUniqueEdgeWeightedDigraph, +"for a pos int and a rational", [IsPosInt, IsRat], +function(n, p) + return DIGRAPHS_Random_Edge_Weighted_Digraph_N_P(n, p); +end); + +InstallMethod(RandomUniqueEdgeWeightedDigraph, +"for a func, a pos int, and a float", [IsFunction, IsPosInt, IsFloat], +function(filt, n, p) + return DIGRAPHS_Random_Edge_Weighted_Digraph_Filt_N_P(filt, n, p); +end); + +InstallMethod(RandomUniqueEdgeWeightedDigraph, +"for a func, a pos int, and a rational", [IsFunction, IsPosInt, IsRat], +function(filt, n, p) + return DIGRAPHS_Random_Edge_Weighted_Digraph_Filt_N_P(filt, n, p); +end); + +############################################################################# +# 7. Painting Edge Weighted Digraph +############################################################################# +InstallMethod(DigraphFromPath, "for a digraph, a record, and a pos int", +[IsDigraph, IsRecord, IsPosInt], +function(digraph, record, destination) + local idx, distances, edges, p, parents, + nrVertices, outNeighbours, vertex; + + distances := record.distances; + edges := record.edges; + parents := record.parents; + nrVertices := Size(distances); + + outNeighbours := EmptyPlist(nrVertices); + + # fill out neighbours with empty lists + for idx in [1 .. nrVertices] do + Add(outNeighbours, []); + od; + + vertex := destination; + # while vertex isnt the start vertex + while parents[vertex] <> fail do + p := parents[vertex]; # parent of vertex is p + + Add(outNeighbours[p], vertex); + vertex := p; + od; + + return Digraph(outNeighbours); +end); + +InstallMethod(DigraphFromPaths, +"for a digraph, and a record", [IsDigraph, IsRecord], +function(digraph, record) + local idx, distances, edges, parents, nrVertices, outNeighbours, + u, v; + + distances := record.distances; + edges := record.edges; + parents := record.parents; + nrVertices := Size(distances); + + outNeighbours := EmptyPlist(nrVertices); + + # fill out neighbours with empty lists + for idx in [1 .. nrVertices] do + Add(outNeighbours, []); + od; + + for idx in [1 .. Size(parents)] do + u := parents[idx]; + v := idx; + + # this is the start vertex + if u = fail then + continue; + fi; + + Add(outNeighbours[u], v); + od; + + return Digraph(outNeighbours); +end); + +DIGRAPHS_Get_Least_Weight_Edge := function(digraph, u, v) + local weights, edgeWeights, smallestEdgeIdx, minWeight, w, outs, idx; + + outs := OutNeighbours(digraph)[u]; + weights := EdgeWeights(digraph); + + edgeWeights := weights[u]; + + smallestEdgeIdx := 1; + minWeight := infinity; + for idx in [1 .. Size(edgeWeights)] do + w := edgeWeights[idx]; + if w < minWeight and outs[idx] = v then + minWeight := w; + smallestEdgeIdx := idx; + fi; + od; + + return smallestEdgeIdx; +end; + +InstallMethod(DotEdgeWeightedDigraph, "for a digraph, a digraph, and a record", +[IsDigraph, IsDigraph, IsRecord], +function(digraph, subdigraph, options) + local digraphVertices, outsOriginal, + outNeighboursOriginal, nrVertices, outsSubdigraph, + outNeighboursSubdigraph, edgeColours, + vertColours, u, v, idxOfSmallestEdge, opts, + edgeColour, sourceColour, destColour, vertColour, weights, default, name; + + default := rec( + highlightColour := "blue", + edgeColour := "black", + vertColour := "lightpink", + sourceColour := "green", + destColour := "red"); + + if IsRecord(options) then + opts := ShallowCopy(options); + fi; + + for name in RecNames(default) do + if IsBound(opts.(name)) then + default.(name) := opts.(name); + fi; + od; + + digraphVertices := DigraphVertices(subdigraph); + nrVertices := Size(digraphVertices); + outsOriginal := OutNeighbors(digraph); + outsSubdigraph := OutNeighbors(subdigraph); + + edgeColours := EmptyPlist(nrVertices); + vertColours := EmptyPlist(nrVertices); + + for u in digraphVertices do + vertColours[u] := default.vertColour; + edgeColours[u] := []; + outNeighboursSubdigraph := outsSubdigraph[u]; + outNeighboursOriginal := outsOriginal[u]; + + # make everything black + for v in outNeighboursOriginal do + Add(edgeColours[u], default.edgeColour); + od; + + # paint mst edges + for v in outNeighboursSubdigraph do + idxOfSmallestEdge := DIGRAPHS_Get_Least_Weight_Edge(digraph, u, v); + edgeColours[u][idxOfSmallestEdge] := default.highlightColour; + od; + od; + + # set source and dest colours + if IsBound(opts.source) then + if 1 <= opts.source and opts.source <= nrVertices then + vertColours[opts.source] := default.sourceColour; + else + ErrorNoReturn("source vertex does not exist,"); + fi; + fi; + + if IsBound(opts.dest) then + if 1 <= opts.dest and opts.dest <= nrVertices then + vertColours[opts.dest] := default.destColour; + else + ErrorNoReturn("destination vertex does not exist,"); + fi; + fi; + + weights := EdgeWeights(digraph); + + return DotColoredEdgeWeightedDigraph( + digraph, vertColours, edgeColours, weights); +end); + +# InstallMethod(DigraphMinimumCuts, "for a digraph", +# [IsDigraph], +# function(digraph) +# local contract, minCut, fastMinCut, KargerStein; + +# contract := function(digraph, options) +# local digraphVertices, nrVertices, nrV, nrEdges, i, u, v, +# edgeList, outNeigbours, idx, randomEdgeIdx, cuts, edgesCut, parent, +# x, y, rank, opts, default, name; + +# default := rec(minV := 2); + +# if IsRecord(options) then +# opts := ShallowCopy(options); +# else +# opts := rec(); +# fi; + +# for name in RecNames(default) do +# if IsBound(opts.(name)) then +# default.(name) := opts.(name); +# fi; +# od; + +# # weights := EdgeWeights(digraph); +# digraphVertices := DigraphVertices(digraph); +# nrVertices := Size(digraphVertices); +# nrEdges := Size(DigraphEdges(digraph)); + +# edgeList := []; +# for u in digraphVertices do +# outNeigbours := OutNeighbors(digraph)[u]; +# for idx in [1 .. Size(outNeigbours)] do +# v := outNeigbours[idx]; # the out neighbour + +# Add(edgeList, [u, v]); +# od; +# od; + +# # sort edge weights by their weight +# i := Size(edgeList); + +# parent := []; +# rank := []; + +# for v in [1 .. nrVertices] do +# Add(parent, v); +# Add(rank, 1); +# od; + +# edgesCut := []; +# nrV := nrVertices; +# while nrV > default.minV do +# randomEdgeIdx := Random([1 .. Size(edgeList)]); + +# u := edgeList[randomEdgeIdx][1]; +# v := edgeList[randomEdgeIdx][2]; + +# x := DIGRAPHS_Find(parent, u); +# y := DIGRAPHS_Find(parent, v); + +# if x <> y then +# nrV := nrV - 1; +# DIGRAPHS_Union(parent, rank, x, y); +# fi; +# od; + +# cuts := 0; + +# for i in [1 .. nrEdges] do +# u := edgeList[i][1]; +# v := edgeList[i][2]; + +# x := DIGRAPHS_Find(parent, u); +# y := DIGRAPHS_Find(parent, v); + +# if x <> y then +# Add(edgesCut, [u, v]); +# cuts := cuts + 1; +# fi; +# od; + +# return rec(cuts := cuts, edgesCut := edgesCut); +# end; + +# minCut := function(digraph) +# local nrEdges, nrVertices, upperBound, i, cutInfo, edgesCut; + +# nrEdges := Size(DigraphEdges(digraph)); +# nrVertices := Size(DigraphVertices(digraph)); + +# # upperBound := Int(nrVertices * +# # (nrVertices - 1) * Log((nrVertices/2), 2)); +# upperBound := nrVertices; + +# for i in [1 .. upperBound] do +# cutInfo := contract(digraph, rec()); +# if cutInfo.cuts <= nrEdges then +# nrEdges := cutInfo.cuts; +# edgesCut := cutInfo.edgesCut; +# fi; +# od; + +# return rec(cuts := nrEdges, edgesCut := edgesCut); +# end; + +# fastMinCut := function(digraph) +# local nrVertices, g1, g2; + +# nrVertices := Size(DigraphVertices(digraph)); +# if (nrVertices <= 6) then +# return minCut(digraph); +# fi; + +# g1 := contract(digraph, rec(minV := 2)); +# g2 := contract(digraph, rec(minV := 2)); + +# if g1.cuts <= g2.cuts then +# return rec(cuts := g1.cuts, edgesCut := g1.edgesCut); +# else +# return rec(cuts := g2.cuts, edgesCut := g2.edgesCut); +# fi; +# end; + +# KargerStein := function(digraph) +# local digraphVertices, nrVertices, nrEdges, +# i, upperBound, edgesCut, cutInfo; + +# digraphVertices := DigraphVertices(digraph); +# nrVertices := Size(digraphVertices); +# nrEdges := Size(DigraphEdges(digraph)); +# edgesCut := []; + +# # upperBound := Int(nrVertices * Log(nrVertices, 2)/(nrVertices - 1)); +# upperBound := nrVertices; + +# for i in [1 .. upperBound] do +# cutInfo := fastMinCut(digraph); +# if cutInfo.cuts <= nrEdges then +# nrEdges := cutInfo.cuts; +# edgesCut := cutInfo.edgesCut; +# fi; +# od; + +# return rec(cuts := nrEdges, edgesCut := edgesCut); +# end; + +# return KargerStein(digraph); +# end); diff --git a/tst/standard/weights.tst b/tst/standard/weights.tst index dd3c94828..edb439052 100644 --- a/tst/standard/weights.tst +++ b/tst/standard/weights.tst @@ -18,7 +18,7 @@ gap> DIGRAPHS_StartTest(); gap> d := EdgeWeightedDigraph([[2], []], [[5], []]); -# create with Digraph +# create edge weighted digraph gap> d := EdgeWeightedDigraph(Digraph([[2], []]), [[5], []]); @@ -85,6 +85,274 @@ gap> d := EdgeWeightedDigraph([[2], [1]], [[-5], [10]]); gap> IsNegativeEdgeWeightedDigraph(d); true +# not connnected digraph +gap> d := EdgeWeightedDigraph([[1], [2]], [[5], [10]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +Error, digraph must be connected, + +# digraph with one node +gap> d := EdgeWeightedDigraph([[]], [[]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := 0 ) + +# digraph with loop +gap> d := EdgeWeightedDigraph([[1]], [[5]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := 0 ) + +# digraph with cycle +gap> d := EdgeWeightedDigraph([[2],[3],[1]],[[5],[10],[15]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := 15 ) + +# digraph with negative edge +gap> d := EdgeWeightedDigraph([[2],[]],[[-5],[]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := -5 ) + +# digraph with negative cycle +gap> d := EdgeWeightedDigraph([[2],[3],[1]],[[-5],[-10],[-15]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := -25 ) + +# digraph with parallel edges +gap> d := EdgeWeightedDigraph([[2,2,2],[1]],[[10,5,15],[7]]); + +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := 5 ) + +# graph one node +gap> d := EdgeWeightedDigraph([[]],[[]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0 ], edges := [ fail ], parents := [ fail ] ) + +# early break when path doesn't exist +gap> d := EdgeWeightedDigraph([[], [1]], [[], [-10]]);; +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, fail ], edges := [ fail, fail ], + parents := [ fail, fail ] ) + +# graph with one node and self loop +gap> d := EdgeWeightedDigraph([[1]],[[5]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0 ], edges := [ fail ], parents := [ fail ] ) + +# graph with two nodes and self loop on second node +gap> d := EdgeWeightedDigraph([[2],[1,2]],[[5],[5,5]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, 5 ], edges := [ fail, 1 ], parents := [ fail, 1 ] ) + +# graph with cycle +gap> d := EdgeWeightedDigraph([[2],[3],[1]],[[2],[3],[4]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, 2, 5 ], edges := [ fail, 1, 1 ], + parents := [ fail, 1, 2 ] ) + +# parallel edges +gap> d := EdgeWeightedDigraph([[2,2,2],[1]],[[10,5,15],[7]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, 5 ], edges := [ fail, 2 ], parents := [ fail, 1 ] ) + +# negative edges +gap> d := EdgeWeightedDigraph([[2],[1]],[[-2],[7]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, -2 ], edges := [ fail, 1 ], parents := [ fail, 1 ] ) + +# parallel negative edges +gap> d := EdgeWeightedDigraph([[2,2,2],[1]],[[-2,-3,-4],[7]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, -4 ], edges := [ fail, 3 ], parents := [ fail, 1 ] ) + +# negative cycle +gap> d := EdgeWeightedDigraph([[2,2,2],[1]],[[-10,5,-15],[7]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +Error, negative cycle exists, + +# source not in graph pos int +gap> d := EdgeWeightedDigraph([[2],[1]],[[2],[7]]); + +gap> DigraphEdgeWeightedShortestPath(d, 3); +Error, source vertex does not exist within digraph + +# no path exists +gap> d := EdgeWeightedDigraph([[1],[2]],[[5],[10]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, fail ], edges := [ fail, fail ], + parents := [ fail, fail ] ) + +# no path exists with negative edge weight +gap> d := EdgeWeightedDigraph([[2],[2],[]],[[-5],[10],[]]); + +gap> r := DigraphEdgeWeightedShortestPath(d, 1);; +gap> r.distances = [ 0, -5, fail ]; +true +gap> r.edges = [ fail, 1, fail ]; +true +gap> r.parents = [ fail, 1, fail ]; +true + +# parallel edges +gap> d := EdgeWeightedDigraph([[2,2,2],[]],[[3,2,1],[]]); + +gap> DigraphEdgeWeightedShortestPaths(d); +rec( distances := [ [ 0, 1 ], [ fail, 0 ] ], + edges := [ [ fail, 3 ], [ fail, fail ] ], + parents := [ [ fail, 1 ], [ fail, fail ] ] ) + +# negative cycle +gap> d := EdgeWeightedDigraph([[2],[3],[1]],[[-3],[-5],[-7]]); + +gap> DigraphEdgeWeightedShortestPaths(d); +Error, negative cycle exists, + +# source not in graph neg int +gap> DigraphEdgeWeightedShortestPath(d, -1); +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `DigraphEdgeWeightedShortestPath' on 2 a\ +rguments + +# testing johnson +gap> d := EdgeWeightedDigraph([[2],[3],[],[],[]],[[3],[5],[],[],[]]); + +gap> DigraphEdgeWeightedShortestPaths(d); +rec( distances := [ [ 0, 3, 8, fail, fail ], [ fail, 0, 5, fail, fail ], + [ fail, fail, 0, fail, fail ], [ fail, fail, fail, 0, fail ], + [ fail, fail, fail, fail, 0 ] ], + edges := [ [ fail, 1, 1, fail, fail ], [ fail, fail, 1, fail, fail ], + [ fail, fail, fail, fail, fail ], [ fail, fail, fail, fail, fail ], + [ fail, fail, fail, fail, fail ] ], + parents := [ [ fail, 1, 2, fail, fail ], [ fail, fail, 2, fail, fail ], + [ fail, fail, fail, fail, fail ], [ fail, fail, fail, fail, fail ], + [ fail, fail, fail, fail, fail ] ] ) + +# empty digraphs +gap> d := EdgeWeightedDigraph([],[]); + +gap> DigraphMaximumFlow(d, 1, 1); +Error, invalid source, + +# single vertex (also empty digraphs) +gap> d := EdgeWeightedDigraph([[]],[[]]); + +gap> DigraphMaximumFlow(d, 1, 1); +rec( flows := [ [ ] ], maxFlow := 0, parents := [ [ ] ] ) + +# source = dest +gap> d := EdgeWeightedDigraph([[2],[]],[[5],[]]); + +gap> DigraphMaximumFlow(d, 1, 1); +rec( flows := [ [ ], [ ] ], maxFlow := 0, parents := [ [ ], [ ] ] ) + +# has loop +gap> d := EdgeWeightedDigraph([[1,2],[]],[[5,10],[]]); + +gap> DigraphMaximumFlow(d, 1, 2); +rec( flows := [ [ ], [ 10 ] ], maxFlow := 10, parents := [ [ ], [ 1 ] ] ) + +# invalid source +gap> d := EdgeWeightedDigraph([[1,2],[]],[[5,10],[]]); + +gap> DigraphMaximumFlow(d, 5, 2); +Error, invalid source, + +# invalid sink +gap> d := EdgeWeightedDigraph([[1,2],[]],[[5,10],[]]); + +gap> DigraphMaximumFlow(d, 1, 5); +Error, invalid sink, + +# sink not reachable +gap> d := EdgeWeightedDigraph([[1],[]],[[5],[]]); + +gap> DigraphMaximumFlow(d, 1, 2); +rec( flows := [ [ ], [ ] ], maxFlow := 0, parents := [ [ ], [ ] ] ) + +# source has in neighbours +gap> d := EdgeWeightedDigraph([[2],[3],[]],[[5],[10],[]]); + +gap> DigraphMaximumFlow(d, 2, 3); +rec( flows := [ [ ], [ ], [ 10 ] ], maxFlow := 10, + parents := [ [ ], [ ], [ 2 ] ] ) + +# sink has out neighbours +gap> d := EdgeWeightedDigraph([[2],[3],[2]],[[5],[10],[7]]); + +gap> DigraphMaximumFlow(d, 2, 3); +rec( flows := [ [ ], [ ], [ 10 ] ], maxFlow := 10, + parents := [ [ ], [ ], [ 2 ] ] ) + +# cycle +gap> d := EdgeWeightedDigraph([[2],[3],[1]],[[5],[10],[7]]); + +gap> DigraphMaximumFlow(d, 1, 3); +rec( flows := [ [ ], [ 5 ], [ 5 ] ], maxFlow := 5, + parents := [ [ ], [ 1 ], [ 2 ] ] ) + +# random edge weighted digraph creation +gap> d := RandomUniqueEdgeWeightedDigraph(5);; +gap> DigraphNrVertices(d); +5 +gap> OutNeighbours(d); +[ [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], [ 1, 2, 3, 4, 5 ], + [ 1, 2, 3, 4, 5 ] ] + +# more random edge weighted digraph creation tests +gap> d := RandomUniqueEdgeWeightedDigraph(5, 0.1);; +gap> DigraphNrVertices(d); +5 + +# more random edge weighted digraph creation tests +gap> d := RandomUniqueEdgeWeightedDigraph(IsStronglyConnectedDigraph, 5, 0.1);; +gap> DigraphNrVertices(d); +5 + +# dot tests +gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);; +gap> sp := DigraphEdgeWeightedShortestPath(d, 1);; +gap> sd := DigraphFromPaths(d, sp);; +gap> DotEdgeWeightedDigraph(d, sd, rec(sourceColour:="red"));; + +# dot tests +gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);; +gap> sp := DigraphEdgeWeightedShortestPath(d, 1);; +gap> sd := DigraphFromPaths(d, sp);; +gap> DotEdgeWeightedDigraph(d, sd, rec(source:=1));; + +# dot tests +gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);; +gap> sp := DigraphEdgeWeightedShortestPath(d, 1);; +gap> sd := DigraphFromPaths(d, sp);; +gap> DotEdgeWeightedDigraph(d, sd, rec(source:=500)); +Error, source vertex does not exist, + +# dot tests +gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);; +gap> sp := DigraphEdgeWeightedShortestPath(d, 1);; +gap> sd := DigraphFromPaths(d, sp);; +gap> DotEdgeWeightedDigraph(d, sd, rec(dest:=2));; + +# dot tests +gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);; +gap> sp := DigraphEdgeWeightedShortestPath(d, 1);; +gap> sd := DigraphFromPaths(d, sp);; +gap> DotEdgeWeightedDigraph(d, sd, rec(dest:=500)); +Error, destination vertex does not exist, + # gap> DIGRAPHS_StopTest(); -gap> STOP_TEST("Digraphs package: standard/weights.tst", 0); \ No newline at end of file +gap> STOP_TEST("Digraphs package: standard/weights.tst", 0); diff --git a/tst/testinstall.tst b/tst/testinstall.tst index 0f8b0acbd..9111cf776 100644 --- a/tst/testinstall.tst +++ b/tst/testinstall.tst @@ -411,10 +411,18 @@ gap> String(D); "DigraphFromDigraph6String(\"&CECG\")" gap> String(CycleDigraph(4)); "CycleDigraph(4)" + +# Edge-weighted digraphs gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]); gap> EdgeWeights(d); [ [ 5 ], [ 10 ] ] +gap> DigraphEdgeWeightedMinimumSpanningTree(d); +rec( mst := , total := 5 ) +gap> d := EdgeWeightedDigraph([[2], [1, 2]], [[5], [5, 5]]); + +gap> DigraphEdgeWeightedShortestPath(d, 1); +rec( distances := [ 0, 5 ], edges := [ fail, 1 ], parents := [ fail, 1 ] ) # DIGRAPHS_UnbindVariables gap> Unbind(D);