Path: blob/master/src/sage/groups/matrix_gps/orthogonal.py
8815 views
r"""1Orthogonal Linear Groups23The general orthogonal group `GO(n,R)` consists of all `n\times n`4matrices over the ring `R` preserving an `n`-ary positive definite5quadratic form. In cases where there are muliple non-isomorphic6quadratic forms, additional data needs to be specified to7disambiguate. The special orthogonal group is the normal subgroup of8matrices of determinant one.910In characteristics different from 2, a quadratic form is equivalent to11a bilinear symmetric form. Furthermore, over the real numbers a12positive definite quadratic form is equivalent to the diagonal13quadratic form, equivalent to the bilinear symmetric form defined by14the identity matrix. Hence, the orthogonal group `GO(n,\RR)` is the15group of orthogonal matrices in the usual sense.1617In the case of a finite field and if the degree `n` is even, then18there are two inequivalent quadratic forms and a third parameter ``e``19must be specified to disambiguate these two possibilities. The index20of `SO(e,d,q)` in `GO(e,d,q)` is `2` if `q` is odd, but `SO(e,d,q) =21GO(e,d,q)` if `q` is even.)2223.. warning::2425GAP and Sage use different notations:2627* GAP notation: The optional ``e`` comes first, that is, ``GO([e,]28d, q)``, ``SO([e,] d, q)``.2930* Sage notation: The optional ``e`` comes last, the standard Python31convention: ``GO(d, GF(q), e=0)``, ``SO( d, GF(q), e=0)``.3233EXAMPLES::3435sage: GO(3,7)36General Orthogonal Group of degree 3 over Finite Field of size 73738sage: G = SO( 4, GF(7), 1); G39Special Orthogonal Group of degree 4 and form parameter 1 over Finite Field of size 740sage: G.random_element() # random41[4 3 5 2]42[6 6 4 0]43[0 4 6 0]44[4 4 5 1]4546TESTS::4748sage: G = GO(3, GF(5))49sage: latex(G)50\text{GO}_{3}(\Bold{F}_{5})51sage: G = SO(3, GF(5))52sage: latex(G)53\text{SO}_{3}(\Bold{F}_{5})54sage: G = SO(4, GF(5), 1)55sage: latex(G)56\text{SO}_{4}(\Bold{F}_{5}, +)5758AUTHORS:5960- David Joyner (2006-03): initial version6162- David Joyner (2006-05): added examples, _latex_, __str__, gens,63as_matrix_group6465- William Stein (2006-12-09): rewrite6667- Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring.68"""6970#*****************************************************************************71# Copyright (C) 2006 David Joyner and William Stein72# Copyright (C) 2013 Volker Braun <[email protected]>73#74# Distributed under the terms of the GNU General Public License (GPL)75# http://www.gnu.org/licenses/76#*****************************************************************************7778from sage.rings.all import ZZ, is_FiniteField79from sage.misc.latex import latex80from sage.misc.cachefunc import cached_method81from sage.groups.matrix_gps.named_group import (82normalize_args_vectorspace, NamedMatrixGroup_generic, NamedMatrixGroup_gap )83848586def normalize_args_e(degree, ring, e):87"""88Normalize the arguments that relate the choice of quadratic form89for special orthogonal groups over finite fields.9091INPUT:9293- ``degree`` -- integer. The degree of the affine group, that is,94the dimension of the affine space the group is acting on.9596- ``ring`` -- a ring. The base ring of the affine space.9798- ``e`` -- integer, one of `+1`, `0`, `-1`. Only relevant for99finite fields and if the degree is even. A parameter that100distinguishes inequivalent invariant forms.101102OUTPUT:103104The integer ``e`` with values required by GAP.105106TESTS::107108sage: from sage.groups.matrix_gps.orthogonal import normalize_args_e109sage: normalize_args_e(2, GF(3), +1)1101111sage: normalize_args_e(3, GF(3), 0)1120113sage: normalize_args_e(3, GF(3), +1)1140115sage: normalize_args_e(2, GF(3), 0)116Traceback (most recent call last):117...118ValueError: must have e=-1 or e=1 for even degree119"""120if is_FiniteField(ring) and degree%2 == 0:121if e not in (-1, +1):122raise ValueError('must have e=-1 or e=1 for even degree')123else:124e = 0125return ZZ(e)126127128129130131########################################################################132# General Orthogonal Group133########################################################################134135def GO(n, R, e=0, var='a'):136"""137Return the general orthogonal group.138139The general orthogonal group `GO(n,R)` consists of all `n\times n`140matrices over the ring `R` preserving an `n`-ary positive definite141quadratic form. In cases where there are muliple non-isomorphic142quadratic forms, additional data needs to be specified to143disambiguate.144145In the case of a finite field and if the degree `n` is even, then146there are two inequivalent quadratic forms and a third parameter147``e`` must be specified to disambiguate these two possibilities.148149.. note::150151This group is also available via ``groups.matrix.GO()``.152153INPUT:154155- ``n`` -- integer. The degree.156157- ``R`` -- ring or an integer. If an integer is specified, the158corresponding finite field is used.159160- ``e`` -- ``+1`` or ``-1``, and ignored by default. Only relevant161for finite fields and if the degree is even. A parameter that162distinguishes inequivalent invariant forms.163164OUTPUT:165166The general orthogonal group of given degree, base ring, and167choice of invariant form.168169EXAMPLES:170171sage: GO( 3, GF(7))172General Orthogonal Group of degree 3 over Finite Field of size 7173sage: GO( 3, GF(7)).order()174672175sage: GO( 3, GF(7)).gens()176(177[3 0 0] [0 1 0]178[0 5 0] [1 6 6]179[0 0 1], [0 2 1]180)181182TESTS::183184sage: groups.matrix.GO(2, 3, e=-1)185General Orthogonal Group of degree 2 and form parameter -1 over Finite Field of size 3186"""187degree, ring = normalize_args_vectorspace(n, R, var=var)188e = normalize_args_e(degree, ring, e)189if e == 0:190name = 'General Orthogonal Group of degree {0} over {1}'.format(degree, ring)191ltx = r'\text{{GO}}_{{{0}}}({1})'.format(degree, latex(ring))192else:193name = 'General Orthogonal Group of degree' + \194' {0} and form parameter {1} over {2}'.format(degree, e, ring)195ltx = r'\text{{GO}}_{{{0}}}({1}, {2})'.format(degree, latex(ring), '+' if e == 1 else '-')196if is_FiniteField(ring):197cmd = 'GO({0}, {1}, {2})'.format(e, degree, ring.characteristic())198return OrthogonalMatrixGroup_gap(degree, ring, False, name, ltx, cmd)199else:200return OrthogonalMatrixGroup_generic(degree, ring, False, name, ltx)201202203204########################################################################205# Special Orthogonal Group206########################################################################207208def SO(n, R, e=None, var='a'):209"""210Return the special orthogonal group.211212The special orthogonal group `GO(n,R)` consists of all `n\times n`213matrices with determint one over the ring `R` preserving an214`n`-ary positive definite quadratic form. In cases where there are215muliple non-isomorphic quadratic forms, additional data needs to216be specified to disambiguate.217218.. note::219220This group is also available via ``groups.matrix.SO()``.221222INPUT:223224- ``n`` -- integer. The degree.225226- ``R`` -- ring or an integer. If an integer is specified, the227corresponding finite field is used.228229- ``e`` -- ``+1`` or ``-1``, and ignored by default. Only relevant230for finite fields and if the degree is even. A parameter that231distinguishes inequivalent invariant forms.232233OUTPUT:234235The special orthogonal group of given degree, base ring, and choice of236invariant form.237238EXAMPLES::239240sage: G = SO(3,GF(5))241sage: G242Special Orthogonal Group of degree 3 over Finite Field of size 5243244sage: G = SO(3,GF(5))245sage: G.gens()246(247[2 0 0] [3 2 3] [1 4 4]248[0 3 0] [0 2 0] [4 0 0]249[0 0 1], [0 3 1], [2 0 4]250)251sage: G = SO(3,GF(5))252sage: G.as_matrix_group()253Matrix group over Finite Field of size 5 with 3 generators (254[2 0 0] [3 2 3] [1 4 4]255[0 3 0] [0 2 0] [4 0 0]256[0 0 1], [0 3 1], [2 0 4]257)258259TESTS::260261sage: groups.matrix.SO(2, 3, e=1)262Special Orthogonal Group of degree 2 and form parameter 1 over Finite Field of size 3263"""264degree, ring = normalize_args_vectorspace(n, R, var=var)265e = normalize_args_e(degree, ring, e)266if e == 0:267name = 'Special Orthogonal Group of degree {0} over {1}'.format(degree, ring)268ltx = r'\text{{SO}}_{{{0}}}({1})'.format(degree, latex(ring))269else:270name = 'Special Orthogonal Group of degree' + \271' {0} and form parameter {1} over {2}'.format(degree, e, ring)272ltx = r'\text{{SO}}_{{{0}}}({1}, {2})'.format(degree, latex(ring), '+' if e == 1 else '-')273if is_FiniteField(ring):274cmd = 'SO({0}, {1}, {2})'.format(e, degree, ring.characteristic())275return OrthogonalMatrixGroup_gap(degree, ring, True, name, ltx, cmd)276else:277return OrthogonalMatrixGroup_generic(degree, ring, True, name, ltx)278279280281########################################################################282# Orthogonal Group class283########################################################################284285class OrthogonalMatrixGroup_generic(NamedMatrixGroup_generic):286287@cached_method288def invariant_bilinear_form(self):289"""290Return the symmetric bilinear form preserved by the orthogonal291group.292293OUTPUT:294295A matrix.296297EXAMPLES::298299sage: GO(2,3,+1).invariant_bilinear_form()300[0 1]301[1 0]302sage: GO(2,3,-1).invariant_bilinear_form()303[2 1]304[1 1]305"""306from sage.matrix.constructor import identity_matrix307m = identity_matrix(self.base_ring(), self.degree())308m.set_immutable()309return m310311@cached_method312def invariant_quadratic_form(self):313"""314Return the quadratic form preserved by the orthogonal group.315316OUTPUT:317318A matrix.319320EXAMPLES::321322sage: GO(2,3,+1).invariant_quadratic_form()323[0 1]324[0 0]325sage: GO(2,3,-1).invariant_quadratic_form()326[1 1]327[0 2]328"""329from sage.matrix.constructor import identity_matrix330m = identity_matrix(self.base_ring(), self.degree())331m.set_immutable()332return m333334def _check_matrix(self, x, *args):335"""a336Check whether the matrix ``x`` is symplectic.337338See :meth:`~sage.groups.matrix_gps.matrix_group._check_matrix`339for details.340341EXAMPLES::342343sage: G = GO(4, GF(5), +1)344sage: G._check_matrix(G.an_element().matrix())345"""346if self._special and x.determinant() != 1:347raise TypeError('matrix must have determinant one')348F = self.invariant_bilinear_form()349if x * F * x.transpose() != F:350raise TypeError('matrix must be orthogonal with respect to the invariant form')351# TODO: check that quadratic form is preserved in characteristic two352353354class OrthogonalMatrixGroup_gap(OrthogonalMatrixGroup_generic, NamedMatrixGroup_gap):355356@cached_method357def invariant_bilinear_form(self):358"""359Return the symmetric bilinear form preserved by the orthogonal360group.361362OUTPUT:363364A matrix `M` such that, for every group element g, the365identity `g m g^T = m` holds. In characteristic different from366two, this uniquely determines the orthogonal group.367368EXAMPLES::369370sage: G = GO(4, GF(7), -1)371sage: G.invariant_bilinear_form()372[0 1 0 0]373[1 0 0 0]374[0 0 2 0]375[0 0 0 2]376377sage: G = GO(4, GF(7), +1)378sage: G.invariant_bilinear_form()379[0 1 0 0]380[1 0 0 0]381[0 0 6 0]382[0 0 0 2]383384sage: G = GO(4, QQ)385sage: G.invariant_bilinear_form()386[1 0 0 0]387[0 1 0 0]388[0 0 1 0]389[0 0 0 1]390391sage: G = SO(4, GF(7), -1)392sage: G.invariant_bilinear_form()393[0 1 0 0]394[1 0 0 0]395[0 0 2 0]396[0 0 0 2]397"""398m = self.gap().InvariantBilinearForm()['matrix'].matrix()399m.set_immutable()400return m401402@cached_method403def invariant_quadratic_form(self):404"""405Return the quadratic form preserved by the orthogonal group.406407OUTPUT:408409The matrix `Q` defining "orthogonal" as follows. The matrix410determines a quadratic form `q` on the natural vector space411`V`, on which `G` acts, by `q(v) = v Q v^t`. A matrix `M' is412an element of the orthogonal group if `q(v) = q(v M)` for all413`v \in V`.414415EXAMPLES::416417sage: G = GO(4, GF(7), -1)418sage: G.invariant_quadratic_form()419[0 1 0 0]420[0 0 0 0]421[0 0 1 0]422[0 0 0 1]423424sage: G = GO(4, GF(7), +1)425sage: G.invariant_quadratic_form()426[0 1 0 0]427[0 0 0 0]428[0 0 3 0]429[0 0 0 1]430431sage: G = GO(4, QQ)432sage: G.invariant_quadratic_form()433[1 0 0 0]434[0 1 0 0]435[0 0 1 0]436[0 0 0 1]437438sage: G = SO(4, GF(7), -1)439sage: G.invariant_quadratic_form()440[0 1 0 0]441[0 0 0 0]442[0 0 1 0]443[0 0 0 1]444"""445m = self.gap().InvariantQuadraticForm()['matrix'].matrix()446m.set_immutable()447return m448449450451452453