Skip to content

Commit ae69d72

Browse files
jacksonwalterstscrimdimpase
committed
remove debugging tests
check if mult. gen. and seminormal DFT match check to see if the multiplicative generator of GF(25) being computed by Sage matches the one being computed locally. also check the DFT. these are the only two things that may be different. remove double colon in examples I'm using some descriptive strings for each example since they're all a bit different. those sentences have double colons after them, and code immediately follows. Update src/sage/combinat/symmetric_group_algebra.py remove whitespace between `p` and \mid improve doctest examples with better exposition say what the example is before performing it. check that the desired property holds. fix failing doctest examples the computed unitary DFT in Sage is different than when I compute it locally using the same code. not sure why. Update src/sage/matrix/matrix2.pyx fetch the cached rank. if it's full or not yet computed, continue, otherwise throw a ValueError that the matrix is not full rank. better naming convention for SGAs separate the field and the group with an underscore. for fields of size q**2, use the actual squared value remove extraneous ")" chars not sure why these are there. should be GF(9), a field of square order this is meant to test the case when the characteristic divides the order. in this case, p=3, q=9, and n=3. before we were working over GF(3) rather than GF(9), so it triggered the other checks re: whether it is a field of square order. remove unnecessary blank lines, duplicate check remove trailing whitespace update `form` input, add more tests include a description in DFT of the different possible values of `form`. the possible values are `None`, `seminormal`, `modular`, `unitary`. each uses a different basis. also includes new tests for the four different ways that the input field might fail to be a finite field of square order such that the characteristic does not divide the order of the group. Update src/sage/combinat/symmetric_group_algebra.py use lowercase for the ValueError message trigger CI tests use cholesky(extended=True) we have renamed hermitian_decomposition to cholesky(extended=True) Update src/sage/combinat/symmetric_group_representations.py move .H to cached part Update src/sage/combinat/symmetric_group_representations.py move conjugate-transpose to cached part update doctests we're using .H in the unitary_change_of_basis, so the doctests need to reflect that partition, not self._partition forgot underscore, don't define _n define self._n use dimension instead when using the SGA version of specht, we don't have access to the yang_baxter_graph use self._specht with SGA specht module use representation from SGA using self._specht causes tests to fail remove print null_space.basis() appears to be working now with the change to using the symmetric group algebra to get the representation remove print statements use SGA vs. _spect for now include another doctest, print try printing U use A.H instead of A since U = A*A.H, we need to adjust the unitary representation to be A.H\rho(g)A.H.inverse(). also need to update the doctests remove unnecessary import, q, whitespace trigger CI checks use hermitian_decomposition in unitary rep'n we now have a standalone method to compute the Hermitian decomposition of a matrix, so we should use it when computing the change-of-basis matrix for a unitary representation implement unitary DFT of symmetric group over finite fields unitary DFT of symmetric group for number fields and finite fields - perform the unitary DFT of the symmetric group over finite fields and number fields - compute unitary representations of the symmetric group over finite fields - use real orthogonal representations as unitary representations for symmetric group trigger CI checks Update src/sage/matrix/matrix2.pyx add a SEEALSO since this is an extended form of the Cholesky decomposition Update src/sage/matrix/matrix2.pyx remove this break conditional in favor of conditional for while loop Update src/sage/matrix/matrix2.pyx replace always True with explicit conditional move hermitian_decomposition into cholesky this method for decomposition a Hermitian matrix U = AA* is similar in spirit to the Cholesky decomposition, but extends it to work over finite fields of square order. Update src/sage/matrix/matrix2.pyx yes, just forgot to update this initialize row to -1 Update src/sage/matrix/matrix2.pyx add word "package" to GAP ``forms`` Update src/sage/matrix/matrix2.pyx change \ast to * for consistency Trigger CI tests Trigger CI tests Update src/sage/matrix/matrix2.pyx use Matrix handle 1x1 case Update src/sage/matrix/matrix2.pyx cache the rank of the matrix since are computing it anyways add colon after Traceback forgot the colon fail cases should be tests, not examples Update src/sage/matrix/matrix2.pyx add hermitian_decomposition method given a Hermitian matrix U, returns a matrix A such that U = AA* use translated GAP source code for BaseChangeCanonical raise ValueError for non full rank matrices this method does not work for singular matrices. since we are already computing the rank in the row reduction, we can just check at the end whether the rank is full, and if not exit with a ValueError. this may be overcautious - it's not clear that this won't work for some singular matrices. there are cases where it certainly doesn't work, and we include one in the doctest. add example for singular case this example is currently failing and needs to be caught remove unnecessary import, singularity check if the matrix `U` is singular, the process will still result in a matrix but it will fail to have the expected property, namely `B*B.H == U`. Update src/sage/matrix/matrix2.pyx avoid import, directly call `sqrt` method, don't extend to keep result in `ZZ` fix example this was using the output from the `forms` package, when it should be from the GAP source translation. change examples there is a failing example due to the fact that the matrix is singular with q=3, and U = matrix(F,[[1,4,7],[4,1,4],[7,4,1]]). move _cholesky_extended_ff to hidden method move _cholesky_extended_ff to a separate hidden method, and just call it from inside cholesky if extended=true. add doctests and docs for the method separately. sparse for now, should be expanded upon. Update src/sage/matrix/matrix2.pyx add output for GF(3**2) case this case fails to have a lower triangular decomposition which has been checked by brute force. Update src/sage/matrix/matrix2.pyx move two examples over finite fields from tests the two examples computing the extended Cholesky decomposition over square order finite fields are in TESTS, and should be moved to EXAMPLES. Update src/sage/matrix/matrix2.pyx this is better and clarifies that the result of the Cholesky decomposition is lower triangular (and not possibly upper triangular). Update src/sage/matrix/matrix2.pyx add single whitespace before `extended` parameter Update src/sage/matrix/matrix2.pyx looks good. makes sense to point out the Cholesky decomposition might not exist, given that was our initial difficulty. 3 trailing whitespace move hermitian_decomposition into cholesky this method for decomposition a Hermitian matrix U = AA* is similar in spirit to the Cholesky decomposition, but extends it to work over finite fields of square order. add two examples add two examples of a Hermitian decomposition, one of which is not upper/lower triangular (so would be impossible with Cholesky decomposition) Update src/sage/matrix/matrix2.pyx yes, just forgot to update this initialize row to -1 Update src/sage/matrix/matrix2.pyx add word "package" to GAP ``forms`` Update src/sage/matrix/matrix2.pyx change \ast to * for consistency Trigger CI tests use self.__class__ within matrix.pyx file Trigger CI tests remove blank line Update src/sage/matrix/matrix2.pyx Update src/sage/matrix/matrix2.pyx Update src/sage/matrix/matrix2.pyx Update src/sage/matrix/matrix2.pyx Update src/sage/matrix/matrix2.pyx use Matrix handle 1x1 case format failing test case properly format failing test case properly by removing variables that hold results of computation. just call the function that raises the exception Update src/sage/combinat/symmetric_group_representations.py formatting changes (use ^T rather than .T, use `\rho` instead of `rho`). Update src/sage/combinat/symmetric_group_representations.py formatting changes Update src/sage/combinat/symmetric_group_representations.py use `.. MATH::` formatting instead of inline code. Update src/sage/combinat/symmetric_group_representations.py some formatting changes. add a return, and remove "this is". move docstring from __init__ to class level the description of the mathematics should move from the __init__ function which should only contain "Initialize `self`". whitespace, math formatting issues add unitary DFT examples add some examples with `q=5` and `q=7` and `n=3` for the unitary DFT. add unitary DFT example, case for p|n include an example of the unitary DFT under the `dft` method (rather than just _dft_unitary). include a test for a case GF(4)S_3, when 2|3!, so that the unitary DFT throws a NotImplementedError. Update src/sage/combinat/symmetric_group_algebra.py separate each `form` type into three separate bullets, and don't include None remove extra line from debugging Co-Authored-By: Travis Scrimshaw <[email protected]> Co-Authored-By: Dima Pasechnik <[email protected]>
1 parent 9cd86e9 commit ae69d72

