From 23a6c7d662f54517e49a8f6a0d7a240ce820880c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 1 Oct 2026 22:56:49 +0200 Subject: [PATCH 1/3] Load NautyTracesInterface support as an extension DIGRAPHS_NautyAvailable and NAUTY_DATA were fixed when Digraphs was loaded, depending on whether NautyTracesInterface was marked for loading at that point. Loading NautyTracesInterface afterwards left nauty unusable, e.g. NautyAutomorphismGroup returned fail. Start with nauty unavailable and let a package extension, which GAP reads as soon as both packages are loaded, install NAUTY_DATA and set DIGRAPHS_NautyAvailable. Extensions require GAP 4.13, so drop GAP 4.11 and 4.12 from CI. Assisted-by: Claude Code (Opus 5.5) --- .github/workflows/ubuntu.yml | 6 ----- PackageInfo.g | 6 ++++- README.md | 2 +- gap/isomorph.gi | 35 +++-------------------------- gap/nauty.g | 43 ++++++++++++++++++++++++++++++++++++ read.g | 11 ++------- 6 files changed, 54 insertions(+), 49 deletions(-) create mode 100644 gap/nauty.g diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3205a3e3d..7bf4330a6 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -29,7 +29,6 @@ jobs: - stable-4.15 - v4.14 - v4.13 - - v4.12 mode: - onlyneeded - default @@ -41,11 +40,6 @@ jobs: pkgs-to-clone: https://github.com/digraphs/graphviz.git https://github.com/gap-packages/NautyTracesInterface.git pkgs-to-build: io orb datastructures profiling grape NautyTracesInterface - - gap-version: v4.11 # Note: NautyTracesInterface requires GAP 4.12 - mode: default # There wouldn't be much point testing GAP v4.11 with only needed packages. The only difference is whether Grape is loaded. - pkgs-to-clone: https://github.com/digraphs/graphviz.git - pkgs-to-build: io* orb* datastructures* profiling* grape* - steps: - uses: actions/checkout@v7 - name: Output g++ version . . . diff --git a/PackageInfo.g b/PackageInfo.g index 576909014..2390e0ec4 100644 --- a/PackageInfo.g +++ b/PackageInfo.g @@ -568,7 +568,7 @@ PackageDoc := rec( ), Dependencies := rec( - GAP := ">=4.11.0", + GAP := ">=4.13.0", NeededOtherPackages := [["IO", ">=4.5.1"], ["orb", ">=4.8.2"], ["datastructures", ">=0.2.5"]], @@ -607,6 +607,10 @@ BannerString := Concatenation( "-----------------------------------------------------------------------", "------\n"), +Extensions := [ + rec(needed := [["NautyTracesInterface", ">=0.2"]], + filename := "gap/nauty.g")], + AvailabilityTest := function() local digraphs_so; diff --git a/README.md b/README.md index ac6707a7b..d7775e822 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ For questions, remarks, suggestions, and issues please use the ## Installation It is assumed that you have a working copy of [GAP][] with version number -4.11.0 or higher. The most up-to-date version of GAP, and instructions on how +4.13.0 or higher. The most up-to-date version of GAP, and instructions on how to install it, can be obtained from the [main GAP webpage](https://www.gap-system.org). diff --git a/gap/isomorph.gi b/gap/isomorph.gi index 2062cb819..8d3f35fcf 100644 --- a/gap/isomorph.gi +++ b/gap/isomorph.gi @@ -130,38 +130,9 @@ end); BindGlobal("BLISS_DATA_NO_COLORS", D -> BLISS_DATA(D, fail, fail)); -if DIGRAPHS_NautyAvailable then - BindGlobal("NAUTY_DATA", - function(D, colors) - local data; - if colors <> false then - colors := DIGRAPHS_ValidateVertexColouring(DigraphNrVertices(D), - colors); - colors := NautyColorData(colors); - fi; - if DigraphHasNoVertices(D) then - # This circumvents Issue #17 in NautyTracesInterface, whereby a graph - # with 0 vertices causes a seg fault. - return [Group(()), ()]; - fi; - data := NautyDense(DigraphSource(D), - DigraphRange(D), - DigraphNrVertices(D), - not IsSymmetricDigraph(D), - colors); - if IsEmpty(data[1]) then - data[1] := [()]; - fi; - data[1] := Group(data[1]); - data[2] := data[2] ^ -1; - return data; - end); - - BindGlobal("NAUTY_DATA_NO_COLORS", D -> NAUTY_DATA(D, false)); -else - BindGlobal("NAUTY_DATA", ReturnFail); - BindGlobal("NAUTY_DATA_NO_COLORS", ReturnFail); -fi; +# replaced by gap/nauty.g once NautyTracesInterface is loaded +BindGlobal("NAUTY_DATA", ReturnFail); +BindGlobal("NAUTY_DATA_NO_COLORS", D -> NAUTY_DATA(D, false)); # Canonical labellings diff --git a/gap/nauty.g b/gap/nauty.g new file mode 100644 index 000000000..45d6000ee --- /dev/null +++ b/gap/nauty.g @@ -0,0 +1,43 @@ +############################################################################# +## +## nauty.g +## Copyright (C) 2014-19 James D. Mitchell +## Wilf A. Wilson +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# Package extension, read by GAP once NautyTracesInterface is loaded. + +MakeReadWriteGlobal("NAUTY_DATA"); +NAUTY_DATA := function(D, colors) + local data; + if colors <> false then + colors := DIGRAPHS_ValidateVertexColouring(DigraphNrVertices(D), + colors); + colors := NautyColorData(colors); + fi; + if DigraphHasNoVertices(D) then + # This circumvents Issue #17 in NautyTracesInterface, whereby a graph + # with 0 vertices causes a seg fault. + return [Group(()), ()]; + fi; + data := NautyDense(DigraphSource(D), + DigraphRange(D), + DigraphNrVertices(D), + not IsSymmetricDigraph(D), + colors); + if IsEmpty(data[1]) then + data[1] := [()]; + fi; + data[1] := Group(data[1]); + data[2] := data[2] ^ -1; + return data; +end; +MakeReadOnlyGlobal("NAUTY_DATA"); + +MakeReadWriteGlobal("DIGRAPHS_NautyAvailable"); +DIGRAPHS_NautyAvailable := true; +MakeReadOnlyGlobal("DIGRAPHS_NautyAvailable"); diff --git a/read.g b/read.g index 40ae189c5..9755db9fa 100644 --- a/read.g +++ b/read.g @@ -20,15 +20,8 @@ function() return omit; end); -_NautyTracesInterfaceVersion := - First(PackageInfo("digraphs")[1].Dependencies.SuggestedOtherPackages, - x -> x[1] = "NautyTracesInterface")[2]; - -BindGlobal("DIGRAPHS_NautyAvailable", - IsPackageMarkedForLoading("NautyTracesInterface", - _NautyTracesInterfaceVersion)); - -Unbind(_NautyTracesInterfaceVersion); +# set to true by gap/nauty.g once NautyTracesInterface is loaded +BindGlobal("DIGRAPHS_NautyAvailable", false); ReadPackage("digraphs", "gap/utils.gi"); ReadPackage("digraphs", "gap/digraph.gi"); From 9a03da1be5edca6c4052b5e8b6c6f366b4ba4481 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 1 Oct 2026 22:56:57 +0200 Subject: [PATCH 2/3] Stop binding GRAPE names when GRAPE is absent Without GRAPE, Digraphs bound IsGraph, Vertices and Adjacency itself to avoid warnings about unbound globals. Loading GRAPE afterwards then printed warnings such as "BIND_GLOBAL: variable `Adjacency' already has a value". Look the GRAPE functions up at runtime instead. Assisted-by: Claude Code (Opus 5.5) --- gap/digraph.gi | 9 +++++---- init.g | 13 +++---------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/gap/digraph.gi b/gap/digraph.gi index d7a2169f2..337bfef4a 100644 --- a/gap/digraph.gi +++ b/gap/digraph.gi @@ -236,12 +236,13 @@ end); InstallMethod(DigraphCons, "for IsMutableDigraph and a record", [IsMutableDigraph, IsRecord], function(_, record) - local D, cmp, labels, i; + local D, cmp, labels, i, adj; - if IsGraph(record) then + if DIGRAPHS_IsGrapeGraph(record) then # IsGraph is a function not a filter, so we cannot have a separate method + adj := ValueGlobal("Adjacency"); D := DigraphNC(IsMutableDigraph, - List(Vertices(record), x -> Adjacency(record, x))); + List(ValueGlobal("Vertices")(record), x -> adj(record, x))); if IsBound(record.names) then SetDigraphVertexLabels(D, StructuralCopy(record.names)); fi; @@ -382,7 +383,7 @@ InstallMethod(DigraphCons, "for IsImmutableDigraph and a record", function(_, record) local D; D := MakeImmutable(DigraphCons(IsMutableDigraph, record)); - if IsGraph(record) then + if DIGRAPHS_IsGrapeGraph(record) then # IsGraph is a function not a filter, so we cannot have a separate method # for this. if not IsTrivial(record.group) then diff --git a/init.g b/init.g index 20ed2f3dd..50bb128b4 100644 --- a/init.g +++ b/init.g @@ -35,16 +35,9 @@ fi; BindGlobal("DIGRAPHS_IsGrapeLoaded", {} -> IsPackageMarkedForLoading("grape", "4.8.1")); -# To avoid warnings when GRAPE is not loaded -if not IsBound(IsGraph) then - IsGraph := ReturnFalse; -fi; -if not IsBound(Vertices) then - DeclareOperation("Vertices", [IsRecord]); -fi; -if not IsBound(Adjacency) then - Adjacency := IdFunc; -fi; +# IsGraph belongs to GRAPE, which need not be loaded +BindGlobal("DIGRAPHS_IsGrapeGraph", + x -> DIGRAPHS_IsGrapeLoaded() and ValueGlobal("IsGraph")(x)); ReadPackage("digraphs", "gap/digraph.gd"); ReadPackage("digraphs", "gap/digraphs.g"); From 5158fa1b1c0b96535924190a73dd8c870ff7b15c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 1 Oct 2026 22:56:57 +0200 Subject: [PATCH 3/3] Remove code for GAP versions before 4.13 Assisted-by: Claude Code (Opus 5.5) --- PackageInfo.g | 32 +++++--------------------------- gap/oper.gi | 11 ++--------- init.g | 24 ++---------------------- 3 files changed, 9 insertions(+), 58 deletions(-) diff --git a/PackageInfo.g b/PackageInfo.g index 2390e0ec4..4d669264e 100644 --- a/PackageInfo.g +++ b/PackageInfo.g @@ -31,10 +31,6 @@ _STANDREWSMATHS := Concatenation(["Mathematical Institute, North Haugh, ", _STANDREWSCS := Concatenation(["Jack Cole Building, North Haugh, ", "St Andrews, Fife, KY16 9SX, Scotland"]); -if not CompareVersionNumbers(GAPInfo.Version, "4.12") then - IsKernelExtensionAvailable := fail; -fi; - SetPackageInfo(rec( PackageName := "Digraphs", Subtitle := "Graphs, digraphs, and multidigraphs in GAP", @@ -612,25 +608,11 @@ Extensions := [ filename := "gap/nauty.g")], AvailabilityTest := function() - local digraphs_so; - - if CompareVersionNumbers(GAPInfo.Version, "4.12") then - if not IsKernelExtensionAvailable("digraphs") then - LogPackageLoadingMessage(PACKAGE_WARNING, - ["the kernel module is not compiled, ", - "the package cannot be loaded."]); - return fail; - fi; - else - # TODO this clause can be removed once Digraphs requires GAP>=4.12.1 - digraphs_so := Filename(DirectoriesPackagePrograms("digraphs"), - "digraphs.so"); - if (not "digraphs" in SHOW_STAT()) and digraphs_so = fail then - LogPackageLoadingMessage(PACKAGE_WARNING, - ["the kernel module is not compiled, ", - "the package cannot be loaded."]); - return fail; - fi; + if not IsKernelExtensionAvailable("digraphs") then + LogPackageLoadingMessage(PACKAGE_WARNING, + ["the kernel module is not compiled, ", + "the package cannot be loaded."]); + return fail; fi; return true; end, @@ -669,10 +651,6 @@ AutoDoc := rec( "&Digraphs;", "Digraphs"), "&GAP;", "GAP"))); -if not CompareVersionNumbers(GAPInfo.Version, "4.12") then - Unbind(IsKernelExtensionAvailable); -fi; - MakeReadWriteGlobal("_RecogsFunnyWWWURLFunction"); MakeReadWriteGlobal("_RecogsFunnyNameFormatterFunction"); Unbind(_RecogsFunnyWWWURLFunction); diff --git a/gap/oper.gi b/gap/oper.gi index bdc462bee..805cb5b9f 100644 --- a/gap/oper.gi +++ b/gap/oper.gi @@ -2548,7 +2548,7 @@ InstallMethod(DigraphCycleBasis, "for a digraph", [IsDigraph], function(G) local OutNbr, InNbr, n, partialSum, m, visited, unusedEdges, i, c, s, stack, - z, u, v, p, B; + u, v, p, B; # Check for loops if DigraphHasLoops(G) then @@ -2631,14 +2631,7 @@ function(G) # - unusedEdges : The list of unused edges to be converted to a basis vector # - visited : The result of the depth first search above - # TODO : In the case the Digraph package requires GAP 4.12 or over, - # remove the following if statement. - if CompareVersionNumbers(GAPInfo.Version, "4.12") then - B := List([1 .. c], i -> NewZeroVector(IsGF2VectorRep, GF(2), m)); - else - z := List([1 .. m], i -> Zero(GF(2))); - B := List([1 .. c], i -> Vector(GF(2), z)); - fi; + B := List([1 .. c], i -> NewZeroVector(IsGF2VectorRep, GF(2), m)); for i in [1 .. c] do u := unusedEdges[i][1]; diff --git a/init.g b/init.g index 50bb128b4..236ac5bd3 100644 --- a/init.g +++ b/init.g @@ -8,28 +8,8 @@ ############################################################################# ## -# load a method for UserHomeExpand if necessary -if not IsBound(UserHomeExpand) then - BindGlobal("UserHomeExpand", USER_HOME_EXPAND); -fi; - -if CompareVersionNumbers(GAPInfo.Version, "4.12") then - if not LoadKernelExtension("digraphs") then - Error("failed to load the Digraphs package kernel extension"); - fi; -else - # TODO this clause can be removed once Digraphs requires GAP>=4.12.1 - # load kernel function if it is installed: - if not IsBound(DIGRAPH_OUT_NBS) and "digraphs" in SHOW_STAT() then - # try static module - LoadStaticModule("digraphs"); - fi; - if not IsBound(DIGRAPH_OUT_NBS) and - Filename(DirectoriesPackagePrograms("digraphs"), - "digraphs.so") <> fail then - LoadDynamicModule(Filename(DirectoriesPackagePrograms("digraphs"), - "digraphs.so")); - fi; +if not LoadKernelExtension("digraphs") then + Error("failed to load the Digraphs package kernel extension"); fi; BindGlobal("DIGRAPHS_IsGrapeLoaded",