diff --git a/lib/centralizer.gi b/lib/centralizer.gi index 1b47973..7bda059 100644 --- a/lib/centralizer.gi +++ b/lib/centralizer.gi @@ -96,7 +96,19 @@ end ); # Same as DecomposeCentralizerBlocks but converts to full matrices InstallGlobalFunction( CentralizerOfRepresentation, function(rho) - return List(CentralizerBlocksOfRepresentation(rho), BlockDiagonalMatrix@); + local decomposition, basis_change, block_basis; + + decomposition := ComputeUsingMethod@(rho); + basis_change := TransposedMat(decomposition.basis); + block_basis := List(decomposition.centralizer_basis, + BlockDiagonalMatrix@); + + # The standard block generators commute with the block-diagonalized + # representation. Conjugate them back so that this function returns the + # centralizer in the coordinates of the representation supplied by the + # caller. + return List(block_basis, + C -> basis_change * C * basis_change^-1); end ); # Given an orthonormal basis for the centralizer (w.r.t the product diff --git a/lib/groupsum.gi b/lib/groupsum.gi index 49e6069..aed6f11 100644 --- a/lib/groupsum.gi +++ b/lib/groupsum.gi @@ -1,6 +1,10 @@ InstallGlobalFunction( GroupSumBSGS, function(G, summand) local H, n, chain, groups, m, sum, i, cosets, iso, zero, g, S, right_reps; + if IsTrivial(G) then + return summand(One(G)); + fi; + # stab chain computation only works on permutation groups iso := IsomorphismPermGroup(G); H := Image(iso, G); diff --git a/lib/serre.gi b/lib/serre.gi index c6533de..60a5c3f 100644 --- a/lib/serre.gi +++ b/lib/serre.gi @@ -155,6 +155,14 @@ InstallGlobalFunction( CanonicalDecomposition, function(rho) # The group we are taking representations of G := Source(rho); + # All projector calculations below act on vectors by matrices. Preserve + # the public convenience of accepting permutation representations by + # converting once and then consistently using the linear representation. + rho := ConvertRhoIfNeeded@(rho); + if rho = fail then + Error(" must be a finite linear or permutation representation"); + fi; + # The list of irreps W_i of G over F appearing in rho irreps := ValueOption("irreps"); @@ -164,8 +172,7 @@ InstallGlobalFunction( CanonicalDecomposition, function(rho) # We might need to convert here, since this function needs a # linear rep - irreps := RelevantIrreps@(ConvertRhoIfNeeded@(rho), - irreps); + irreps := RelevantIrreps@(rho, irreps); # if there's only 1 irrep, the canonical summand is just the whole # space! diff --git a/lib/utils.gi b/lib/utils.gi index bd3dd7d..f487b82 100644 --- a/lib/utils.gi +++ b/lib/utils.gi @@ -268,13 +268,14 @@ OrthonormalBasis@ := function(v) end; InstallGlobalFunction( IsOrthonormalSet, function(S, prod) - return ForAll(S, v1 -> ForAll(S, function(v2) - if v1 = v2 then - return prod(v1, v2) = 1; - else - return prod(v1, v2) = 0; - fi; - end )); + return ForAll([1..Length(S)], + i -> ForAll([1..Length(S)], function(j) + if i = j then + return prod(S[i], S[j]) = 1; + else + return prod(S[i], S[j]) = 0; + fi; + end )); end ); KroneckerList@ := function(reps) diff --git a/tst/README.md b/tst/README.md new file mode 100644 index 0000000..cb05fbf --- /dev/null +++ b/tst/README.md @@ -0,0 +1,39 @@ +# RepnDecomp test strategy + +The hand-written tests in this directory complement the examples generated by +AutoDoc. They use fixed groups, matrices, and random seeds, and primarily test +mathematical invariants instead of a particular choice of basis. + +The suite checks that: + +- returned summands are invariant, irreducible, independent, and exhaustive; +- collected summands agree exactly with representation isomorphism classes; +- character inner products recover the expected multiplicities; +- a returned basis simultaneously block diagonalizes every group generator; +- the centralizer has dimension `sum(m_i^2)`, is an algebra containing the + identity, and commutes with the original representation in its original + coordinates; +- centralizer projection reproduces direct conjugacy-class sums; +- explicit intertwiners satisfy their defining equations; +- BSGS group sums equal direct enumeration; +- tensor, unitarization, LDL, permutation, and orbital operations satisfy their + defining identities. + +These checks reflect the principal computational use cases and certification +criteria discussed in: + +- Hymabaccus and Pasechnik, *Decomposing Linear Representations of Finite + Groups*, ; +- Vallentin, *Symmetry in semidefinite programs*, + ; +- Rosset, Montealegre-Mora, and Bancal, *RepLAB: a + computational/numerical approach to representation theory*, + ; +- Montealegre-Mora et al., *Certifying Numerical Decompositions of Compact + Group Representations*, ; +- Olver, *Representations of the symmetric group are decomposable in + polynomial time*, . + +No wall-clock thresholds are asserted. Performance-sensitive paths are +covered by small deterministic representatives so the suite remains suitable +for GAP's `minimal`, `latest`, and `devel` CI jobs. diff --git a/tst/centralizer-properties.tst b/tst/centralizer-properties.tst new file mode 100644 index 0000000..14c009d --- /dev/null +++ b/tst/centralizer-properties.tst @@ -0,0 +1,53 @@ +# Centralizer and class-sum properties motivated by symmetry-reduced SDPs. + +gap> START_TEST("centralizer-properties.tst"); + +gap> G := DihedralGroup(8);; irreps := IrreducibleRepresentations(G);; +gap> blockRep := DirectSumOfRepresentations([irreps[4], irreps[4], irreps[5]]);; +gap> A := REPN_TEST_UpperUnitriangularMat(4);; +gap> rho := REPN_TEST_ConjugateRepresentation(blockRep, A);; +gap> centralizer := CentralizerOfRepresentation(rho);; +gap> Length(centralizer) = 2^2 + 1^2; +true +gap> ForAll(centralizer, C -> REPN_TEST_CommutesWithRepresentation(rho, C)); +true +gap> RankMat(List(centralizer, Flat)) = Length(centralizer); +true +gap> centSpace := VectorSpace(Cyclotomics, centralizer);; +gap> IdentityMat(4) in centSpace; +true +gap> ForAll(centralizer, X -> ForAll(centralizer, Y -> X*Y in centSpace)); +true + +gap> blockCentralizer := CentralizerBlocksOfRepresentation(rho);; +gap> Length(blockCentralizer) = 5; +true +gap> ForAll(blockCentralizer, blocks -> Length(blocks) = 2); +true +gap> List(blockCentralizer[1], Length) = [2,2]; +true + +gap> diagRep := BlockDiagonalRepresentation(rho);; +gap> expandedBlocks := List(blockCentralizer, BlockDiagonalMatrix@RepnDecomp);; +gap> ForAll(expandedBlocks, C -> REPN_TEST_CommutesWithRepresentation(diagRep, C)); +true + +gap> unitaryRep := blockRep;; unitaryCentralizer := CentralizerOfRepresentation(unitaryRep);; +gap> orthogonalCentralizer := OrthonormalBasis@RepnDecomp(unitaryCentralizer);; +gap> IsOrthonormalSet(orthogonalCentralizer, InnerProduct@RepnDecomp); +true +gap> ForAll(ConjugacyClasses(G), class -> ClassSumCentralizer(unitaryRep, class, orthogonalCentralizer) = Sum(class, g -> Image(unitaryRep,g))); +true +gap> ForAll(ConjugacyClasses(G), class -> ClassSumCentralizerNC(unitaryRep, class, orthogonalCentralizer) = Sum(class, g -> Image(unitaryRep,g))); +true + +gap> sizes := [rec(dimension:=1,nblocks:=2), rec(dimension:=3,nblocks:=1)];; +gap> standardBlocks := SizesToBlocks(sizes);; +gap> Length(standardBlocks) = 5; +true +gap> ForAll(standardBlocks, blocks -> DimensionsMat(BlockDiagonalMatrix@RepnDecomp(blocks)) = [5,5]); +true +gap> RankMat(List(standardBlocks, blocks -> Flat(BlockDiagonalMatrix@RepnDecomp(blocks)))) = 5; +true + +gap> STOP_TEST("centralizer-properties.tst", 1); diff --git a/tst/core-utils.tst b/tst/core-utils.tst new file mode 100644 index 0000000..e29f02f --- /dev/null +++ b/tst/core-utils.tst @@ -0,0 +1,69 @@ +# Deterministic tests for small matrix, character, and representation helpers. + +gap> START_TEST("core-utils.tst"); + +gap> Take@RepnDecomp([ 1, 2, 3 ], 0) = []; +true +gap> Take@RepnDecomp([ 1, 2, 3 ], 2) = [ 1, 2 ]; +true +gap> Drop@RepnDecomp([ 1, 2, 3 ], 0) = [ 1, 2, 3 ]; +true +gap> Drop@RepnDecomp([ 1, 2, 3 ], 3) = []; +true + +gap> original := [ 1, 2, 3 ];; copies := Replicate@RepnDecomp(original, 2);; +gap> copies[1][1] := 9;; copies[2] = original and original = [ 1, 2, 3 ]; +true +gap> Replicate@RepnDecomp(7, 3) = [ 7, 7, 7 ]; +true + +gap> BlockDiagonalMatrix@RepnDecomp([]) = []; +true +gap> BlockDiagonalMatrix@RepnDecomp([ IdentityMat(2), IdentityMat(3) ]) = IdentityMat(5); +true +gap> BlockDiagonalMatrix@RepnDecomp([ [[2]], [], [[3,0],[0,4]] ]) = DiagonalMat([2,3,4]); +true + +gap> M := DiagonalMat([ 1, 2, 3, 4 ]);; +gap> ExtractBlock@RepnDecomp(M, 1, 1, 2) = DiagonalMat([1,2]); +true +gap> ExtractBlock@RepnDecomp(M, 1, 2, 2) = NullMat(2,2); +true +gap> ExtractBlock@RepnDecomp(M, 2, 2, 2) = DiagonalMat([3,4]); +true + +gap> V := VectorSpace(Rationals, [[1,0],[0,1]], [0,0]);; +gap> W := MatrixImage@RepnDecomp([[1,0],[0,0]], V);; +gap> Dimension(W) = 1 and [1,0] in W and not [0,1] in W; +true + +gap> IsOrthonormalSet([[1,0],[0,1]], function(x,y) return x*y; end); +true +gap> IsOrthonormalSet([[1,0],[1,0]], function(x,y) return x*y; end); +false + +gap> G := SymmetricGroup(3);; irreps := IrreducibleRepresentations(G);; +gap> ForAll(irreps, IsFiniteGroupLinearRepresentation); +true +gap> ForAll(irreps, rho -> DegreeOfRepresentation(rho) = Length(Image(rho, One(G)))); +true +gap> chars := List(irreps, CharacterOfRepresentation@RepnDecomp);; +gap> ForAll([1..Length(chars)], i -> ForAll([1..Length(chars)], function(j) if i=j then return InnerProductOfCharacters@RepnDecomp(chars[i],chars[j],G)=1; else return InnerProductOfCharacters@RepnDecomp(chars[i],chars[j],G)=0; fi; end)); +true + +gap> direct := DirectSumOfRepresentations([irreps[1], irreps[3]]);; +gap> DegreeOfRepresentation(direct) = 3; +true +gap> IrrVectorOfRepresentation@RepnDecomp(direct, chars) = [1,0,1]; +true + +gap> perm := ActionHomomorphism(G, [1..3]);; +gap> IsFiniteGroupPermutationRepresentation(perm); +true +gap> linear := PermToLinearRep(perm);; +gap> IsFiniteGroupLinearRepresentation(linear) and DegreeOfRepresentation(linear) = 3; +true +gap> ForAll(GeneratorsOfGroup(G), g -> Image(linear,g) = PermutationMat(Image(perm,g),3)); +true + +gap> STOP_TEST("core-utils.tst", 1); diff --git a/tst/decomposition-properties.tst b/tst/decomposition-properties.tst new file mode 100644 index 0000000..11490bc --- /dev/null +++ b/tst/decomposition-properties.tst @@ -0,0 +1,68 @@ +# Property tests for exact decomposition and simultaneous block diagonalization. + +gap> START_TEST("decomposition-properties.tst"); + +gap> G := SymmetricGroup(3);; irreps := IrreducibleRepresentations(G);; +gap> blockRep := DirectSumOfRepresentations([irreps[1], irreps[3], irreps[3]]);; +gap> A := REPN_TEST_UpperUnitriangularMat(5);; +gap> rho := REPN_TEST_ConjugateRepresentation(blockRep, A);; +gap> info := REPN_ComputeUsingSerre(rho : irreps := irreps);; +gap> REPN_TEST_IsBlockDiagonalization(rho, info); +true +gap> REPN_TEST_IsCollectedDecomposition(rho, Filtered(info.decomposition, x -> Length(x) > 0)); +true +gap> List(Filtered(info.decomposition, x -> Length(x) > 0), Length) = [1,2]; +true +gap> Sum(Flat(info.decomposition), summand -> Length(summand.basis)) = 5; +true +gap> Length(info.centralizer_basis) = 1^2 + 2^2; +true + +gap> collected := IrreducibleDecompositionCollected(rho);; +gap> REPN_TEST_IsCollectedDecomposition(rho, collected); +true +gap> REPN_TEST_IsInvariantDecomposition(rho, IrreducibleDecomposition(rho)); +true + +gap> rhoK := REPN_TEST_ConjugateRepresentation(blockRep, A);; +gap> infoK := REPN_ComputeUsingSerre(rhoK : irreps := irreps, use_kronecker);; +gap> REPN_TEST_IsBlockDiagonalization(rhoK, infoK); +true +gap> REPN_TEST_IsCollectedDecomposition(rhoK, Filtered(infoK.decomposition, x -> Length(x) > 0)); +true + +gap> rhoAlt := REPN_TEST_ConjugateRepresentation(blockRep, A);; +gap> infoAlt := REPN_ComputeUsingMyMethod(rhoAlt : irreps := irreps);; +gap> REPN_TEST_IsBlockDiagonalization(rhoAlt, infoAlt); +true +gap> REPN_TEST_IsCollectedDecomposition(rhoAlt, infoAlt.decomposition); +true + +gap> canonical := CanonicalDecomposition(REPN_TEST_ConjugateRepresentation(blockRep, A) : irreps := irreps);; +gap> SortedList(List(canonical, Dimension)) = [1,4]; +true +gap> ForAll(canonical, V -> IsGInvariant@RepnDecomp(rho, V)); +true +gap> RankMat(Concatenation(List(canonical, V -> List(Basis(V))))) = 5; +true + +gap> T := Group(());; trivial3 := FuncToHom@RepnDecomp(T, g -> IdentityMat(3));; +gap> trivialDecomp := IrreducibleDecomposition(trivial3);; +gap> Length(trivialDecomp) = 3 and REPN_TEST_IsInvariantDecomposition(trivial3, trivialDecomp); +true + +gap> H := SymmetricGroup(4);; hirreps := IrreducibleRepresentations(H);; +gap> multiplicityFree := DirectSumOfRepresentations([hirreps[1], hirreps[3], hirreps[5]]);; +gap> mfDecomp := IrreducibleDecomposition(multiplicityFree);; +gap> Length(mfDecomp) = 3 and REPN_TEST_IsInvariantDecomposition(multiplicityFree, mfDecomp); +true + +gap> perm := ActionHomomorphism(G, [1..3]);; +gap> permCanonical := CanonicalDecomposition(perm : irreps := irreps);; +gap> SortedList(List(permCanonical, Dimension)) = [1,2]; +true +gap> linearPerm := PermToLinearRep(perm);; +gap> ForAll(permCanonical, V -> IsGInvariant@RepnDecomp(linearPerm, V)); +true + +gap> STOP_TEST("decomposition-properties.tst", 1); diff --git a/tst/isomorphism-properties.tst b/tst/isomorphism-properties.tst new file mode 100644 index 0000000..c1054e8 --- /dev/null +++ b/tst/isomorphism-properties.tst @@ -0,0 +1,40 @@ +# Deterministic inputs for all explicit-intertwiner implementations. + +gap> START_TEST("isomorphism-properties.tst"); + +gap> G := SymmetricGroup(4);; irreps := IrreducibleRepresentations(G);; +gap> rho := DirectSumOfRepresentations([irreps[1], irreps[3], irreps[3]]);; +gap> B := REPN_TEST_UpperUnitriangularMat(5);; +gap> tau := REPN_TEST_ConjugateRepresentation(rho, B);; +gap> AreRepsIsomorphic(rho, tau); +true +gap> IsLinearRepresentationIsomorphism(B^-1, rho, tau); +true +gap> IsLinearRepresentationIsomorphism(NullMat(5,5), rho, tau); +false + +gap> iso := LinearRepresentationIsomorphism(rho, tau);; +gap> IsLinearRepresentationIsomorphism(iso, rho, tau); +true +gap> isoK := LinearRepresentationIsomorphism(rho, tau : use_kronecker);; +gap> IsLinearRepresentationIsomorphism(isoK, rho, tau); +true +gap> isoOrbit := LinearRepresentationIsomorphism(rho, tau : use_orbit_sum);; +gap> IsLinearRepresentationIsomorphism(isoOrbit, rho, tau); +true +gap> isoSlow := LinearRepresentationIsomorphismSlow(rho, tau);; +gap> IsLinearRepresentationIsomorphism(isoSlow, rho, tau); +true + +gap> AreRepsIsomorphic(irreps[1], irreps[2]); +false +gap> LinearRepresentationIsomorphism(irreps[1], irreps[2]); +fail +gap> AreRepsIsomorphic(rho, irreps[1]); +false + +gap> H := CyclicGroup(2);; hrep := IrreducibleRepresentations(H)[1];; +gap> AreRepsIsomorphic(irreps[1], hrep); +false + +gap> STOP_TEST("isomorphism-properties.tst", 1); diff --git a/tst/tensor-groupsum-properties.tst b/tst/tensor-groupsum-properties.tst new file mode 100644 index 0000000..17d8bc3 --- /dev/null +++ b/tst/tensor-groupsum-properties.tst @@ -0,0 +1,48 @@ +# Tensor identities and exact BSGS group-sum comparisons. + +gap> START_TEST("tensor-groupsum-properties.tst"); + +gap> G := SymmetricGroup(4);; irreps := IrreducibleRepresentations(G);; +gap> rho := irreps[4];; tau := irreps[3];; +gap> tensor := TensorProductOfRepresentations(rho, tau);; +gap> kron := KroneckerProductOfRepresentations(rho, tau);; +gap> testMatrix := [[1,2],[3,4],[5,6]];; +gap> ForAll(GeneratorsOfGroup(G), function(g) local explicit; explicit := KroneckerProduct(Image(rho,g),Image(tau,g)); return WrapMatrix@RepnDecomp(explicit*Flat(testMatrix),2) = Image(tensor,g)*testMatrix; end); +true +gap> ForAll(GeneratorsOfGroup(G), g -> Image(kron,g) = KroneckerProduct(Image(rho,g),Image(tau,g))); +true +gap> tensorChar := CharacterOfTensorProductOfRepresentations(tensor);; +gap> ForAll(GeneratorsOfGroup(G), g -> tensorChar(g) = Trace(Image(rho,g))*Trace(Image(tau,g))); +true + +gap> gens := GeneratorsOfGroup(G);; x := Image(tensor,gens[1]);; y := Image(tensor,gens[2]);; +gap> (x*y)*testMatrix = x*(y*testMatrix); +true +gap> One(x)*testMatrix = testMatrix; +true +gap> x^-1*(x*testMatrix) = testMatrix; +true + +gap> GroupSumBSGS(G, g -> Image(rho,g)) = Sum(G, g -> Image(rho,g)); +true +gap> GroupSumBSGS(G, g -> Image(kron,g)) = Sum(G, g -> Image(kron,g)); +true +gap> H := SmallGroup(12,3);; hrep := IrreducibleRepresentations(H)[4];; +gap> GroupSumBSGS(H, g -> Image(hrep,g)) = Sum(H, g -> Image(hrep,g)); +true +gap> T := Group(());; trep := FuncToHom@RepnDecomp(T, g -> [[1]]);; +gap> GroupSumBSGS(T, g -> Image(trep,g)) = [[1]]; +true + +gap> direct := DirectSumOfRepresentations([irreps[1],irreps[3]]);; +gap> ForAll(GeneratorsOfGroup(G), g -> Image(direct,g) = BlockDiagonalMatrix@RepnDecomp([Image(irreps[1],g),Image(irreps[3],g)])); +true + +gap> G2 := SymmetricGroup(2);; G3 := SymmetricGroup(3);; +gap> products := TensorProductRepLists(IrreducibleRepresentations(G2), IrreducibleRepresentations(G3));; +gap> Length(products) = Length(IrreducibleRepresentations(G2))*Length(IrreducibleRepresentations(G3)); +true +gap> ForAll(products, rep -> Source(rep) = DirectProduct(G2,G3)); +true + +gap> STOP_TEST("tensor-groupsum-properties.tst", 1); diff --git a/tst/testall.g b/tst/testall.g index 267a5d3..2e74135 100644 --- a/tst/testall.g +++ b/tst/testall.g @@ -6,6 +6,14 @@ # LoadPackage( "RepnDecomp" ); +# Shared deterministic property checks used by the hand-written regression +# tests. The AutoDoc-generated examples remain independent of these helpers. +ReadPackage( "RepnDecomp", "tst/testutils.g" ); + +# Several public algorithms use randomized choices internally. Fixing the +# global source makes failures reproducible while still exercising those paths. +Reset( GlobalMersenneTwister, 27041993 ); + TestDirectory(DirectoriesPackageLibrary( "RepnDecomp", "tst" ), rec(exitGAP := true, testOptions := rec(compareFunction := "uptowhitespace"))); diff --git a/tst/testutils.g b/tst/testutils.g new file mode 100644 index 0000000..fea8599 --- /dev/null +++ b/tst/testutils.g @@ -0,0 +1,112 @@ +# +# Shared helpers for RepnDecomp's hand-written tests. +# + +BindGlobal( "REPN_TEST_ConjugateRepresentation", function( rho, A ) + return ComposeHomFunction( rho, M -> A^-1 * M * A ); +end ); + +BindGlobal( "REPN_TEST_RestrictedRepresentation", function( rho, summand ) + local basis; + + if IsRecord( summand ) and IsBound( summand.basis ) then + basis := summand.basis; + elif IsVectorSpace( summand ) then + basis := List( Basis( summand ) ); + else + Error( "test summand must be a record with a basis or a vector space" ); + fi; + + basis := Basis( VectorSpace( Cyclotomics, basis ) ); + return RestrictRep@RepnDecomp( rho, basis ); +end ); + +BindGlobal( "REPN_TEST_IsIrreducible", function( rho ) + local chi; + chi := CharacterOfRepresentation@RepnDecomp( rho ); + return InnerProductOfCharacters@RepnDecomp( chi, chi, Source( rho ) ) = 1; +end ); + +BindGlobal( "REPN_TEST_IsInvariantDecomposition", function( rho, decomposition ) + local bases, degree; + + bases := List( decomposition, function( summand ) + if IsRecord( summand ) and IsBound( summand.basis ) then + return summand.basis; + elif IsVectorSpace( summand ) then + return List( Basis( summand ) ); + fi; + Error( "test summand must be a record with a basis or a vector space" ); + end ); + degree := DegreeOfRepresentation( rho ); + + return Sum( bases, Length ) = degree + and RankMat( Concatenation( bases ) ) = degree + and ForAll( bases, + basis -> IsGInvariant@RepnDecomp( rho, basis ) ) + and ForAll( decomposition, + summand -> REPN_TEST_IsIrreducible( + REPN_TEST_RestrictedRepresentation( rho, summand ) ) ); +end ); + +BindGlobal( "REPN_TEST_IsCollectedDecomposition", function( rho, collected ) + local flat, restricted, i, j; + + flat := Flat( collected ); + if not REPN_TEST_IsInvariantDecomposition( rho, flat ) then + return false; + fi; + + restricted := List( collected, + family -> List( family, + summand -> REPN_TEST_RestrictedRepresentation( + rho, summand ) ) ); + + for i in [ 1 .. Length( restricted ) ] do + if not ForAll( restricted[i], + rep -> AreRepsIsomorphic( restricted[i][1], rep ) ) then + return false; + fi; + + if i < Length( restricted ) then + for j in [ i + 1 .. Length( restricted ) ] do + if AreRepsIsomorphic( restricted[i][1], restricted[j][1] ) then + return false; + fi; + od; + fi; + od; + + return true; +end ); + +BindGlobal( "REPN_TEST_CommutesWithRepresentation", function( rho, A ) + return ForAll( GeneratorsOfGroup( Source( rho ) ), + g -> A * Image( rho, g ) = Image( rho, g ) * A ); +end ); + +BindGlobal( "REPN_TEST_IsBlockDiagonalization", function( rho, info ) + local P, degree; + + degree := DegreeOfRepresentation( rho ); + P := TransposedMat( info.basis ); + + return Length( info.basis ) = degree + and RankMat( info.basis ) = degree + and ForAll( GeneratorsOfGroup( Source( rho ) ), + g -> Image( info.diagonal_rep, g ) + = P^-1 * Image( rho, g ) * P ); +end ); + +BindGlobal( "REPN_TEST_UpperUnitriangularMat", function( n ) + local A, i; + + A := IdentityMat( n ); + for i in [ 1 .. n - 1 ] do + A[i][i + 1] := i; + od; + if n > 2 then + A[1][n] := -1; + fi; + return A; +end ); diff --git a/tst/unitary-permutation-properties.tst b/tst/unitary-permutation-properties.tst new file mode 100644 index 0000000..a8699ae --- /dev/null +++ b/tst/unitary-permutation-properties.tst @@ -0,0 +1,49 @@ +# Unitarization, Hermitian linear algebra, and orbital centralizers. + +gap> START_TEST("unitary-permutation-properties.tst"); + +gap> G := SymmetricGroup(3);; irreps := IrreducibleRepresentations(G);; +gap> rho := DirectSumOfRepresentations([irreps[1],irreps[2]]);; +gap> IsUnitaryRepresentation(rho); +true +gap> A := [[-1,1],[-2,-1]];; +gap> tau := REPN_TEST_ConjugateRepresentation(rho, A);; +gap> IsUnitaryRepresentation(tau); +false +gap> result := UnitaryRepresentation(tau);; +gap> IsUnitaryRepresentation(result.unitary_rep); +true +gap> AreRepsIsomorphic(tau, result.unitary_rep); +true +gap> ForAll(GeneratorsOfGroup(G), g -> result.basis_change*Image(result.unitary_rep,g) = Image(tau,g)*result.basis_change); +true + +gap> hermitian := [[4,1+E(4)],[1-E(4),3]];; +gap> ldl := LDLDecomposition(hermitian);; +gap> hermitian = ldl.L*DiagonalMat(ldl.D)*ConjugateTranspose@RepnDecomp(ldl.L); +true +gap> ForAll([1..Length(ldl.L)], i -> ForAll([1..Length(ldl.L)], j -> j<=i or ldl.L[i][j]=0)); +true +gap> LDLDecomposition(IdentityMat(4)) = rec(L:=IdentityMat(4),D:=[1,1,1,1]); +true + +gap> matrices := [[[1,0],[0,0]], [[0,0],[0,1]]];; +gap> orth := OrthonormalBasis@RepnDecomp(matrices);; +gap> IsOrthonormalSet(orth, InnerProduct@RepnDecomp); +true +gap> VectorSpace(Cyclotomics,orth) = VectorSpace(Cyclotomics,matrices); +true + +gap> P := SymmetricGroup(3);; perm := ActionHomomorphism(P,[1..3]);; +gap> orbitalBasis := RepresentationCentralizerPermRep@RepnDecomp(perm);; +gap> Length(orbitalBasis) = 2; +true +gap> linearPerm := PermToLinearRep(perm);; +gap> ForAll(orbitalBasis, M -> REPN_TEST_CommutesWithRepresentation(linearPerm,M)); +true +gap> Sum(orbitalBasis) = List([1..3], i -> [1,1,1]); +true +gap> RankMat(List(orbitalBasis,Flat)) = 2; +true + +gap> STOP_TEST("unitary-permutation-properties.tst", 1);