File tree

3 files changed

+562
-9
lines changed

3 files changed

+562
-9
lines changed

src/sage/combinat/symmetric_group_algebra.py

+138-5
Original file line numberDiff line numberDiff line change
@@ -2035,22 +2035,60 @@ def dft(self, form=None, mult='l2r'):
20352035
(:meth:`antipode`) of the seminormal basis instead of
20362036
the seminormal basis.
20372037
2038-
EXAMPLES::
2038+
- ``form`` -- string (default: ``"modular"`` if `p|n!` else ``"seminormal"``);
2039+
one of the following:
20392040
2040-
sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
2041-
sage: QS3.dft()
2041+
* `"seminormal"` -- use the seminormal basis
2042+
* ``"modular"`` -- use the modular DFT, which uses the Peirce decomposition
2043+
of the group algebra into blocks with central orthogonal idempotents
2044+
* ``"unitary"`` -- use the unitary DFT, which computes the extended
2045+
Cholesky decomposition of an `S_n`-invariant symmetric bilinear form as
2046+
a change-of-basis matrix (for positive characteristics, it must be
2047+
a finite field of square order)
2048+
2049+
EXAMPLES:
2050+
2051+
The default is the seminormal DFT when the characteristic does not divide the group order::
2052+
2053+
sage: QQ_S3 = SymmetricGroupAlgebra(QQ, 3)
2054+
sage: QQ_S3.dft()
20422055
[ 1 1 1 1 1 1]
20432056
[ 1 1/2 -1 -1/2 -1/2 1/2]
20442057
[ 0 3/4 0 3/4 -3/4 -3/4]
20452058
[ 0 1 0 -1 1 -1]
20462059
[ 1 -1/2 1 -1/2 -1/2 -1/2]
20472060
[ 1 -1 -1 1 1 -1]
20482061
2062+
The unitary form works in characteristic zero::
2063+
2064+
sage: U = QQ_S3.dft(form='unitary'); U
2065+
[-1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2]
2066+
[ 1/3*sqrt3 1/6*sqrt3 -1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 1/6*sqrt3]
2067+
[ 0 1/2 0 1/2 -1/2 -1/2]
2068+
[ 0 1/2 0 -1/2 1/2 -1/2]
2069+
[ 1/3*sqrt3 -1/6*sqrt3 1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 -1/6*sqrt3]
2070+
[-1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2]
2071+
sage: U*U.H == 1
2072+
True
2073+
2074+
Over finite fields of square order with characteristic `p > n`, we can perform the unitary DFT::
2075+
2076+
sage: GF25_S3 = SymmetricGroupAlgebra(GF(5**2), 3)
2077+
sage: U = GF25_S3.dft(form='unitary'); U
2078+
[ 1 1 1 1 1 1]
2079+
[2*z2 + 4 z2 + 2 3*z2 + 1 4*z2 + 3 4*z2 + 3 z2 + 2]
2080+
[ 0 2 0 2 3 3]
2081+
[ 0 z2 + 1 0 4*z2 + 4 z2 + 1 4*z2 + 4]
2082+
[2*z2 + 4 4*z2 + 3 2*z2 + 4 4*z2 + 3 4*z2 + 3 4*z2 + 3]
2083+
[ 1 4 4 1 1 4]
2084+
sage: U*U.H == 1
2085+
True
2086+
20492087
Over fields of characteristic `p > 0` such that `p \mid n!`, we use the
20502088
modular Fourier transform (:issue:`37751`)::
20512089
2052-
sage: GF2S3 = SymmetricGroupAlgebra(GF(2), 3)
2053-
sage: GF2S3.dft()
2090+
sage: GF2_S3 = SymmetricGroupAlgebra(GF(2), 3)
2091+
sage: GF2_S3.dft()
20542092
[1 0 0 0 1 0]
20552093
[0 1 0 0 0 1]
20562094
[0 0 1 0 0 1]
@@ -2066,8 +2104,103 @@ def dft(self, form=None, mult='l2r'):
20662104
return self._dft_seminormal(mult=mult)
20672105
if form == "modular":
20682106
return self._dft_modular()
2107+
if form == "unitary":
2108+
return self._dft_unitary()
20692109
raise ValueError("invalid form (= %s)" % form)
20702110

