Skip to content

Commit 26a7cbb

Browse files
authored
PrimaryDecomp recognises cyclic matrices (#62)
* PrimaryDecomp recognises cyclic matrices
1 parent 8ac67da commit 26a7cbb

File tree

1 file changed

+95
-90
lines changed

1 file changed

+95
-90
lines changed

gap/nofoma.gi

Lines changed: 95 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -627,32 +627,52 @@ BindGlobal("nfmPolyEvalFromSpan", function(span,pol)
627627
return resu;
628628
end);
629629

630-
#TODO: recognise cyclic matrices
631-
#Primary Decomposition using a modified version of Allan Steel's algorithm
632-
#Standalone version
633-
#Returns matrix B such that B*A*B^-1 is in primary decomposition form
634-
#along with dimensions of primary subspaces
635-
InstallGlobalFunction(PrimaryDecomp, function(A)
636-
local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v,
637-
COB,pot,gs,f2,dims,toAdd,combined,dim;
630+
631+
BindGlobal("factoriseByKnownFactors", function(kFacs,pol)
632+
local fac,factup,i,resfacs,count,newpol,oldpol;
633+
resfacs := [];
634+
oldpol := pol;
635+
for factup in kFacs do
636+
fac := factup[1];
637+
count := 0;
638+
for i in [1..factup[2]] do
639+
newpol := Quotient(oldpol, fac);
640+
if newpol = fail then
641+
break;
642+
fi;
643+
count := count + 1;
644+
oldpol := newpol;
645+
od;
646+
if not count = 0 then
647+
Add(resfacs, [fac, count]);
648+
fi;
649+
od;
650+
return resfacs;
651+
end);
652+
653+
#TODO: if size of minpolfacs is one we can return identity
654+
#Primary Decomposition using a modified version of Allan Steel's algorithm for use in the Jordan normal form function
655+
#Takes matrix along with its minimal polynomial and its factorised version as input
656+
#Returns matrix B such that B*A*B^-1 is in primary decomp. form, dimensions of primary subspaces
657+
#and factors of minimal polynomial in correct order
658+
BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs)
659+
local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v,
660+
COB,pot,gs,f2,toAdd,pos,dims,dim;
638661
rank := 0;
639662
n := NrRows(A);
640663
F := DefaultFieldOfMatrix(A);
641664
v := nfmGenerateRandomVector(F,n);
642-
A := Matrix(F,A);
643-
if IsZero(A) then
644-
return OneMutable(A);
645-
fi;
646-
gens := []; #Li_s as in Steel paper will go in here
665+
gens := []; #Li_s as in Steel paper will go in here (but without separate U)
647666
gs := []; #distinct factors of minimal polynomial
667+
dims := [];
648668
while not rank = n do
649669
m := UnivariatePolynomial(F,SpinMatVector(A,v)[3]);
650670
p := One(PolynomialRing(F));
651671
for i in [1..Size(gens)] do
652672
L_i := gens[i];
653673
if not IsOne(Gcd(m,gs[i])) then
654674
f := m;
655-
pot := 0;
675+
pot := 0;
656676
for j in [1..n] do
657677
f2 := Quotient(f,gs[i]);
658678
if f2 = fail then
@@ -663,8 +683,8 @@ InstallGlobalFunction(PrimaryDecomp, function(A)
663683
od;
664684
w := PolynomialToMatVec(A,f,v);
665685
p := p * gs[i]^pot;
666-
#minimal polynomial of w has degree smaller than or equal to n - degree(f)
667-
wspan := nfmSpinUntil(w,A,n-Degree(f));
686+
#minimal polynomial of w has degree smaller than or equal to minpol/f
687+
wspan := nfmSpinUntil(w,A,Degree(minpol)-Degree(f));
668688
toAdd := EcheloniseMat(Concatenation(wspan,gens[i]));
669689
if not IsMatrix(toAdd) then
670690
toAdd := [toAdd]; # Convert vector to 1-row matrix
@@ -676,8 +696,9 @@ InstallGlobalFunction(PrimaryDecomp, function(A)
676696
v :=PolynomialToMatVec(A,p,v);
677697
m := Quotient(m,p);
678698
if not IsOne(m) then
679-
facs := Factors(m);
680-
facs := Collected(facs);
699+
#TODO: make this work
700+
#facs := factoriseByKnownFactors(minpolfacs,m);
701+
facs := Collected(Factors(m));
681702
for i in [1..Size(facs)] do
682703
qi := (facs[i][1])^(facs[i][2]);
683704
w := PolynomialToMatVec(A,Quotient(m,qi),v);
@@ -686,7 +707,7 @@ InstallGlobalFunction(PrimaryDecomp, function(A)
686707
Add(gs, facs[i][1]);
687708
od;
688709
fi;
689-
#alles in eine matrix
710+
# put everything into a single matrix
690711
COB := ZeroMatrix(F,n,n);
691712
k := 0;
692713
for i in [1..Size(gens)] do
@@ -701,70 +722,70 @@ InstallGlobalFunction(PrimaryDecomp, function(A)
701722
v := nfmFindVectorNotInSubspaceNC(COB);
702723
fi;
703724
od;
704-
#sorted blocks and count dims
705725
COB := ZeroMatrix(F,n,n);
706-
combined := [];
707-
for i in [1..Size(gs)] do
708-
Add(combined, [gens[i],gs[i]]);
709-
od;
710-
Sort(combined, {v,w} -> v[2] < w[2]);
726+
#sort primary subspaces back into order and collect dims
711727
k := 0;
712-
dims := [];
713-
for i in [1..Size(gs)] do
714-
L_i := combined[i][1];
715-
dim := NrRows(L_i);
728+
for i in [1..Size(gens)] do
729+
pos := Position(gs, minpolfacs[i][1]);
730+
L_i := gens[pos];
731+
dim := NrRows(L_i); #?
716732
Add(dims, dim);
717733
CopySubMatrix(L_i, COB, [1..dim], [k+1..k+dim],[1..n], [1..n]);
718734
k := k + dim;
719735
od;
720736
return [COB, dims];
721737
end);
722738

723-
BindGlobal("factoriseByKnownFactors", function(kFacs,pol)
724-
local fac,factup,i,resfacs,count,newpol,oldpol;
725-
resfacs := [];
726-
oldpol := pol;
727-
for factup in kFacs do
728-
fac := factup[1];
729-
count := 0;
730-
for i in [1..factup[2]] do
731-
newpol := Quotient(oldpol, fac);
732-
if newpol = fail then
733-
break;
734-
fi;
735-
count := count + 1;
736-
oldpol := newpol;
739+
#Primary Decomposition for cyclic matrices
740+
#Jordan normal form will call this function if a cyclic matrix is detected
741+
BindGlobal("nfmPrimaryDecompositionforJNFCyclic", function(A, minpol, minpolfacs)
742+
local vspan,n,w,mf,wspan,qi,k,COB,dims;
743+
n := NrRows(A);
744+
vspan := nfmFindCyclicVectorNC(A);
745+
dims := [];
746+
COB := ZeroMutable(A);
747+
k := 0;
748+
for mf in minpolfacs do
749+
qi := (mf[1])^(mf[2]);
750+
w := nfmPolyEvalFromSpan(vspan,Quotient(minpol,qi));
751+
wspan := nfmSpinUntil(w,A,Degree(mf[1])*mf[2]);
752+
CopySubMatrix(wspan, COB, [1..NrRows(wspan)], [k+1..k+NrRows(wspan)], [1..n], [1..n]);
753+
Add(dims, NrRows(wspan));
754+
k := k + NrRows(wspan);
737755
od;
738-
if not count = 0 then
739-
Add(resfacs, [fac, count]);
740-
fi;
741-
od;
742-
return resfacs;
756+
return [COB, dims];
743757
end);
744758

745-
#TODO: if size of minpolfacs is one we can return identity
746-
#Primary Decomposition using a modified version of Allan Steel's algorithm for use in the Jordan normal form function
747-
#Takes matrix along with its minimal polynomial and its factorised version as input
748-
#Returns matrix B such that B*A*B^-1 is in primary decomp. form, dimensions of primary subspaces
749-
#and factors of minimal polynomial in correct order
750-
BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs)
751-
local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v,
752-
COB,pot,gs,f2,toAdd,pos,dims,dim;
759+
#TODO: recognise cyclic matrices
760+
#Primary Decomposition using a modified version of Allan Steel's algorithm
761+
#Standalone version
762+
#Returns matrix B such that B*A*B^-1 is in primary decomposition form
763+
#along with dimensions of primary subspaces
764+
InstallGlobalFunction(PrimaryDecomp, function(A)
765+
local rank,F,n,m,f,w,p,j,i,wspan,gens,facs,L_i,qi,k,v,
766+
COB,pot,gs,f2,dims,toAdd,combined,dim,minpol;
753767
rank := 0;
754768
n := NrRows(A);
755769
F := DefaultFieldOfMatrix(A);
756770
v := nfmGenerateRandomVector(F,n);
757-
gens := []; #Li_s as in Steel paper will go in here (but without separate U)
771+
A := Matrix(F,A);
772+
minpol := MinimalPolynomial(F,A);
773+
if Degree(minpol) = n then
774+
return nfmPrimaryDecompositionforJNFCyclic(A,minpol,Collected(Factors(minpol)));
775+
fi;
776+
if IsZero(A) then
777+
return IdentityMat(n,F);
778+
fi;
779+
gens := []; #Li_s as in Steel paper will go in here
758780
gs := []; #distinct factors of minimal polynomial
759-
dims := [];
760781
while not rank = n do
761782
m := UnivariatePolynomial(F,SpinMatVector(A,v)[3]);
762783
p := One(PolynomialRing(F));
763784
for i in [1..Size(gens)] do
764785
L_i := gens[i];
765786
if not IsOne(Gcd(m,gs[i])) then
766787
f := m;
767-
pot := 0;
788+
pot := 0;
768789
for j in [1..n] do
769790
f2 := Quotient(f,gs[i]);
770791
if f2 = fail then
@@ -775,8 +796,8 @@ BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs)
775796
od;
776797
w := PolynomialToMatVec(A,f,v);
777798
p := p * gs[i]^pot;
778-
#minimal polynomial of w has degree smaller than or equal to minpol/f
779-
wspan := nfmSpinUntil(w,A,Degree(minpol)-Degree(f));
799+
#minimal polynomial of w has degree smaller than or equal to n - degree(f)
800+
wspan := nfmSpinUntil(w,A,n-Degree(f));
780801
toAdd := EcheloniseMat(Concatenation(wspan,gens[i]));
781802
if not IsMatrix(toAdd) then
782803
toAdd := [toAdd]; # Convert vector to 1-row matrix
@@ -788,9 +809,8 @@ BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs)
788809
v :=PolynomialToMatVec(A,p,v);
789810
m := Quotient(m,p);
790811
if not IsOne(m) then
791-
#TODO: make this work
792-
#facs := factoriseByKnownFactors(minpolfacs,m);
793-
facs := Collected(Factors(m));
812+
facs := Factors(m);
813+
facs := Collected(facs);
794814
for i in [1..Size(facs)] do
795815
qi := (facs[i][1])^(facs[i][2]);
796816
w := PolynomialToMatVec(A,Quotient(m,qi),v);
@@ -799,7 +819,7 @@ BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs)
799819
Add(gs, facs[i][1]);
800820
od;
801821
fi;
802-
# put everything into a single matrix
822+
#alles in eine matrix
803823
COB := ZeroMatrix(F,n,n);
804824
k := 0;
805825
for i in [1..Size(gens)] do
@@ -814,40 +834,25 @@ BindGlobal("nfmPrimaryDecompositionforJNF", function(A, minpol, minpolfacs)
814834
v := nfmFindVectorNotInSubspaceNC(COB);
815835
fi;
816836
od;
837+
#sorted blocks and count dims
817838
COB := ZeroMatrix(F,n,n);
818-
#sort primary subspaces back into order and collect dims
839+
combined := [];
840+
for i in [1..Size(gs)] do
841+
Add(combined, [gens[i],gs[i]]);
842+
od;
843+
Sort(combined, function(v,w) return v[2] < w[2]; end);
819844
k := 0;
820-
for i in [1..Size(gens)] do
821-
pos := Position(gs, minpolfacs[i][1]);
822-
L_i := gens[pos];
823-
dim := NrRows(L_i); #?
845+
dims := [];
846+
for i in [1..Size(gs)] do
847+
L_i := combined[i][1];
848+
dim := NrRows(L_i);
824849
Add(dims, dim);
825850
CopySubMatrix(L_i, COB, [1..dim], [k+1..k+dim],[1..n], [1..n]);
826851
k := k + dim;
827852
od;
828853
return [COB, dims];
829854
end);
830855

831-
#Primary Decomposition for cyclic matrices
832-
#Jordan normal form will call this function if a cyclic matrix is detected
833-
BindGlobal("nfmPrimaryDecompositionforJNFCyclic", function(A, minpol, minpolfacs)
834-
local vspan,n,w,mf,wspan,qi,k,COB,dims;
835-
n := NrRows(A);
836-
vspan := nfmFindCyclicVectorNC(A);
837-
dims := [];
838-
COB := ZeroMutable(A);
839-
k := 0;
840-
for mf in minpolfacs do
841-
qi := (mf[1])^(mf[2]);
842-
w := nfmPolyEvalFromSpan(vspan,Quotient(minpol,qi));
843-
wspan := nfmSpinUntil(w,A,Degree(mf[1])*mf[2]);
844-
CopySubMatrix(wspan, COB, [1..NrRows(wspan)], [k+1..k+NrRows(wspan)], [1..n], [1..n]);
845-
Add(dims, NrRows(wspan));
846-
k := k + NrRows(wspan);
847-
od;
848-
return [COB, dims];
849-
end);
850-
851856
#Input: For matrix A with minimal polynomial p^m: m, vector v and p(A)
852857
#Returns: v, length r of v, vp^(r-1)(A)
853858
BindGlobal("GetMinPolPowerWithVec", function(m,v,Ainp)

0 commit comments

Comments
 (0)