From 4c380374c2313ec35e055225672493900a5229ce Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:10:17 +0100 Subject: [PATCH 1/8] Implements Digraph constructor from 2 lists --- gap/digraph.gd | 1 + gap/digraph.gi | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/gap/digraph.gd b/gap/digraph.gd index 46cb83d79..09f3f306c 100644 --- a/gap/digraph.gd +++ b/gap/digraph.gd @@ -70,6 +70,7 @@ DeclareOperation("Digraph", [IsList]); DeclareOperation("Digraph", [IsList, IsFunction]); DeclareOperation("Digraph", [IsInt, IsList, IsList]); DeclareOperation("Digraph", [IsList, IsList, IsList]); +DeclareOperation("Digraph", [IsList, IsList]); DeclareOperation("ListNamedDigraphs", [IsString]); DeclareOperation("ListNamedDigraphs", [IsString, IsPosInt]); diff --git a/gap/digraph.gi b/gap/digraph.gi index 337bfef4a..d91b9e5cc 100644 --- a/gap/digraph.gi +++ b/gap/digraph.gi @@ -480,6 +480,25 @@ function(name) return DigraphFromDiSparse6String(DIGRAPHS_NamedDigraphs.(name)); end); +InstallMethod(Digraph, "for a list and a list", [IsList, IsList], +function(vertex_labels, edges) + local D, nrverts; + + if not IsDuplicateFreeList(vertex_labels) then + ErrorNoReturn(" contains duplicate labels,"); + fi; + if not (IsSubset(vertex_labels, Union(edges))) then + ErrorNoReturn(" references a label that isn't in ,"); + fi; + + nrverts := Length(vertex_labels); + D := DigraphByEdges(List(edges, + x -> [Position(vertex_labels, x[1]), + Position(vertex_labels, x[2])]), nrverts); + SetDigraphVertexLabels(D, vertex_labels); + return D; +end); + InstallMethod(ListNamedDigraphs, "for a string and a pos int", [IsString, IsPosInt], From 0443d08064677fafc2d92a46cfe686998c79ccc0 Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:11:03 +0100 Subject: [PATCH 2/8] Add tests --- tst/standard/digraph.tst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tst/standard/digraph.tst b/tst/standard/digraph.tst index 4c458f29e..346b13df2 100644 --- a/tst/standard/digraph.tst +++ b/tst/standard/digraph.tst @@ -230,6 +230,19 @@ gap> if DIGRAPHS_IsGrapeLoaded() then > fi; gap> gr := Digraph(IsMutableDigraph, [1 .. 3], [3, 2, 1], [2, 3, 2]); +gap> Digraph(["a", "b"], [["a", "a"]]); + +gap> Digraph(["a", "b"], [["a", "a"], ["a", "a"]]); + +gap> D1 := Digraph([[1], -1, "abc", true], +> [[-1, [1]], ["abc", -1]]);; +gap> D2 := Digraph([[], [1], [2], []]);; +gap> IsIsomorphicDigraph(D1, D2); +true +gap> Digraph([1, 2], [[2, 3]]); +Error, references a label that isn't in , +gap> Digraph([1, 1], []); +Error, contains duplicate labels, # Digraph (by an integer and a function) gap> divides := function(a, b) From 8fc769f2f43bd9bbe2b2e91c79a4c096185849f6 Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:18:44 +0100 Subject: [PATCH 3/8] Add docs --- doc/digraph.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/digraph.xml b/doc/digraph.xml index 05e578e11..3a6c96772 100644 --- a/doc/digraph.xml +++ b/doc/digraph.xml @@ -132,6 +132,19 @@ edges from i to j. + for two lists + + if obj is a duplicate-free list, and source is a list of + pairs of elements in obj, then this function returns a digraph + with vertices [1 .. Length(obj)] and + Length(source) edges. For each i in + [1 .. Length(source)] there exists an edge given by the + pair at source[i].

+ + The vertices of the digraph will be labelled by the elements of + obj. + + for three lists if obj is a duplicate-free list, and source and @@ -231,6 +244,9 @@ gap> gr := Digraph([ > [3, 5, 7, 8, 10], [2, 5, 7], [3, 6, 7, 9, 10], [1, 4], > [1, 5, 9], [1, 2, 7, 8], [3, 5]]); +gap> gr := Digraph(["a", "b", "c", "d"], [["a", "b"], ["c", "b"]]); +gap> IsIsomorphicDigraph(Digraph([[2], [], [2], []]), gr); +true gap> gr := Digraph(["a", "b", "c"], ["a"], ["b"]); gap> gr := Digraph(5, [1, 2, 2, 4, 1, 1], [2, 3, 5, 5, 1, 1]); From 9b3e171fc559982a1149e3c4f513272fa6f4abe1 Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:19:19 +0100 Subject: [PATCH 4/8] Make nearby docs clearer --- doc/digraph.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/digraph.xml b/doc/digraph.xml index 3a6c96772..056dcafd1 100644 --- a/doc/digraph.xml +++ b/doc/digraph.xml @@ -260,9 +260,9 @@ gap> gr := Digraph([1 .. 10], ReturnTrue); gap> Digraph("Diamond"); ]]> - The next example illustrates the uses of the fourth and fifth variants of - this constructor. The resulting digraph is a strongly regular graph, and - it is actually the point graph of the van Lint-Schrijver partial + The next example illustrates the uses of the variants of this constructor + involving functions. The resulting digraph is a strongly regular graph, + and it is actually the point graph of the van Lint-Schrijver partial geometry, . The algebraic description is taken from the seminal paper of Calderbank and Kantor . From f079e0cd8a7f7265b081d20ce42bd8034d1aa74d Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:10:39 +0100 Subject: [PATCH 5/8] Fix incorrect doc example --- doc/digraph.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/digraph.xml b/doc/digraph.xml index 056dcafd1..1f85ec4b0 100644 --- a/doc/digraph.xml +++ b/doc/digraph.xml @@ -244,7 +244,7 @@ gap> gr := Digraph([ > [3, 5, 7, 8, 10], [2, 5, 7], [3, 6, 7, 9, 10], [1, 4], > [1, 5, 9], [1, 2, 7, 8], [3, 5]]); -gap> gr := Digraph(["a", "b", "c", "d"], [["a", "b"], ["c", "b"]]); +gap> gr := Digraph(["a", "b", "c", "d"], [["a", "b"], ["c", "b"]]);; gap> IsIsomorphicDigraph(Digraph([[2], [], [2], []]), gr); true gap> gr := Digraph(["a", "b", "c"], ["a"], ["b"]); From 4b52ecdc90f4e78585704349a48b23fec832f066 Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:09:16 +0100 Subject: [PATCH 6/8] More verbose errors --- gap/digraph.gi | 27 ++++++++++++++++++++++----- tst/standard/digraph.tst | 5 +++-- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/gap/digraph.gi b/gap/digraph.gi index d91b9e5cc..b5caaf2fd 100644 --- a/gap/digraph.gi +++ b/gap/digraph.gi @@ -482,13 +482,30 @@ end); InstallMethod(Digraph, "for a list and a list", [IsList, IsList], function(vertex_labels, edges) - local D, nrverts; + local label_counts, duplicates, badpos, labels, D, nrverts; - if not IsDuplicateFreeList(vertex_labels) then - ErrorNoReturn(" contains duplicate labels,"); + label_counts := Collected(vertex_labels); + duplicates := List(Filtered(label_counts, x -> x[2] > 1), x -> x[1]); + if not IsEmpty(duplicates) then + ErrorNoReturn(" contains duplicate labels: ", + duplicates, ","); fi; - if not (IsSubset(vertex_labels, Union(edges))) then - ErrorNoReturn(" references a label that isn't in ,"); + + # get first invalid edge - misformed + badpos := PositionProperty(edges, e -> not IsList(e) or Length(e) <> 2); + if badpos <> fail then + ErrorNoReturn("invalid edge ", edges[badpos], " in position ", badpos, + " in ; edges must be pairs of elements,"); + fi; + + # get first invalid edge - bad reference + labels := Set(vertex_labels); # set makes it faster to check containment + badpos := PositionProperty(edges, + e -> not (e[1] in labels and e[2] in labels)); + if badpos <> fail then + ErrorNoReturn("invalid edge ", edges[badpos], " in at position ", + badpos, "; edges must reference vertices from ", + vertex_labels, ","); fi; nrverts := Length(vertex_labels); diff --git a/tst/standard/digraph.tst b/tst/standard/digraph.tst index 346b13df2..6ad3348cc 100644 --- a/tst/standard/digraph.tst +++ b/tst/standard/digraph.tst @@ -240,9 +240,10 @@ gap> D2 := Digraph([[], [1], [2], []]);; gap> IsIsomorphicDigraph(D1, D2); true gap> Digraph([1, 2], [[2, 3]]); -Error, references a label that isn't in , +Error, invalid edge [ 2, 3 ] in at position +1; edges must reference vertices from [ 1, 2 ], gap> Digraph([1, 1], []); -Error, contains duplicate labels, +Error, contains duplicate labels: [ 1 ], # Digraph (by an integer and a function) gap> divides := function(a, b) From 0cc84b2c6e1255cdad26de608c2f72756844fb48 Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:36:19 +0100 Subject: [PATCH 7/8] Change Digraph to DigraphByEdges --- doc/digraph.xml | 33 +++++++++--------- gap/digraph.gd | 2 +- gap/digraph.gi | 72 ++++++++++++++++++++-------------------- tst/standard/digraph.tst | 28 ++++++++-------- 4 files changed, 67 insertions(+), 68 deletions(-) diff --git a/doc/digraph.xml b/doc/digraph.xml index 1f85ec4b0..faa5d8221 100644 --- a/doc/digraph.xml +++ b/doc/digraph.xml @@ -132,19 +132,6 @@ edges from i to j. - for two lists - - if obj is a duplicate-free list, and source is a list of - pairs of elements in obj, then this function returns a digraph - with vertices [1 .. Length(obj)] and - Length(source) edges. For each i in - [1 .. Length(source)] there exists an edge given by the - pair at source[i].

- - The vertices of the digraph will be labelled by the elements of - obj. - - for three lists if obj is a duplicate-free list, and source and @@ -244,9 +231,6 @@ gap> gr := Digraph([ > [3, 5, 7, 8, 10], [2, 5, 7], [3, 6, 7, 9, 10], [1, 4], > [1, 5, 9], [1, 2, 7, 8], [3, 5]]); -gap> gr := Digraph(["a", "b", "c", "d"], [["a", "b"], ["c", "b"]]);; -gap> IsIsomorphicDigraph(Digraph([[2], [], [2], []]), gr); -true gap> gr := Digraph(["a", "b", "c"], ["a"], ["b"]); gap> gr := Digraph(5, [1, 2, 2, 4, 1, 1], [2, 3, 5, 5, 1, 1]); @@ -339,6 +323,8 @@ gap> D := DigraphByAdjacencyMatrix(IsMutableDigraph, <#GAPDoc Label="DigraphByEdges"> + A digraph. &STANDARD_FILT_TEXT; @@ -352,8 +338,15 @@ gap> D := DigraphByAdjacencyMatrix(IsMutableDigraph, then this function returns the digraph with n vertices and list list.

- See . + If labels is a duplicate-free list, and edges is a list of + pairs of elements from labels, then this function returns a digraph + with vertices [1 .. Length(labels)] and + Length(edges) edges. For each i in + [1 .. Length(edges)] there exists an edge given by the + pair at edges[i]. The vertices of the digraph will be labelled by + the elements of labels.

+ See . DigraphByEdges( > [[1, 3], [2, 1], [2, 3], [2, 5], [3, 6], @@ -367,6 +360,12 @@ gap> DigraphByEdges(IsMutableDigraph, > [[1, 3], [2, 1], [2, 3], [2, 5], [3, 6], > [4, 6], [5, 2], [5, 4], [5, 6], [6, 6]], 12); +gap> gr := DigraphByEdges( +> ["a", "b", "x", "y"], +> [["a", "x"], ["x", "b"]]); + +gap> IsIsomorphicDigraph(Digraph([[3], [], [2], []]), gr); +true ]]> diff --git a/gap/digraph.gd b/gap/digraph.gd index 09f3f306c..32d77c71c 100644 --- a/gap/digraph.gd +++ b/gap/digraph.gd @@ -70,7 +70,6 @@ DeclareOperation("Digraph", [IsList]); DeclareOperation("Digraph", [IsList, IsFunction]); DeclareOperation("Digraph", [IsInt, IsList, IsList]); DeclareOperation("Digraph", [IsList, IsList, IsList]); -DeclareOperation("Digraph", [IsList, IsList]); DeclareOperation("ListNamedDigraphs", [IsString]); DeclareOperation("ListNamedDigraphs", [IsString, IsPosInt]); @@ -91,6 +90,7 @@ DeclareOperation("DigraphByEdges", [IsFunction, IsList]); DeclareOperation("DigraphByEdges", [IsFunction, IsList, IsInt]); DeclareOperation("DigraphByEdges", [IsList]); DeclareOperation("DigraphByEdges", [IsList, IsInt]); +DeclareOperation("DigraphByEdges", [IsList, IsList]); DeclareConstructor("DigraphByInNeighboursCons", [IsDigraph, IsList]); DeclareConstructor("DigraphByInNeighboursConsNC", [IsDigraph, IsList]); diff --git a/gap/digraph.gi b/gap/digraph.gi index b5caaf2fd..8946a5c74 100644 --- a/gap/digraph.gi +++ b/gap/digraph.gi @@ -480,42 +480,6 @@ function(name) return DigraphFromDiSparse6String(DIGRAPHS_NamedDigraphs.(name)); end); -InstallMethod(Digraph, "for a list and a list", [IsList, IsList], -function(vertex_labels, edges) - local label_counts, duplicates, badpos, labels, D, nrverts; - - label_counts := Collected(vertex_labels); - duplicates := List(Filtered(label_counts, x -> x[2] > 1), x -> x[1]); - if not IsEmpty(duplicates) then - ErrorNoReturn(" contains duplicate labels: ", - duplicates, ","); - fi; - - # get first invalid edge - misformed - badpos := PositionProperty(edges, e -> not IsList(e) or Length(e) <> 2); - if badpos <> fail then - ErrorNoReturn("invalid edge ", edges[badpos], " in position ", badpos, - " in ; edges must be pairs of elements,"); - fi; - - # get first invalid edge - bad reference - labels := Set(vertex_labels); # set makes it faster to check containment - badpos := PositionProperty(edges, - e -> not (e[1] in labels and e[2] in labels)); - if badpos <> fail then - ErrorNoReturn("invalid edge ", edges[badpos], " in at position ", - badpos, "; edges must reference vertices from ", - vertex_labels, ","); - fi; - - nrverts := Length(vertex_labels); - D := DigraphByEdges(List(edges, - x -> [Position(vertex_labels, x[1]), - Position(vertex_labels, x[2])]), nrverts); - SetDigraphVertexLabels(D, vertex_labels); - return D; -end); - InstallMethod(ListNamedDigraphs, "for a string and a pos int", [IsString, IsPosInt], @@ -1005,6 +969,42 @@ InstallMethod(DigraphByEdges, "for a function, a list, and an integer", InstallMethod(DigraphByEdges, "for a function and a list", [IsFunction, IsList], DigraphByEdgesCons); +InstallMethod(DigraphByEdges, "for a list and a list", [IsList, IsList], +function(vertex_labels, edges) + local label_counts, duplicates, badpos, labels, D, nrverts; + + label_counts := Collected(vertex_labels); + duplicates := List(Filtered(label_counts, x -> x[2] > 1), x -> x[1]); + if not IsEmpty(duplicates) then + ErrorNoReturn(" contains duplicate labels: ", + duplicates, ","); + fi; + + # get first invalid edge - misformed + badpos := PositionProperty(edges, e -> not IsList(e) or Length(e) <> 2); + if badpos <> fail then + ErrorNoReturn("invalid edge ", edges[badpos], " in position ", badpos, + " in ; edges must be pairs of elements,"); + fi; + + # get first invalid edge - bad reference + labels := Set(vertex_labels); # set makes it faster to check containment + badpos := PositionProperty(edges, + e -> not (e[1] in labels and e[2] in labels)); + if badpos <> fail then + ErrorNoReturn("invalid edge ", edges[badpos], " in at position ", + badpos, "; edges must reference vertices from ", + vertex_labels, ","); + fi; + + nrverts := Length(vertex_labels); + D := DigraphByEdges(List(edges, + x -> [Position(vertex_labels, x[1]), + Position(vertex_labels, x[2])]), nrverts); + SetDigraphVertexLabels(D, vertex_labels); + return D; +end); + InstallMethod(DigraphByInNeighboursCons, "for IsMutableDigraph, and a list", [IsMutableDigraph, IsList], function(_, list) diff --git a/tst/standard/digraph.tst b/tst/standard/digraph.tst index 6ad3348cc..2744a4a7e 100644 --- a/tst/standard/digraph.tst +++ b/tst/standard/digraph.tst @@ -230,20 +230,6 @@ gap> if DIGRAPHS_IsGrapeLoaded() then > fi; gap> gr := Digraph(IsMutableDigraph, [1 .. 3], [3, 2, 1], [2, 3, 2]); -gap> Digraph(["a", "b"], [["a", "a"]]); - -gap> Digraph(["a", "b"], [["a", "a"], ["a", "a"]]); - -gap> D1 := Digraph([[1], -1, "abc", true], -> [[-1, [1]], ["abc", -1]]);; -gap> D2 := Digraph([[], [1], [2], []]);; -gap> IsIsomorphicDigraph(D1, D2); -true -gap> Digraph([1, 2], [[2, 3]]); -Error, invalid edge [ 2, 3 ] in at position -1; edges must reference vertices from [ 1, 2 ], -gap> Digraph([1, 1], []); -Error, contains duplicate labels: [ 1 ], # Digraph (by an integer and a function) gap> divides := function(a, b) @@ -346,6 +332,20 @@ Error, the 1st argument (list of edges) must be pairs of positive integers but\ gap> DigraphByEdges([12], 10); Error, the 1st argument (list of edges) must be a list of lists, but found int\ eger in position 1 +gap> DigraphByEdges(["a", "b"], [["a", "a"]]); + +gap> DigraphByEdges(["a", "b"], [["a", "a"], ["a", "a"]]); + +gap> D1 := DigraphByEdges([[1], -1, "abc", true], +> [[-1, [1]], ["abc", -1]]);; +gap> D2 := Digraph([[], [1], [2], []]);; +gap> IsIsomorphicDigraph(D1, D2); +true +gap> DigraphByEdges([1, 2], [[2, 3]]); +Error, invalid edge [ 2, 3 ] in at position +1; edges must reference vertices from [ 1, 2 ], +gap> DigraphByEdges([1, 1, 2], [1, 2]); +Error, contains duplicate labels: [ 1 ], # DigraphByAdjacencyMatrix (by an integer matrix) From 65aa4d9a612714bd892d6425faf9ff6e1c7f381a Mon Sep 17 00:00:00 2001 From: William-Kenyon <115789274+William-Kenyon@users.noreply.github.com> Date: Fri, 16 Oct 2026 18:39:52 +0100 Subject: [PATCH 8/8] Codespell --- gap/digraph.gi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gap/digraph.gi b/gap/digraph.gi index 8946a5c74..09837e173 100644 --- a/gap/digraph.gi +++ b/gap/digraph.gi @@ -980,7 +980,7 @@ function(vertex_labels, edges) duplicates, ","); fi; - # get first invalid edge - misformed + # get first invalid edge - malformed badpos := PositionProperty(edges, e -> not IsList(e) or Length(e) <> 2); if badpos <> fail then ErrorNoReturn("invalid edge ", edges[badpos], " in position ", badpos,