2111+
def _dft_unitary(self):
2112+
"""
2113+
Return the unitary form of the discrete Fourier transform for ``self``.
2114+
2115+
EXAMPLES::
2116+
2117+
sage: QQ_S3 = SymmetricGroupAlgebra(QQ, 3)
2118+
sage: QQ_S3._dft_unitary()
2119+
[-1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2]
2120+
[ 1/3*sqrt3 1/6*sqrt3 -1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 1/6*sqrt3]
2121+
[ 0 1/2 0 1/2 -1/2 -1/2]
2122+
[ 0 1/2 0 -1/2 1/2 -1/2]
2123+
[ 1/3*sqrt3 -1/6*sqrt3 1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 -1/6*sqrt3]
2124+
[-1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2]
2125+
sage: GF49_S3 = SymmetricGroupAlgebra(GF(7**2), 3)
2126+
sage: GF49_S3._dft_unitary()
2127+
[5*z2 + 5 5*z2 + 5 5*z2 + 5 5*z2 + 5 5*z2 + 5 5*z2 + 5]
2128+
[2*z2 + 5 z2 + 6 5*z2 + 2 6*z2 + 1 6*z2 + 1 z2 + 6]
2129+
[ 0 4*z2 + 5 0 4*z2 + 5 3*z2 + 2 3*z2 + 2]
2130+
[ 0 3*z2 + 2 0 4*z2 + 5 3*z2 + 2 4*z2 + 5]
2131+
[2*z2 + 5 6*z2 + 1 2*z2 + 5 6*z2 + 1 6*z2 + 1 6*z2 + 1]
2132+
[5*z2 + 5 2*z2 + 2 2*z2 + 2 5*z2 + 5 5*z2 + 5 2*z2 + 2]
2133+
2134+
TESTS::
2135+
2136+
sage: QQ_S3 = SymmetricGroupAlgebra(QQ, 3)
2137+
sage: U = QQ_S3._dft_unitary()
2138+
sage: U*U.H == 1
2139+
True
2140+
sage: GF25_S3 = SymmetricGroupAlgebra(GF(5**2), 3)
2141+
sage: U = GF25_S3._dft_unitary()
2142+
sage: U*U.H == 1
2143+
True
2144+
sage: GF5_S3 = SymmetricGroupAlgebra(GF(5), 3)
2145+
sage: U = GF5_S3._dft_unitary()
2146+
Traceback (most recent call last):
2147+
...
2148+
ValueError: the base ring must be a finite field of square order
2149+
sage: Z5_S3 = SymmetricGroupAlgebra(Integers(5), 3)
2150+
sage: U = Z5_S3._dft_unitary()
2151+
Traceback (most recent call last):
2152+
...
2153+
ValueError: the base ring must be a finite field of square order
2154+
sage: F.<x> = FunctionField(GF(3))
2155+
sage: GF3_x_S3 = SymmetricGroupAlgebra(F, 5)
2156+
sage: U = GF3_x_S3._dft_unitary()
2157+
Traceback (most recent call last):
2158+
...
2159+
ValueError: the base ring must be a finite field of square order
2160+
sage: GF9_S3 = SymmetricGroupAlgebra(GF(3**2), 3)
2161+
sage: U = GF9_S3._dft_unitary()
2162+
Traceback (most recent call last):
2163+
...
2164+
NotImplementedError: not implemented when p|n!; dimension of invariant forms may be greater than one
2165+
"""
2166+
from sage.matrix.special import diagonal_matrix
2167+
F = self.base_ring()
2168+
G = self.group()
2169+
2170+
if F.characteristic() == 0:
2171+
from sage.misc.functional import sqrt
2172+
from sage.rings.number_field.number_field import NumberField
2173+
dft_matrix = self.dft()
2174+
diag = (dft_matrix * dft_matrix.H).diagonal()
2175+
primes_needed = {factor for d in diag for factor, _ in d.squarefree_part().factor()}
2176+
names = [f"sqrt{factor}" for factor in primes_needed]
2177+
x = PolynomialRing(QQ, 'x').gen()
2178+
K = NumberField([x**2 - d for d in primes_needed], names=names)
2179+
sqrt_diag_inv = diagonal_matrix([~sqrt(K(d)) for d in diag])
2180+
return sqrt_diag_inv * dft_matrix
2181+
2182+
# positive characteristic case
2183+
assert F.characteristic() > 0, "F must have positive characteristic"
2184+
if not (F.is_field() and F.is_finite() and F.order().is_square()):
2185+
raise ValueError("the base ring must be a finite field of square order")
2186+
if F.characteristic().divides(G.cardinality()):
2187+
raise NotImplementedError("not implemented when p|n!; dimension of invariant forms may be greater than one")
2188+
q = F.order().sqrt()
2189+
2190+
def conj_square_root(u):
2191+
if not u:
2192+
return F.zero()
2193+
z = F.multiplicative_generator()
2194+
k = u.log(z)
2195+
if k % (q+1) != 0:
2196+
raise ValueError(f"unable to factor as {u} is not in base field GF({q})")
2197+
return z ** ((k//(q+1)) % (q-1))
2198+
2199+
dft_matrix = self.dft()
2200+
sign_diag = (dft_matrix * dft_matrix.H).diagonal()
2201+
conj_sqrt_diag_inv = diagonal_matrix([~conj_square_root(d) for d in sign_diag])
2202+
return conj_sqrt_diag_inv * dft_matrix
2203+
20712204
def _dft_seminormal(self, mult='l2r'):
20722205
"""
20732206
Return the seminormal form of the discrete Fourier transform for ``self``.

0 commit comments

Comments
 (0)