From bed438e5567ec85a036791720b850c1e09b5ee5c Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 31 Mar 2026 16:20:46 +0200 Subject: [PATCH] Hash plain finite-field vectors and matrices Add a generic finite-field vector hash helper that computes the minimal field of the entries via BaseDomain and feeds NumberFFVector. Reuse it for non-compressed matrices by hashing rows with the existing matrix-list combiner while leaving compressed GF(2) and 8bit methods alone. Resolves #77 Co-authored-by: Codex --- CHANGES | 1 + doc/hash.xml | 42 +++++++++++++++++++++++++++----------- gap/hash.gd | 1 + gap/hash.gi | 55 ++++++++++++++++++++++++++++++++++++++------------ tst/bugfix.tst | 27 +++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 25 deletions(-) diff --git a/CHANGES b/CHANGES index 2a18ca9..92feebd 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ This file describes changes in the orb package. 5.x.y (2026-MM-DD) - Once again allow using this package without its kernel extension (this was accidentally broken in version 4.9.1) + - Add hash functions for non-compressed finite-field vectors and matrices 5.0.1 (2025-06-20) - Various janitorial changes diff --git a/doc/hash.xml b/doc/hash.xml index c239b37..da6a14a 100644 --- a/doc/hash.xml +++ b/doc/hash.xml @@ -89,9 +89,7 @@ Oper="ChooseHashFunction"/>. a record This method is for compressed vectors over the field GF(2) of -two elements. Note that there is no hash function for non-compressed -vectors over GF(2) because those objects cannot efficiently -be recognised from their type. +two elements.

Note that you can only use the resulting hash functions for vectors of the same length. @@ -103,9 +101,7 @@ of the same length. a record This method is for compressed vectors over a finite field with up -to 256 elements. Note that there is no hash function for -non-compressed such vectors because those objects cannot efficiently be -recognised from their type. +to 256 elements.

Note that you can only use the resulting hash functions for vectors of the same length. @@ -117,9 +113,7 @@ of the same length. a record This method is for compressed matrices over the field GF(2) of -two elements. Note that there is no hash function for non-compressed -matrices over GF(2) because those objects cannot efficiently -be recognised from their type. +two elements.

Note that you can only use the resulting hash functions for matrices of the same size. @@ -131,9 +125,33 @@ of the same size. a record This method is for compressed matrices over a finite field with up -to 256 elements. Note that there is no hash function for -non-compressed such vectors because those objects cannot efficiently be -recognised from their type. +to 256 elements. +

+Note that you can only use the resulting hash functions for matrices +of the same size. + + + + + + a record + +This method is for non-compressed vectors of finite field elements. +It uses the smallest field containing all entries of the vector, so +equal vectors involving entries from subfields get the same hash. +

+Note that you can only use the resulting hash functions for vectors +of the same length. + + + + + + a record + +This method is for non-compressed matrices of finite field elements. +It hashes the rows using the method for non-compressed vectors of +finite field elements.

