From f3399ec4503cdab0458f6994a5fd93222fc4c8a1 Mon Sep 17 00:00:00 2001 From: Alia Bonnet Date: Sun, 17 May 2026 20:25:31 +0200 Subject: [PATCH 1/3] renamed PrimaryDecomp --- gap/nofoma.gd | 4 ++-- gap/nofoma.gi | 12 ++++++------ tst/primary.tst | 14 +++++++------- tst/representations.tst | 14 +++++++------- tst/utils.g | 12 ++++++------ 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/gap/nofoma.gd b/gap/nofoma.gd index ef092ea..062294f 100644 --- a/gap/nofoma.gd +++ b/gap/nofoma.gd @@ -358,7 +358,7 @@ DeclareGlobalFunction("JordanChevalleyDecMatF"); #! > [ Z(5), Z(5)^0, 0*Z(5), Z(5)^0, 0*Z(5) ], #! > [ Z(5)^0, Z(5)^0, Z(5)^0, 0*Z(5), Z(5)^3 ], #! > [ Z(5), 0*Z(5), Z(5)^3, 0*Z(5), Z(5)^3 ] ];; -#! gap> B := PrimaryDecomp(A);; +#! gap> B := PrimaryDecomposition(A);; #! gap> Display(B[2]); #! [ 1, 4 ] #! gap> Factors(MinimalPolynomial(A)); @@ -369,7 +369,7 @@ DeclareGlobalFunction("JordanChevalleyDecMatF"); #! gap> MinimalPolynomial(PrimA{[2..5]}{[2..5]}); #! x_1^4-x_1^3+Z(5)^3*x_1+Z(5)^3 #! @EndExampleSession -DeclareGlobalFunction("PrimaryDecomp"); +DeclareGlobalFunction("PrimaryDecomposition"); #! @Chapter Auxiliary functions #! @Section Vectors and matrices and their associated polynomials diff --git a/gap/nofoma.gi b/gap/nofoma.gi index 341e937..a3a441d 100644 --- a/gap/nofoma.gi +++ b/gap/nofoma.gi @@ -654,7 +654,7 @@ end); #Takes matrix along with its minimal polynomial and its factorised version as input #Returns matrix B such that B*A*B^-1 is in primary decomp. form, dimensions of primary subspaces #and factors of minimal polynomial in correct order -BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs) +BindGlobal("nfmPrimaryDecompositionositionforJNF", function(A, minpol, minpolfacs) local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v, COB,pot,gs,f2,toAdd,pos,dims,dim; rank := 0; @@ -734,7 +734,7 @@ end); #Primary Decomposition for cyclic matrices #Jordan normal form will call this function if a cyclic matrix is detected -BindGlobal("nfmPrimaryDecompositionforJNFCyclic", function(A, minpol, minpolfacs) +BindGlobal("nfmPrimaryDecompositionositionforJNFCyclic", function(A, minpol, minpolfacs) local vspan,n,w,mf,wspan,qi,k,COB,dims; n := NrRows(A); vspan := nfmFindCyclicVectorNC(A); @@ -757,7 +757,7 @@ end); #Standalone version #Returns matrix B such that B*A*B^-1 is in primary decomposition form #along with dimensions of primary subspaces -InstallGlobalFunction(PrimaryDecomp, function(A) +InstallGlobalFunction(PrimaryDecomposition, function(A) local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v, COB,pot,gs,f2,dims,toAdd,dim,minpol; rank := 0; @@ -766,7 +766,7 @@ InstallGlobalFunction(PrimaryDecomp, function(A) v := nfmGenerateNonZeroVector(n,A); minpol := MinimalPolynomial(F,A); if Degree(minpol) = n then - return nfmPrimaryDecompositionforJNFCyclic(A,minpol,Collected(Factors(minpol))); + return nfmPrimaryDecompositionositionforJNFCyclic(A,minpol,Collected(Factors(minpol))); fi; if IsZero(A) then return IdentityMat(n,F); @@ -1013,9 +1013,9 @@ InstallGlobalFunction(JordanNormalForm, function(A) return JordanNormalFormIrred(A,minpol); fi; if Degree(minpol) = n then - primary := nfmPrimaryDecompositionforJNFCyclic(A, minpol, facOcc); + primary := nfmPrimaryDecompositionositionforJNFCyclic(A, minpol, facOcc); else - primary := nfmPrimaryDecompositionforJNF(A, minpol, facOcc); + primary := nfmPrimaryDecompositionositionforJNF(A, minpol, facOcc); fi; primarydims := primary[2]; #dimensions of generalized eigenspaces COB := primary[1]; #Change of basis matrix, this will be the final COB from A to JNF diff --git a/tst/primary.tst b/tst/primary.tst index 201fd7a..a54a8dd 100644 --- a/tst/primary.tst +++ b/tst/primary.tst @@ -1,18 +1,18 @@ -gap> START_TEST("PrimaryDecomposition"); +gap> START_TEST("PrimaryDecompositionosition"); gap> ReadPackage("nofoma", "tst/utils.g"); true ##Test Primary decomposition -gap> nfmCheckPrimaryDecomp(GF(5),50); +gap> nfmCheckPrimaryDecomposition(GF(5),50); true -gap> nfmCheckPrimaryDecomp(GF(25),25); +gap> nfmCheckPrimaryDecomposition(GF(25),25); true -gap> nfmCheckPrimaryDecomp(GF(7),150); +gap> nfmCheckPrimaryDecomposition(GF(7),150); true -gap> nfmCheckPrimaryDecompNonCyclic(GF(5),50); +gap> nfmCheckPrimaryDecompositionNonCyclic(GF(5),50); true -gap> nfmCheckPrimaryDecompNonCyclic(GF(5^5),10); +gap> nfmCheckPrimaryDecompositionNonCyclic(GF(5^5),10); true ## Stop test -gap> STOP_TEST("PrimaryDecomposition"); +gap> STOP_TEST("PrimaryDecompositionosition"); diff --git a/tst/representations.tst b/tst/representations.tst index 6e1165d..21d8f95 100644 --- a/tst/representations.tst +++ b/tst/representations.tst @@ -58,19 +58,19 @@ gap> CheckJordanChev(bigm, JordanChevalleyDecMat(bigm, MinimalPolynomial(bigm))) [ Z(5)^0, x_1 ] ## Primary decomposition should work for all these matrix families -gap> nfmCheckPrimaryDecompForMatrix(rat); +gap> nfmCheckPrimaryDecompositionForMatrix(rat); true -gap> nfmCheckPrimaryDecompForMatrix(ratm); +gap> nfmCheckPrimaryDecompositionForMatrix(ratm); true -gap> nfmCheckPrimaryDecompForMatrix(smallplain); +gap> nfmCheckPrimaryDecompositionForMatrix(smallplain); true -gap> nfmCheckPrimaryDecompForMatrix(gf2); +gap> nfmCheckPrimaryDecompositionForMatrix(gf2); true -gap> nfmCheckPrimaryDecompForMatrix(gf9); +gap> nfmCheckPrimaryDecompositionForMatrix(gf9); true -gap> nfmCheckPrimaryDecompForMatrix(bigplain); +gap> nfmCheckPrimaryDecompositionForMatrix(bigplain); true -gap> nfmCheckPrimaryDecompForMatrix(bigm); +gap> nfmCheckPrimaryDecompositionForMatrix(bigm); true ## Jordan normal form should preserve the input matrix family diff --git a/tst/utils.g b/tst/utils.g index 944a957..5127475 100644 --- a/tst/utils.g +++ b/tst/utils.g @@ -57,10 +57,10 @@ end; #TODO: now that primdecomp has sorted blocks this can be tested more efficiently #check primary decomp function with field F and dimension n #checks if the submatrices have the correct minimal polynomials -nfmCheckPrimaryDecomp := function(F, n) +nfmCheckPrimaryDecomposition := function(F, n) local A,Prim,B,dim,k,minpolfacs,sub; A := Matrix(F,RandomInvertibleMat(n,F)); - Prim := PrimaryDecomp(A); + Prim := PrimaryDecomposition(A); B := A^Inverse(Prim[1]); minpolfacs := Factors(MinimalPolynomial(F,A)); k := 1; @@ -74,10 +74,10 @@ nfmCheckPrimaryDecomp := function(F, n) return true; end; -nfmCheckPrimaryDecompNonCyclic := function(F, n) +nfmCheckPrimaryDecompositionNonCyclic := function(F, n) local A,Prim,B,dim,k,minpolfacs,sub; A := Matrix(F,nfmGenerateNonCyclicMatrix(F,n)); - Prim := PrimaryDecomp(A); + Prim := PrimaryDecomposition(A); B := A^Inverse(Prim[1]); minpolfacs := Factors(MinimalPolynomial(F,A)); k := 1; @@ -108,9 +108,9 @@ nfmCheckInvariantFactorsForMatrix := function(A) return InvariantFactorsMat(A) = FrobeniusNormalForm(A)[1]; end; -nfmCheckPrimaryDecompForMatrix := function(A) +nfmCheckPrimaryDecompositionForMatrix := function(A) local Prim,B,dim,k,minpolfacs,sub,AA; - Prim := PrimaryDecomp(A); + Prim := PrimaryDecomposition(A); AA := Matrix(A, Prim[1]); B := AA^Inverse(Prim[1]); minpolfacs := Factors(MinimalPolynomial(AA)); From 154a49deef3b9ed4578b82136ffcf8df11ca5377 Mon Sep 17 00:00:00 2001 From: Alia Bonnet Date: Sun, 17 May 2026 20:59:26 +0200 Subject: [PATCH 2/3] reworked primarydecomp function --- gap/nofoma.gd | 11 ++++++----- gap/nofoma.gi | 22 +++++++++++++--------- tst/utils.g | 6 +++--- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/gap/nofoma.gd b/gap/nofoma.gd index 062294f..8429ef0 100644 --- a/gap/nofoma.gd +++ b/gap/nofoma.gd @@ -344,13 +344,14 @@ DeclareGlobalFunction("JordanChevalleyDecMatF"); #! @Section The primary decomposition #! @Arguments A #! @Description -#! Returns a list containing two elements. The first element is +#! Returns a list containing three elements. The first element is #! a base change matrix B such that BAB^{-1} is a #! primary form of A, i.e., a block diagonal matrix where the minimal polynomials #! of the the diagonal blocks are precisely the powers of irreducible factors -#! of the minimal polynomial of A. The second element is the size of each -#! block. -#! This function uses a modified version of Steel's algorithm. +#! of the minimal polynomial of A, in descending order. The second element is a list containing +#! the collected irreducible factors of the minimal polynomial of A, in the same order. The +#! last element is a list containing the the size of each block. +#! The exact algorithm used in this function is described in #! #! @BeginExampleSession #! gap> A := [ [ Z(5)^2, 0*Z(5), Z(5)^2, Z(5)^3, Z(5) ], @@ -359,7 +360,7 @@ DeclareGlobalFunction("JordanChevalleyDecMatF"); #! > [ Z(5)^0, Z(5)^0, Z(5)^0, 0*Z(5), Z(5)^3 ], #! > [ Z(5), 0*Z(5), Z(5)^3, 0*Z(5), Z(5)^3 ] ];; #! gap> B := PrimaryDecomposition(A);; -#! gap> Display(B[2]); +#! gap> Display(B[3]); #! [ 1, 4 ] #! gap> Factors(MinimalPolynomial(A)); #! [ x_1-Z(5)^0, x_1^4-x_1^3+Z(5)^3*x_1+Z(5)^3 ] diff --git a/gap/nofoma.gi b/gap/nofoma.gi index a3a441d..5df2d0d 100644 --- a/gap/nofoma.gi +++ b/gap/nofoma.gi @@ -654,7 +654,7 @@ end); #Takes matrix along with its minimal polynomial and its factorised version as input #Returns matrix B such that B*A*B^-1 is in primary decomp. form, dimensions of primary subspaces #and factors of minimal polynomial in correct order -BindGlobal("nfmPrimaryDecompositionositionforJNF", function(A, minpol, minpolfacs) +BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs) local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v, COB,pot,gs,f2,toAdd,pos,dims,dim; rank := 0; @@ -734,22 +734,24 @@ end); #Primary Decomposition for cyclic matrices #Jordan normal form will call this function if a cyclic matrix is detected -BindGlobal("nfmPrimaryDecompositionositionforJNFCyclic", function(A, minpol, minpolfacs) +BindGlobal("nfmPrimaryDecompositionforJNFCyclic", function(A, minpol, minpolfacs) local vspan,n,w,mf,wspan,qi,k,COB,dims; n := NrRows(A); vspan := nfmFindCyclicVectorNC(A); dims := []; COB := ZeroMutable(A); k := 0; + elDivs := []; for mf in minpolfacs do qi := (mf[1])^(mf[2]); + Add(elDivs, qi); w := nfmPolyEvalFromSpan(vspan,Quotient(minpol,qi)); wspan := nfmSpinUntil(w,A,Degree(mf[1])*mf[2]); CopySubMatrix(wspan, COB, [1..NrRows(wspan)], [k+1..k+NrRows(wspan)], [1..n], [1..n]); Add(dims, NrRows(wspan)); k := k + NrRows(wspan); od; - return [COB, dims]; + return [COB, elDivs, dims]; end); #TODO: recognise cyclic matrices @@ -759,14 +761,15 @@ end); #along with dimensions of primary subspaces InstallGlobalFunction(PrimaryDecomposition, function(A) local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v, - COB,pot,gs,f2,dims,toAdd,dim,minpol; + COB,pot,gs,f2,dims,toAdd,dim,minpol,collected; rank := 0; n := NrRows(A); F := DefaultFieldOfMatrix(A); v := nfmGenerateNonZeroVector(n,A); minpol := MinimalPolynomial(F,A); + collected := Collected(Factors(minpol)); if Degree(minpol) = n then - return nfmPrimaryDecompositionositionforJNFCyclic(A,minpol,Collected(Factors(minpol))); + return nfmPrimaryDecompositionforJNFCyclic(A,minpol,collected); fi; if IsZero(A) then return IdentityMat(n,F); @@ -838,7 +841,7 @@ InstallGlobalFunction(PrimaryDecomposition, function(A) CopySubMatrix(L_i, COB, [1..dim], [k+1..k+dim],[1..n], [1..n]); k := k + dim; od; - return [COB, dims]; + return [COB, collected, dims]; end); #Input: For matrix A with minimal polynomial p^m: m, vector v and p(A) @@ -999,7 +1002,7 @@ end); #Input: Matrix A #Returns matrix B such that A^Inverse(B) is in Jordan normal form InstallGlobalFunction(JordanNormalForm, function(A) - local n,F,pol,minpol,primary,primarydims,crhr,cyclicdims,elDivs, + local n,F,pol,minpol,primary,primarydims,crhr,cyclicdims,elDivs,prim1, subsubCOB,COB,subA,cy,i,j,facOcc,subCOB,crcy,subsubA,prepreCOB,preCOB; F := DefaultFieldOfMatrix(A); n := NrRows(A); @@ -1013,9 +1016,10 @@ InstallGlobalFunction(JordanNormalForm, function(A) return JordanNormalFormIrred(A,minpol); fi; if Degree(minpol) = n then - primary := nfmPrimaryDecompositionositionforJNFCyclic(A, minpol, facOcc); + prim1 := nfmPrimaryDecompositionforJNFCyclic(A, minpol, facOcc); + primary := [prim1[1],prim1[3]]; else - primary := nfmPrimaryDecompositionositionforJNF(A, minpol, facOcc); + primary := nfmPrimaryDecompositionforJNF(A, minpol, facOcc); fi; primarydims := primary[2]; #dimensions of generalized eigenspaces COB := primary[1]; #Change of basis matrix, this will be the final COB from A to JNF diff --git a/tst/utils.g b/tst/utils.g index 5127475..39f72e1 100644 --- a/tst/utils.g +++ b/tst/utils.g @@ -64,7 +64,7 @@ nfmCheckPrimaryDecomposition := function(F, n) B := A^Inverse(Prim[1]); minpolfacs := Factors(MinimalPolynomial(F,A)); k := 1; - for dim in Prim[2] do + for dim in Prim[3] do sub := ExtractSubMatrix(B,[k..k+dim-1],[k..k+dim-1]); k := k+dim; if not Factors(MinimalPolynomial(F,sub))[1] in minpolfacs then @@ -81,7 +81,7 @@ nfmCheckPrimaryDecompositionNonCyclic := function(F, n) B := A^Inverse(Prim[1]); minpolfacs := Factors(MinimalPolynomial(F,A)); k := 1; - for dim in Prim[2] do + for dim in Prim[3] do sub := ExtractSubMatrix(B,[k..k+dim-1],[k..k+dim-1]); k := k+dim; if not Factors(MinimalPolynomial(F,sub))[1] in minpolfacs then @@ -115,7 +115,7 @@ nfmCheckPrimaryDecompositionForMatrix := function(A) B := AA^Inverse(Prim[1]); minpolfacs := Factors(MinimalPolynomial(AA)); k := 1; - for dim in Prim[2] do + for dim in Prim[3] do sub := ExtractSubMatrix(B,[k..k+dim-1],[k..k+dim-1]); k := k+dim; if not Factors(MinimalPolynomial(sub))[1] in minpolfacs then From 0c787616e197af611449a13558b7830d11db45d1 Mon Sep 17 00:00:00 2001 From: Alia Bonnet Date: Sun, 17 May 2026 21:03:10 +0200 Subject: [PATCH 3/3] fixed minor mistake --- gap/nofoma.gi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gap/nofoma.gi b/gap/nofoma.gi index 5df2d0d..32b1e7e 100644 --- a/gap/nofoma.gi +++ b/gap/nofoma.gi @@ -735,7 +735,7 @@ end); #Primary Decomposition for cyclic matrices #Jordan normal form will call this function if a cyclic matrix is detected BindGlobal("nfmPrimaryDecompositionforJNFCyclic", function(A, minpol, minpolfacs) - local vspan,n,w,mf,wspan,qi,k,COB,dims; + local vspan,n,w,mf,wspan,qi,k,COB,dims,elDivs; n := NrRows(A); vspan := nfmFindCyclicVectorNC(A); dims := [];