From 57f3d33c904f9b7bc1673460dff7eec3c77536eb Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Wed, 8 Oct 2025 17:28:34 +0100 Subject: [PATCH 1/4] Add string to PartitionDSFamily declaration This, for example, allows the family to be printed, and so the record UF can also be printed. --- gap/union-find.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gap/union-find.gd b/gap/union-find.gd index acecc3f..a96167f 100644 --- a/gap/union-find.gd +++ b/gap/union-find.gd @@ -28,7 +28,7 @@ DeclareCategory("IsPartitionDS", IsObject); #! @Description #! Family containing all partition data structures -BindGlobal("PartitionDSFamily", NewFamily(IsPartitionDS)); +BindGlobal("PartitionDSFamily", NewFamily("PartitionDSFamily", IsPartitionDS)); # # Constructors. Given an integer return the trivial partition (n parts of size 1) From 8d7ebcaee6f365dad7328abbe7e57f92bbb85990 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Wed, 8 Oct 2025 17:29:28 +0100 Subject: [PATCH 2/4] Remove redundant code from union-find.gi The package is not loadable if the kernel extension is not compiled, so the code removed in this PR is never used. Perhaps it is a remnant of an older version of the package which was usable with the kernel extension not compiled. --- gap/union-find.gi | 78 +++++++---------------------------------------- tst/uf.tst | 24 +++++++-------- 2 files changed, 23 insertions(+), 79 deletions(-) diff --git a/gap/union-find.gi b/gap/union-find.gi index 5161b83..ede4848 100644 --- a/gap/union-find.gi +++ b/gap/union-find.gi @@ -67,75 +67,19 @@ InstallMethod(PartitionDS, [IsPartitionDSRep and IsPartitionDS and IsMutable, Is end); - -UF.RepresentativeTarjan := - function(uf, x) - local gp, sp, p, y, z; - gp := UF.getParent; - sp := UF.setParent; - p := uf!.data; - while true do - y := gp(p[x]); - if y = x then - return x; - fi; - z := gp(p[y]); - if y = z then - return y; - fi; - p[x] := sp(p[x],z); - x := z; - od; +UF.RepresentativeKernel := function(uf, x) + return DS_UF_FIND(x, uf!.data); end; - -if IsBound(DS_UF_FIND) then - UF.RepresentativeKernel := function(uf, x) - return DS_UF_FIND(x, uf!.data); - end; - InstallMethod(Representative, [IsPartitionDSRep and IsPartitionDS, IsPosInt], - UF.RepresentativeKernel); -else - InstallMethod(Representative, [IsPartitionDSRep and IsPartitionDS, IsPosInt], - UF.RepresentativeTarjan); -fi; - -UF.UniteGAP := function(uf, x, y) - local r, rx, ry; - x := Representative(uf, x); - y := Representative(uf, y); - if x = y then - return; +InstallMethod(Representative, [IsPartitionDSRep and IsPartitionDS, IsPosInt], + UF.RepresentativeKernel); + +InstallMethod(Unite, [IsPartitionDSRep and IsMutable and IsPartitionDS, + IsPosInt, IsPosInt], + function(uf, x, y) + if DS_UF_UNITE(x, y, uf!.data) then + uf!.nparts := uf!.nparts -1; fi; - r := uf!.data; - rx := UF.getRank(r[x]); - ry := UF.getRank(r[y]); - if rx > ry then - r[y] := UF.setParent(r[y],x); - elif ry > rx then - r[x] := UF.setParent(r[x],y); - else - r[x] := UF.setParent(r[x],y); - r[y] := UF.setRank(r[y],ry+1); - fi; - uf!.nparts := uf!.nparts -1; - return; -end; - - -if IsBound(DS_UF_UNITE) then - InstallMethod(Unite, [IsPartitionDSRep and IsMutable and IsPartitionDS, - IsPosInt, IsPosInt], - function(uf, x, y) - if DS_UF_UNITE(x, y, uf!.data) then - uf!.nparts := uf!.nparts -1; - fi; - end); -else - InstallMethod(Unite, [IsPartitionDSRep and IsMutable and IsPartitionDS, - IsPosInt, IsPosInt], - UF.UniteGAP); -fi; - +end); InstallMethod(\=, [IsPartitionDSRep and IsPartitionDS, IsPartitionDSRep and IsPartitionDS], IsIdenticalObj); diff --git a/tst/uf.tst b/tst/uf.tst index 41c9e85..0a310c9 100644 --- a/tst/uf.tst +++ b/tst/uf.tst @@ -65,9 +65,9 @@ gap> for x in i2 do Print(x,"\n"); od; 8 9 10 -gap> UF.RepresentativeTarjan(u,3); +gap> Representative(u,3); 2 -gap> UF.UniteGAP(u,4,5); +gap> Unite(u,4,5); gap> u; gap> PartitionDS(IsPartitionDS,[[2,1]]); @@ -91,23 +91,23 @@ gap> Representative(u,15); 16 gap> u := PartitionDS(IsPartitionDS, 16); -gap> for i in [1,3..15] do UF.UniteGAP(u,i, i+1); od; -gap> for i in [1,5..13] do UF.UniteGAP(u,i, i+2); od; -gap> for i in [1,9] do UF.UniteGAP(u,i, i+4); od; -gap> UF.UniteGAP(u,1,9); -gap> UF.RepresentativeTarjan(u,1); +gap> for i in [1,3..15] do Unite(u,i, i+1); od; +gap> for i in [1,5..13] do Unite(u,i, i+2); od; +gap> for i in [1,9] do Unite(u,i, i+4); od; +gap> Unite(u,1,9); +gap> Representative(u,1); 16 -gap> UF.RepresentativeTarjan(u,15); +gap> Representative(u,15); 16 gap> u := PartitionDS(IsPartitionDS, 16);; gap> Unite(u,1,2); gap> Unite(u,2,3); gap> Unite(u,4,2); gap> Unite(u,5,5); -gap> UF.UniteGAP(u,9,10); -gap> UF.UniteGAP(u,10,11); -gap> UF.UniteGAP(u,12,10); -gap> UF.UniteGAP(u,9,12); +gap> Unite(u,9,10); +gap> Unite(u,10,11); +gap> Unite(u,12,10); +gap> Unite(u,9,12); gap> PartsOfPartitionDS(u); [ [ 1, 2, 3, 4 ], [ 5 ], [ 6 ], [ 7 ], [ 8 ], [ 9, 10, 11, 12 ], [ 13 ], [ 14 ], [ 15 ], [ 16 ] ] From 1551555fadac82ce3f31631601c0138b81f324fa Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Wed, 8 Oct 2025 17:31:35 +0100 Subject: [PATCH 3/4] Replace constructors PartitionDS with operations The previous versions of PartitionDS seem to have been written imagining a future where there would be multiple representations of union-find data structures. However, at present there's only one, and so it's annoying to have to include the filter when constructing a PartitionDS. This does not change any of the existing behaviour, but allows PartitionDS to be called with a single argument: either the number of points, or the parts of a partition. --- gap/union-find.gd | 30 ++++++++++++++++++++++++++++-- gap/union-find.gi | 15 +++++++++++++-- tst/uf.tst | 16 ++++++++-------- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/gap/union-find.gd b/gap/union-find.gd index a96167f..bbfceee 100644 --- a/gap/union-find.gd +++ b/gap/union-find.gd @@ -38,11 +38,37 @@ BindGlobal("PartitionDSFamily", NewFamily("PartitionDSFamily", IsPartitionDS)); #! @Description #! Returns the trivial partition of the set [1..n]. #! @Arguments filter, n -DeclareConstructor("PartitionDS",[IsPartitionDS, IsPosInt]); +DeclareConstructor("PartitionDSCons",[IsPartitionDS, IsPosInt]); + #! @Description #! Returns the union find structure of partition. #! @Arguments filter, partition -DeclareConstructor("PartitionDS",[IsPartitionDS, IsCyclotomicCollColl]); +DeclareConstructor("PartitionDSCons",[IsPartitionDS, IsCyclotomicCollColl]); + +# +# Operations. Given an integer return the trivial partition (n parts of size 1) +# Given a list of disjoint sets, return that partition. Any points up to the maximum +# of any of the sets not included in a set are in singleton parts. +# +#! @Description +#! Returns the trivial partition of the set [1..n]. +#! @Arguments filter, n +DeclareOperation("PartitionDS",[IsFunction, IsPosInt]); + +#! @Description +#! Returns the trivial partition of the set [1..n]. +#! @Arguments n +DeclareOperation("PartitionDS",[IsPosInt]); + +#! @Description +#! Returns the union find structure of partition. +#! @Arguments filter, partition +DeclareOperation("PartitionDS",[IsFunction, IsCyclotomicCollColl]); + +#! @Description +#! Returns the union find structure of partition. +#! @Arguments partition +DeclareOperation("PartitionDS",[IsCyclotomicCollColl]); # # Key operations diff --git a/gap/union-find.gi b/gap/union-find.gi index ede4848..0575f62 100644 --- a/gap/union-find.gi +++ b/gap/union-find.gi @@ -25,7 +25,7 @@ UF.setRank := UF.Bitfields.setters[1]; UF.setParent := UF.Bitfields.setters[2]; -InstallMethod(PartitionDS, [IsPartitionDSRep and IsPartitionDS and IsMutable, IsPosInt], +InstallMethod(PartitionDSCons, [IsPartitionDSRep and IsPartitionDS and IsMutable, IsPosInt], function(filt, n) local r; r := rec(); @@ -37,7 +37,7 @@ InstallMethod(PartitionDS, [IsPartitionDSRep and IsPartitionDS and IsMutable, Is end); -InstallMethod(PartitionDS, [IsPartitionDSRep and IsPartitionDS and IsMutable, IsCyclotomicCollColl], +InstallMethod(PartitionDSCons, [IsPartitionDSRep and IsPartitionDS and IsMutable, IsCyclotomicCollColl], function(filt, parts) local r, n, seen, sp, sr, p, x; if not (ForAll(parts, IsSet) and @@ -66,6 +66,17 @@ InstallMethod(PartitionDS, [IsPartitionDSRep and IsPartitionDS and IsMutable, Is return r; end); +InstallMethod(PartitionDS, [IsPosInt], +n -> PartitionDSCons(IsPartitionDSRep, n)); + +InstallMethod(PartitionDS, [IsCyclotomicCollColl], +parts -> PartitionDSCons(IsPartitionDSRep, parts)); + +InstallMethod(PartitionDS, [IsFunction, IsPosInt], +{filt, n} -> PartitionDSCons(filt, n)); + +InstallMethod(PartitionDS, [IsFunction, IsCyclotomicCollColl], +{filt, parts} -> PartitionDSCons(filt, parts)); UF.RepresentativeKernel := function(uf, x) return DS_UF_FIND(x, uf!.data); diff --git a/tst/uf.tst b/tst/uf.tst index 0a310c9..0a6c652 100644 --- a/tst/uf.tst +++ b/tst/uf.tst @@ -5,7 +5,7 @@ gap> START_TEST("uf.tst"); # Test the union-find data structure # # -gap> u := PartitionDS(IsPartitionDS,10); +gap> u := PartitionDS(10); gap> NumberParts(u); 10 @@ -36,7 +36,7 @@ gap> Unite(u,1,3); gap> u; gap> Print(u,"\n"); -PartitionDS( IsPartitionDS, [ [ 1, 2, 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ], [ 8 ], \ +PartitionDS(IsPartitionDS, [ [ 1, 2, 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ], [ 8 ], \ [ 9 ], [ 10 ] ]) gap> PartitionDS( IsPartitionDS, [ [ 1, 2, 3 ], [ 4 ], [ 5 ], [ 6 ], [ 7 ], [ 8 ], > [ 9 ], [ 10 ] ]); @@ -70,16 +70,16 @@ gap> Representative(u,3); gap> Unite(u,4,5); gap> u; -gap> PartitionDS(IsPartitionDS,[[2,1]]); +gap> PartitionDS([[2,1]]); Error, PartitionDS: supplied partition must be a list of disjoint sets of posi\ tive integers -gap> PartitionDS(IsPartitionDS,[[-2,1]]); +gap> PartitionDS([[-2,1]]); Error, PartitionDS: supplied partition must be a list of disjoint sets of posi\ tive integers -gap> PartitionDS(IsPartitionDS,[[1,2,3],[3,4,5]]); +gap> PartitionDS([[1,2,3],[3,4,5]]); Error, PartitionDS: supplied partition must be a list of disjoint sets of posi\ tive integers -gap> u := PartitionDS(IsPartitionDS, 16); +gap> u := PartitionDS(16); gap> for i in [1,3..15] do Unite(u,i, i+1); od; gap> for i in [1,5..13] do Unite(u,i, i+2); od; @@ -89,7 +89,7 @@ gap> Representative(u,1); 16 gap> Representative(u,15); 16 -gap> u := PartitionDS(IsPartitionDS, 16); +gap> u := PartitionDS(16); gap> for i in [1,3..15] do Unite(u,i, i+1); od; gap> for i in [1,5..13] do Unite(u,i, i+2); od; @@ -99,7 +99,7 @@ gap> Representative(u,1); 16 gap> Representative(u,15); 16 -gap> u := PartitionDS(IsPartitionDS, 16);; +gap> u := PartitionDS(16);; gap> Unite(u,1,2); gap> Unite(u,2,3); gap> Unite(u,4,2); From ce57e10cb2f4d14f369f1f408697f0bff265f7b8 Mon Sep 17 00:00:00 2001 From: James Mitchell Date: Wed, 8 Oct 2025 17:34:21 +0100 Subject: [PATCH 4/4] Add RootsOfPartitionDS There is already the IteratorOfRootsOfPartitionDS, but sometimes we just want the roots as a list. This operation is essentially the same as IteratorOfRootsOfPartitionDS but without the overhead of the iterator code. --- gap/union-find.gd | 7 +++++++ gap/union-find.gi | 20 ++++++++++++++++++++ tst/uf.tst | 4 ++++ 3 files changed, 31 insertions(+) diff --git a/gap/union-find.gd b/gap/union-find.gd index bbfceee..dfda1a2 100644 --- a/gap/union-find.gd +++ b/gap/union-find.gd @@ -95,6 +95,13 @@ DeclareOperation("Unite",[IsPartitionDS and IsMutable, IsPosInt, IsPosInt]); #! @Returns an iterator DeclareOperation("RootsIteratorOfPartitionDS", [IsPartitionDS]); +#! @Description +#! Returns a list of the canonical representatives of the parts +#! of the partition unionfind. +#! @Arguments unionfind +#! @Returns A list. +DeclareOperation("RootsOfPartitionDS", [IsPartitionDS]); + #! @Description #! Returns the number of parts of the partition unionfind. #! @Arguments unionfind diff --git a/gap/union-find.gi b/gap/union-find.gi index 0575f62..7007e25 100644 --- a/gap/union-find.gi +++ b/gap/union-find.gi @@ -148,6 +148,26 @@ InstallMethod(RootsIteratorOfPartitionDS, [IsPartitionDSRep and IsPartitionDS], end)); end); +InstallMethod(RootsOfPartitionDS, [IsPartitionDSRep and IsPartitionDS], +function(uf) + local pt, gp, data, n, result; + pt := 0; + gp := UF.getParent; + data := uf!.data; + n := SizeUnderlyingSetDS(uf); + result := []; + while pt <= n do + pt := pt + 1; + while pt <= n and gp(data[pt]) <> pt do + pt := pt + 1; + od; + if pt <= n then + Add(result, pt); + fi; + od; + return result; +end); + InstallMethod(PartsOfPartitionDS, [IsPartitionDS], function(u) local p, i, r, x; diff --git a/tst/uf.tst b/tst/uf.tst index 0a6c652..29a4a88 100644 --- a/tst/uf.tst +++ b/tst/uf.tst @@ -57,6 +57,8 @@ gap> for x in i do Print(x,"\n"); od; 8 9 10 +gap> RootsOfPartitionDS(u); +[ 2, 4, 5, 6, 7, 8, 9, 10 ] gap> for x in i2 do Print(x,"\n"); od; 4 5 @@ -111,6 +113,8 @@ gap> Unite(u,9,12); gap> PartsOfPartitionDS(u); [ [ 1, 2, 3, 4 ], [ 5 ], [ 6 ], [ 7 ], [ 8 ], [ 9, 10, 11, 12 ], [ 13 ], [ 14 ], [ 15 ], [ 16 ] ] +gap> RootsOfPartitionDS(u); +[ 2, 5, 6, 7, 8, 10, 13, 14, 15, 16 ] # gap> STOP_TEST( "uf.tst", 1);