Note that you can only use the resulting hash functions for matrices of the same size. diff --git a/gap/hash.gd b/gap/hash.gd index aa8f68c..73f6281 100644 --- a/gap/hash.gd +++ b/gap/hash.gd @@ -45,6 +45,7 @@ DeclareGlobalFunction( "ORB_HashFunctionForShortGF2Vectors" ); DeclareGlobalFunction( "ORB_HashFunctionForShort8BitVectors" ); DeclareGlobalFunction( "ORB_HashFunctionForGF2Vectors" ); DeclareGlobalFunction( "ORB_HashFunctionFor8BitVectors" ); +DeclareGlobalFunction( "ORB_HashFunctionForFFEVectors" ); DeclareGlobalFunction( "ORB_HashFunctionForCompressedMats" ); DeclareGlobalFunction( "ORB_HashFunctionForIntegers" ); DeclareGlobalFunction( "ORB_HashFunctionForMemory" ); diff --git a/gap/hash.gi b/gap/hash.gi index 3a6b474..c0a1db7 100644 --- a/gap/hash.gi +++ b/gap/hash.gi @@ -531,6 +531,20 @@ function(v,data) return HashKeyBag(v,101,3*GAPInfo.BytesPerVariable,data[2]) mod data[1] + 1; end ); +InstallGlobalFunction( ORB_HashFunctionForFFEVectors, +function(v,data) + local f,x; + if not IsRowVector(v) or not IsFFECollection(v) then + return fail; + fi; + f := BaseDomain(v); + x := NumberFFVector(v, Size(f)); + if x = fail then + return fail; + fi; + return x mod data[1] + 1; +end ); + InstallMethod( ChooseHashFunction, "failure method", [IsObject,IsInt], function(p,hashlen) @@ -540,7 +554,7 @@ InstallMethod( ChooseHashFunction, "failure method", # Now the choosing methods for compressed vectors: InstallMethod( ChooseHashFunction, "for compressed gf2 vectors", - [IsGF2VectorRep and IsList,IsInt], + [IsRowVector and IsFFECollection and IsGF2VectorRep,IsInt], function(p,hashlen) local bytelen; bytelen := QuoInt(Length(p),8); @@ -557,7 +571,7 @@ InstallMethod( ChooseHashFunction, "for compressed gf2 vectors", end ); InstallMethod( ChooseHashFunction, "for compressed 8bit vectors", - [Is8BitVectorRep and IsList,IsInt], + [IsRowVector and IsFFECollection and Is8BitVectorRep,IsInt], function(p,hashlen) local bytelen,i,q,qq; q := Q_VEC8BIT(p); @@ -595,7 +609,7 @@ function(x,data) end ); InstallMethod( ChooseHashFunction, "for compressed gf2 matrices", - [IsGF2MatrixRep and IsList,IsInt], + [IsMatrix and IsFFECollColl and IsGF2MatrixRep and IsList,IsInt], function(p,hashlen) local data; data := [hashlen,ChooseHashFunction(p[1],hashlen), @@ -605,7 +619,7 @@ InstallMethod( ChooseHashFunction, "for compressed gf2 matrices", end ); InstallMethod( ChooseHashFunction, "for compressed 8bit matrices", - [Is8BitMatrixRep and IsList,IsInt], + [IsMatrix and IsFFECollColl and Is8BitMatrixRep and IsList,IsInt], function(p,hashlen) local data,q; q := Q_VEC8BIT(p[1]); @@ -782,17 +796,32 @@ InstallMethod( ChooseHashFunction, "for lists of matrices", end ); InstallMethod( ChooseHashFunction, - "for finite field vectors over big finite fields", - [IsList, IsInt], + "for finite field vectors", + [IsRowVector and IsFFECollection, IsInt], function( l, hashlen ) - local f,q; - if NestingDepthA(l) = 1 and Length(l) > 0 and IsFFE(l[1]) then - f := Field(l); - q := Size(f); - return rec( func := ORB_HashFunctionForShort8BitVectors, - data := [hashlen,q] ); + if IsGF2VectorRep(l) or Is8BitVectorRep(l) then + TryNextMethod(); fi; - TryNextMethod(); + return rec( func := ORB_HashFunctionForFFEVectors, + data := [hashlen] ); + end ); + +InstallMethod( ChooseHashFunction, + "for finite field matrices", + [IsMatrix and IsFFECollColl, IsInt], + function( m, hashlen ) + local r; + if Length(m) = 0 then + r := rec( func := ORB_HashFunctionForFFEVectors, + data := [hashlen] ); + else + r := ChooseHashFunction( m[1], hashlen ); + if r = fail then + return fail; + fi; + fi; + return rec( func := ORB_HashFunctionForMatList, + data := [101,hashlen,r] ); end ); if IsBound(HASH_FUNC_FOR_PPERM) then diff --git a/tst/bugfix.tst b/tst/bugfix.tst index d674ad3..64e15c6 100644 --- a/tst/bugfix.tst +++ b/tst/bugfix.tst @@ -64,5 +64,32 @@ gap> hf.func(w, hf.data); gap> hf.func(v, hf.data); 446 +# verify plain finite field vectors and matrices can be hashed using the +# minimal field generated by their entries +gap> v := [Z(2)^0, 0*Z(2), Z(4)];; +gap> w := [Z(2^2)^0, 0*Z(2), Z(4)];; +gap> v = w; +true +gap> hf := ChooseHashFunction(v, 1009);; +gap> IsRecord(hf); +true +gap> hf.func(v, hf.data) = hf.func(w, hf.data); +true +gap> m := IdentityMat(2, GF(1024));; +gap> n := IdentityMat(2, GF(2));; +gap> m = n; +true +gap> hf := ChooseHashFunction(m, 1009);; +gap> IsRecord(hf); +true +gap> hf.func(m, hf.data) = hf.func(n, hf.data); +true +gap> m := [[Z(3)^0, 0*Z(3)], [0*Z(3), Z(3)^0]];; +gap> hf := ChooseHashFunction(m, 1009);; +gap> IsRecord(hf); +true +gap> IsInt(hf.func(m, hf.data)); +true + # gap> STOP_TEST("Orb package: bugfix.tst", 0);