Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added EdgeWeightedDigraph #575

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions doc/weights.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#############################################################################
##
#W weights.xml
#Y Copyright (C) 2023 Raiyan Chowdhury
##
## Licensing information can be found in the README file of this package.
##
#############################################################################
##


<#GAPDoc Label="EdgeWeights">
<ManSection>
<Attr Name="EdgeWeights" Arg="digraph"/>
<Oper Name="EdgeWeightsMutableCopy" Arg="digraph"/>
<Returns>A list of lists of integers, floats or rationals.</Returns>
<Description>
<C>EdgeWeights</C> returns the list of lists of edge weights of
the edges of the digraph <A>digraph</A>.<P/>

More specifically, <C>weights[i][j]</C> is the weight given to the <C>j</C>th edge from vertex <C>i</C>, according to the ordering of edges given by <C>OutNeighbours(digraph)[i]</C>.<P/>

The function <C>EdgeWeights</C> returns an immutable list of immutable
lists, whereas the function <C>EdgeWeightsMutableCopy</C> returns a copy
of <C>EdgeWeights</C> which is a mutable list of mutable lists.<P/>

The edge weights of a digraph cannot be computed and must be set either
using <C>SetEdgeWeights</C> or <Ref Func="EdgeWeightedDigraph" />.<P/>
<Example><![CDATA[
gap> gr := EdgeWeightedDigraph([[2], [3], [1]], [[5], [10], [15]]);
<immutable digraph with 3 vertices, 3 edges>
gap> EdgeWeights(gr);
[ [ 5 ], [ 10 ], [ 15 ] ]
gap> a := EdgeWeightsMutableCopy(gr);
[ [ 5 ], [ 10 ], [ 15 ] ]
gap> a[1][1] := 100;
100
gap> a;
[ [ 100 ], [ 10 ], [ 15 ] ]
gap> b := EdgeWeights(gr);
[ [ 5 ], [ 10 ], [ 15 ] ]
gap> b[1][1] := 534;
Error, List Assignment: <list> must be a mutable list
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>

RaiyanC marked this conversation as resolved.
Show resolved Hide resolved
<#GAPDoc Label="EdgeWeightedDigraph">
<ManSection>
<Func Name="EdgeWeightedDigraph" Arg="digraph, weights"/>
<Returns>A digraph or <K>fail</K></Returns>
<Description>
The argument <A>digraph</A> may be a digraph or a list of lists of integers, floats or rationals.<P/>

<A>weights</A> must be a list of lists of integers, floats or rationals
of an equal size and shape to <C>OutNeighbours(digraph)</C>, otherwise it will fail.<P/>
mtorpey marked this conversation as resolved.
Show resolved Hide resolved

This will create a digraph and set the EdgeWeights to <A>weights</A>.<P/>

See <Ref Attr="EdgeWeights"/>.
<Example><![CDATA[
gap> g := EdgeWeightedDigraph(Digraph([[2], [1]]), [[5], [15]]);
<immutable digraph with 2 vertices, 2 edges>
gap> g := EdgeWeightedDigraph([[2], [1]], [[5], [15]]);
<immutable digraph with 2 vertices, 2 edges>
RaiyanC marked this conversation as resolved.
Show resolved Hide resolved
gap> EdgeWeights(g);
[ [ 5 ], [ 15 ] ]
]]></Example>
</Description>
</ManSection>
<#/GAPDoc>
5 changes: 5 additions & 0 deletions doc/z-chap5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<#Include Label="IsTransitiveDigraph">
</Section>

<Section><Heading>Edge Weights</Heading>
<#Include Label="EdgeWeights">
<#Include Label="EdgeWeightedDigraph">
</Section>

<Section><Heading>Orders</Heading>
<#Include Label="IsPreorderDigraph">
<#Include Label="IsPartialOrderDigraph">
Expand Down
43 changes: 43 additions & 0 deletions gap/digraph.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,11 @@ InstallMethod(RandomDigraphCons,
[IsConnectedDigraph, IsInt, IsRat],
{filt, n, p} -> RandomDigraphCons(IsConnectedDigraph, 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 Expand Up @@ -1631,6 +1636,44 @@ function(filt, n, p)
return DigraphNC(adjacencyList);
end);

InstallMethod(RandomDigraphCons,
"for IsStronglyConnectedDigraph, a positive integer, and a float",
[IsStronglyConnectedDigraph, IsPosInt, IsFloat],
function(filt, n, p)
local d, adjMatrix, stronglyConnectedComponents,
scc_a, scc_b, i, random_u, random_v;

d := RandomDigraph(n, p);

stronglyConnectedComponents := DigraphStronglyConnectedComponents(d);

adjMatrix := AdjacencyMatrixMutableCopy(d);

for i in [1 .. Size(stronglyConnectedComponents.comps) - 1] do
scc_a := stronglyConnectedComponents.comps[i];
scc_b := stronglyConnectedComponents.comps[i + 1];

# add a connection from u to v
random_u := Random(scc_a);
random_v := Random(scc_b);

adjMatrix[random_u][random_v] := 1;
od;

# connect end scc to first scc
scc_a := stronglyConnectedComponents.comps[
Size(stronglyConnectedComponents.comps)];
scc_b := stronglyConnectedComponents.comps[1];

# add a connection from u to v
random_u := Random(scc_a);
random_v := Random(scc_b);

adjMatrix[random_u][random_v] := 1;

return DigraphByAdjacencyMatrix(adjMatrix);
end);

InstallMethod(RandomDigraphCons,
"for IsAcyclicDigraph, a positive integer, and a float",
[IsAcyclicDigraph, IsPosInt, IsFloat],
Expand Down
3 changes: 2 additions & 1 deletion gap/doc.g
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ BindGlobal("DIGRAPHS_DocXMLFiles",
"orbits.xml",
"planar.xml",
"prop.xml",
"utils.xml"]);
"utils.xml",
"weights.xml"]);

BindGlobal("DIGRAPHS_MakeDoc",
function(pkgdir)
Expand Down
17 changes: 17 additions & 0 deletions gap/weights.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#############################################################################
##
## weights.gd
## Copyright (C) 2023 Raiyan Chowdhury
##
## Licensing information can be found in the README file of this package.
##
#############################################################################
##

# 1. Edge Weights
DeclareAttribute("EdgeWeights", IsDigraph);
DeclareGlobalFunction("EdgeWeightedDigraph");
DeclareProperty("IsNegativeEdgeWeightedDigraph", IsDigraph and HasEdgeWeights);
RaiyanC marked this conversation as resolved.
Show resolved Hide resolved

# 2. Edge Weight Copies
DeclareOperation("EdgeWeightsMutableCopy", [IsDigraph and HasEdgeWeights]);
93 changes: 93 additions & 0 deletions gap/weights.gi
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#############################################################################
##
## weights.gi
## Copyright (C) 2023 Raiyan Chowdhury
##
## Licensing information can be found in the README file of this package.
##
#############################################################################
##

#############################################################################
# 1. Edge Weights
#############################################################################

InstallGlobalFunction(EdgeWeightedDigraph,
function(digraph, weights)
local digraphVertices, nrVertices, u, outNeighbours,
outNeighbourWeights, idx;

if IsDigraph(digraph) then
digraph := DigraphCopy(digraph);
else
digraph := Digraph(digraph);
fi;

# check all elements of weights is a list
if not ForAll(weights, IsListOrCollection) then
ErrorNoReturn("the 2nd argument (list) must be a list of lists,");
fi;

digraphVertices := DigraphVertices(digraph);

nrVertices := Size(digraphVertices);

# check number there is an edge weight list for vertex u
if nrVertices <> Size(weights) then
ErrorNoReturn("the number of out neighbours and weights must be equal,");
fi;

# check all elements of weights is a list and size/shape is correct
for u in digraphVertices do
outNeighbours := OutNeighbors(digraph)[u];

outNeighbourWeights := weights[u];

# check number of out neighbours for u
# and number of weights given is the same
if Size(outNeighbours) <> Size(outNeighbourWeights) then
ErrorNoReturn(
"the sizes of the out neighbours and weights for vertex ",
u, " must be equal,");
RaiyanC marked this conversation as resolved.
Show resolved Hide resolved
fi;

# check all elements of out neighbours are appropriate
for idx in [1 .. Size(outNeighbours)] do

if not (IsInt(outNeighbourWeights[idx])
or IsFloat(outNeighbourWeights[idx])
or IsRat(outNeighbourWeights[idx])) then
ErrorNoReturn(
"out neighbour weight must be an integer, float or rational,");
fi;
od;
od;

SetEdgeWeights(digraph, weights);
return digraph;
end);

InstallMethod(IsNegativeEdgeWeightedDigraph, "for an edge weighted digraph",
[IsDigraph and HasEdgeWeights],
function(digraph)
local weights, u, w;

weights := EdgeWeights(digraph);

for u in weights do
for w in u do
if Float(w) < Float(0) then
return true;
fi;
od;
od;
return false;
end);

#############################################################################
# 2. Copies of edge weights
#############################################################################

InstallMethod(EdgeWeightsMutableCopy, "for a digraph with edge weights",
[IsDigraph and HasEdgeWeights],
D -> List(EdgeWeights(D), ShallowCopy));
1 change: 1 addition & 0 deletions init.g
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@ ReadPackage("digraphs", "gap/orbits.gd");
ReadPackage("digraphs", "gap/cliques.gd");
ReadPackage("digraphs", "gap/planar.gd");
ReadPackage("digraphs", "gap/examples.gd");
ReadPackage("digraphs", "gap/weights.gd");

DeclareInfoClass("InfoDigraphs");
1 change: 1 addition & 0 deletions read.g
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ ReadPackage("digraphs", "gap/orbits.gi");
ReadPackage("digraphs", "gap/cliques.gi");
ReadPackage("digraphs", "gap/planar.gi");
ReadPackage("digraphs", "gap/examples.gi");
ReadPackage("digraphs", "gap/weights.gi");
9 changes: 9 additions & 0 deletions tst/standard/digraph.tst
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,15 @@ gap> graph := RandomDigraph(IsConnectedDigraph, 10);;
> Print("False");
> fi;

# Random Strongly Connected Digraph
gap> for n in [1 .. 20] do
> c := RandomDigraph(IsConnectedDigraph, n, 0);
> s := RandomDigraph(IsStronglyConnectedDigraph, n, 0);
> if DigraphNrEdges(c) > DigraphNrEdges(s) then
> Print("False");
> fi;
> od;

# Random Symmetric Digraph
gap> for n in [1 .. 20] do
> graph := RandomDigraph(IsSymmetricDigraph, n, 0);
Expand Down
90 changes: 90 additions & 0 deletions tst/standard/weights.tst
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@

#############################################################################
##
#W standard/weights.tst
#Y Copyright (C) 2023 Raiyan Chowdhury
##
##
## Licensing information can be found in the README file of this package.
##
#############################################################################
##

gap> START_TEST("Digraphs package: standard/weights.tst");
gap> LoadPackage("digraphs", false);;

#
gap> DIGRAPHS_StartTest();
gap> d := EdgeWeightedDigraph([[2], []], [[5], []]);
<immutable digraph with 2 vertices, 1 edge>

# create with Digraph
gap> d := EdgeWeightedDigraph(Digraph([[2], []]), [[5], []]);
<immutable digraph with 2 vertices, 1 edge>

# weight not valid
gap> d := EdgeWeightedDigraph([[2], []], [["a"], []]);
Error, out neighbour weight must be an integer, float or rational,

# check all elements of out neighbours are list
gap> d := EdgeWeightedDigraph(["a", []], [[5], []]);
Error, the argument <list> must be a list of lists of positive integers not ex\
ceeding the length of the argument,

# check all elements of weights are list
gap> d := EdgeWeightedDigraph([[1], []], [5, []]);
Error, the 2nd argument (list) must be a list of lists,

# string for digraphs
gap> d := EdgeWeightedDigraph([["a"], []], [[2], []]);
Error, the argument <list> must be a list of lists of positive integers not ex\
ceeding the length of the argument,

# incorrect digraph and weights
gap> d := EdgeWeightedDigraph([[2]], [[5], []]);
Error, the argument <list> must be a list of lists of positive integers not ex\
ceeding the length of the argument,

# incorrect digraph and weights
gap> d := EdgeWeightedDigraph([[2], []], [[5]]);
Error, the number of out neighbours and weights must be equal,

# incorrect digraph and weights
gap> d := EdgeWeightedDigraph([[2, 2], []], [[5], []]);
Error, the sizes of the out neighbours and weights for vertex 1 must be equal,

# incorrect digraph and weights
gap> d := EdgeWeightedDigraph([[2], []], [[5, 10], []]);
Error, the sizes of the out neighbours and weights for vertex 1 must be equal,

# changing edge weights mutable copy
gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);
<immutable digraph with 2 vertices, 2 edges>
gap> m := EdgeWeightsMutableCopy(d);
RaiyanC marked this conversation as resolved.
Show resolved Hide resolved
[ [ 5 ], [ 10 ] ]
gap> m[1] := [25];
[ 25 ]
gap> m;
[ [ 25 ], [ 10 ] ]
gap> m[2][1] := 30;
30
gap> m;
[ [ 25 ], [ 30 ] ]
gap> m := EdgeWeights(d);
[ [ 5 ], [ 10 ] ]
gap> m[1] := [25];
Error, List Assignment: <list> must be a mutable list

# negative edge weights
gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);
<immutable digraph with 2 vertices, 2 edges>
gap> IsNegativeEdgeWeightedDigraph(d);
false
gap> d := EdgeWeightedDigraph([[2], [1]], [[-5], [10]]);
<immutable digraph with 2 vertices, 2 edges>
gap> IsNegativeEdgeWeightedDigraph(d);
true

#
gap> DIGRAPHS_StopTest();
gap> STOP_TEST("Digraphs package: standard/weights.tst", 0);
4 changes: 4 additions & 0 deletions tst/testinstall.tst
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ gap> String(D);
"DigraphFromDigraph6String(\"&CECG\")"
gap> String(CycleDigraph(4));
"CycleDigraph(4)"
gap> d := EdgeWeightedDigraph([[2], [1]], [[5], [10]]);
<immutable digraph with 2 vertices, 2 edges>
gap> EdgeWeights(d);
[ [ 5 ], [ 10 ] ]

# DIGRAPHS_UnbindVariables
gap> Unbind(gr2);
Expand Down
Loading