from sage.all_cmdline import *
_sage_const_2 = Integer(2); _sage_const_1 = Integer(1); _sage_const_0 = Integer(0)
"""
Local Matrix Groups
This module contains several extensions to classes inherited from sage matrix group classes.
It fills in missing methods
##############################################################################
# Copyright (C) 2016 Sebastian Oehms <[email protected]>
#
# Distributed under the terms of the GNU General Public License (GPL)
#
# The full text of the GPL is available at:
#
# http://www.gnu.org/licenses/
##############################################################################
This module contains extensions of the following classes
sage class | extended class
-------------------------------------------------------------------------------
UnitaryMatrixGroup_generic | local_UnitaryMatrixGroup_generic
UnitaryMatrixGroup_gap | local_UnitaryMatrixGroup_gap
This module contains the following new classes
new class | inherited from
-------------------------------------------------------------------------------
MatrixGroup_subgroup: | FinitelyGeneratedMatrixGroup_gap
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
from sage.groups.matrix_gps.unitary import *
from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap
from sage.groups.libgap_wrapper import ParentLibGAP
from lib.utils_gap_interface import *
class MatrixGroup_subgroup(FinitelyGeneratedMatrixGroup_gap):
"""
This class can be used to create subgroups of matrix_groups beeing generated by a finite list of generators in the
ambient group. It is an analogy to the PermutationGroup_subgroup class It can be used in cases of matrix groups
which don't pocess a subgroup attribute.
If you don't see this well formatted type
sage: print MatrixGroup_subgroup.__doc__
This class has two methods overwriting the corresponding methods of FinitelyGeneratedMatrixGroup_gap class.
- "__init__": to register the information corresponding to the ambient group
- "_repr_: to print a representing string containing the ambient group
INPUT (to the constructor):
- "ambient": the matrix group for which the subgroup shoul be craeted
- "generators": list of elements in the ambient group generating the subgroup to be defined
EXAMPLE:
sage: UCF = UniversalCyclotomicField()
sage: G = GL(2, UCF)
sage: M = matrix( UCF, [[1, E(12)], [0,-1]] ); M
[ 1 -E(12)^7]
[ 0 -1]
sage: S = G.subgroup([M])
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
.......
AttributeError: 'LinearMatrixGroup_generic_with_category' object has no attribute 'subgroup'
sage:
sage: from lib.local_matrix_group import *
sage: S = MatrixGroup_subgroup(G, [M])
sage: S
Subgroup of General Linear Group of degree 2 over Universal Cyclotomic Field generated by ([ 1 -E(12)^7]
[ 0 -1],)
sage:
TESTS:
sage: UCF = UniversalCyclotomicField()
sage: G = GL(2, UCF)
sage: M = matrix( UCF, [[1, E(12)], [0,-1]] )
sage: S = MatrixGroup_subgroup(G, [M])
sage: TestSuite(S).run()
AUTHOR
- Sebastian Oehms, Okt. 2016
"""
def __init__(self, ambient, generators ):
"""
Python constructor.
for more information type
sage: print MatrixGroup_subgroup.__doc__
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
for g in generators:
if g not in ambient:
raise ValueError("Generator %s is not in the group"%(g))
gap_gens = [libgap(matrix_gen) for matrix_gen in generators]
gap_group = libgap.Group(gap_gens)
base_ring = ambient.base_ring()
degree = ambient.degree()
FinitelyGeneratedMatrixGroup_gap.__init__(self, degree, base_ring, gap_group)
self._ambient = ambient
def _repr_(self):
"""
Return a string representation
If you don't see this well formatted type
sage: print MatrixGroup_subgroup._repr_.__doc__
OUTPUT:
String decribing self
TESTS:
sage: UCF = UniversalCyclotomicField()
sage: G = GL(2, UCF)
sage: M = matrix( UCF, [[1, E(12)], [0,-1]] )
sage: S = MatrixGroup_subgroup(G, [M])
sage: S
Subgroup of General Linear Group of degree 2 over Universal Cyclotomic Field generated by ([ 1 -E(12)^7]
[ 0 -1],)
sage:
"""
return "Subgroup of %s generated by:\n%s"%(self._ambient ,self.gens())
class local_UnitaryMatrixGroup_generic(UnitaryMatrixGroup_generic):
"""
This class is an extension of the sage class UnitaryMatrixGroup_generic
If you don't see this well formatted type
sage: print local_UnitaryMatrixGroup_generic.__doc__
This class contains the following additional methods
- _conjugate
- _adjoined
- subgroup
- invariant_bilinear_form / invariant_form
Furthermore the original method
- _check_matrix
has been overwritten since the original one does not behave correctly in the case of finite fields
EXAMPLES:
See the method documentation. For instance, type:
sage: print local_UnitaryMatrixGroup_generic.subgroup.__doc__
sage: print local_UnitaryMatrixGroup_generic.invariant_bilinea_form.__doc__
TESTS:
sage: G34 = GU(3,4)
sage: TestSuite(G33).run()
sage: UCF = UniversalCyclotomicField()
sage: G3UCF = GU(3, UCF)
sage: TestSuite(G3UCF).run()
sage: h=matrix(UCF, 3,3,[[0,0,1],[0,1,0],[1,0,0]] )
sage: G3h = GU(3, UCF, hermitian_form=h)
sage: TestSuite(G3h).run()
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
_hermitian_form_ = None
def subgroup(self, generators):
"""
Return the subgroup generated by the given generators
If you don't see this well formatted type
sage: print local_UnitaryMatrixGroup_generic.subgroup.__doc__
In case where self is an instance of ParentLibGAP this method is identical to the corresponding method of that class.
In all other cases an instance of the class MatrixGroup_subgroup is returned
INPUT:
- ``generators`` -- a list/tuple/iterable of group elements of self
OUTPUT:
The subgroup generated by ``generators`` as an in instance of
- FinitelyGeneratedMatrixGroup_gap if self is an instance of ParentLibGAP
- MatrixGroup_subgroup elsewise
EAMPLE:
sage: from lib.local_matrix_group import *
sage: UCF = UniversalCyclotomicField()
sage: M = matrix( UCF, [[0, 1], [1,0]] )
sage: GU2=GU(2,UCF, hermitian_form=M); GU2
General Unitary Group of degree 2 over Universal Cyclotomic Field with respect to hermitian form [0 1]
[1 0]
sage: S = GU2.subgroup( [M] ); S
Subgroup of General Unitary Group of degree 2 over Universal Cyclotomic Field with respect to hermitian form [0 1]
[1 0] generated by ([0 1]
[1 0],)
sage: isinstance( GU3, ParentLibGAP)
False
sage: isinstance( S, ParentLibGAP)
True
sage: isinstance( S, MatrixGroup_subgroup )
True
sage:
TESTS:
see the tests for the class
AUTHOR:
- Sebastian Oehms, Sept. 2016
"""
if isinstance( self, ParentLibGAP ):
try:
SubGroup = ParentLibGAP.subgroup( self, generators )
except:
SubGroup = MatrixGroup_subgroup(self, generators )
else:
SubGroup = MatrixGroup_subgroup(self, generators )
return SubGroup
def __set_hermitian_form__(self, hermitian_form ):
"""
This method registers the hermitian form to which should be kept invariant be self
"""
self._hermitian_form_ = hermitian_form
def invariant_bilinear_form(self):
"""
Return the sesquilinear form preserved by the unitary group.
If you don't see this well formatted type
sage: print local_UnitaryMatrixGroup_generic.invariant_bilinear_form.__doc__
If a hermitian_form has been set explicitely by the method __set_hermitian_form__ it will be returned.
Elsewise the gap function "InvariantSesquilinearForm" is used.
Compare the corresponding methods with respect to the orthogonal groups
OUTPUT:
the hermitian form as a matrix with entries in the base_ring
EXAMPLES:
sage: GU25 = GU(2,5)
sage: GU52.invariant_bilinear_form()
[0 0 0 0 1]
[0 0 0 1 0]
[0 0 1 0 0]
[0 1 0 0 0]
[1 0 0 0 0]
sage: GU3Q = GU(3,QQ)
sage: GU3Q.invariant_bilinear_form()
[1 0 0]
[0 1 0]
[0 0 1]
TESTS:
see the tests for the class
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
if self._hermitian_form_ != None:
return self._hermitian_form_
if self.degree() > _sage_const_1 :
try:
m = self.gap().InvariantSesquilinearForm()['matrix'].matrix()
except AttributeError:
m = self.one().matrix()
else:
m = matrix( _sage_const_1 ,_sage_const_1 , [_sage_const_1 ] )
m.set_immutable()
return m
def invariant_form(self):
"""
Return the sesquilinear form preserved by the unitary group. Compare the corresponding methods with respect to the
symplectic groups. It is identical to invariant_bilinear_form (duplicate names in sage). For more information type
sage: print local_UnitaryMatrixGroup_generic.invariant_bilinear_form.__doc__
"""
return self.invariant_bilinear_form()
def _conjugate(self, mat):
"""
This internal method calculates the elementwise conjugate of the matrix mat
INPUT:
- mat matrix with entries from the base_ring of self
OUTPUT:
- the conjugate of mat as matrix of the same type
EXAMPLES:
sage: from lib.local_matrix_group import *
sage: GU52 = GU(5,2)
sage: gl = GU52.some_elements()
sage: g = gl[0]; g
[1 0 0 0 0]
[0 a 0 0 0]
[0 0 1 0 0]
[0 0 0 a 0]
[0 0 0 0 1]
sage: GU52._conjugate( g.matrix() )
[ 1 0 0 0 0]
[ 0 a + 1 0 0 0]
[ 0 0 1 0 0]
[ 0 0 0 a + 1 0]
[ 0 0 0 0 1]
sage:
TESTS:
see the tests for the class
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
if isinstance(self.base_ring(), UniversalCyclotomicField):
result = mat.conjugate()
elif finite_field_sqrt( self.base_ring() ):
matconj = mat.dict()
for ind in matconj.keys():
matconj.update({ind:matconj[ind].frobenius()})
result = matrix(matconj)
else:
result = mat.conjugate()
return result
def _adjoint(self, mat):
"""
Return the adjoint (conjugate and transpose) matrix of mat.
INPUT:
- mat matrix with entries from the base_ring of self
OUTPUT:
- the conjugate and transpose of mat as matrix of the same type
EXAMPLES:
sage: from lib.local_matrix_group import *
sage: GU52 = GU(5,2)
sage: gl = GU52.gens()
sage: g = gl[1]; g
[0 1 0 0 0]
[a 0 1 0 1]
[1 0 1 0 0]
[1 0 0 0 0]
[0 0 0 1 0]
sage: GU52._adjoint( g.matrix() )
[ 0 a + 1 1 1 0]
[ 1 0 0 0 0]
[ 0 1 1 0 0]
[ 0 0 0 0 1]
[ 0 1 0 0 0]
sage:
TESTS:
see the tests for the class
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
result = self._conjugate(mat.transpose())
return result
def _check_matrix(self, x, *args):
"""
Check whether the matrix ``x`` is unitary. This method overwrites the original sage-method with the same name. In
addition to it
- it takes into acount a special user defined hermitian form
- it works well in the case of a finite field (which is not the case for the original version)
If you don't see this well formatted type
sage: print local_UnitaryMatrixGroup_generic._check_matrix.__doc__
To read the original docstring type:
sage: print sage.groups.matrix_gps.unitary.UnitaryMatrixGroup_generic._check_matrix.__doc__
Description of the bug wich should be avoided by this variation of the original method:
If an element of a finite unitary group is converted to the underlying matrix class it can not be converted
back to the unitary group since the method unitary for the matrix gives false (see example below)
Note: the unitary method attached to the matrix is not changed. It continues to returns false for elements of finite
unitary groups.
INPUT:
same as for the original method
OUTPUT:
same as for the original method
EXAMPLES:
First try with the original method to convert an element of a unitary group to a matrix and back:
sage: G32=GU(3,2)
sage: g1, g2 =G32.gens()
sage: g1m = g1.matrix(); g1m
[a 0 0]
[0 1 0]
[0 0 a]
sage: g1m.is_unitary()
False
sage: g1m in G32
False
Now, do the same thing using local_UnitaryMatrixGroup_generic._check_matrix.__doc__:
sage: from lib.local_matrix_group import *
sage: G32=GU(3,2)
sage: g1, g2 = G32.gens()
sage: g1m=g1.matrix()
sage: g1m.is_unitary()
False
sage: g1m in G32
True
TESTS:
see the tests for the class
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
try:
finite_field = finite_field_sqrt( self.base_ring() )
except:
finite_field = False
def check_unitary( self, x ):
"""
This function supports an alternative check to x.is_unitary()
for finite fields or explicit hermitian form
"""
if self._hermitian_form_ != None or finite_field:
xadjoint = self._adjoint( x )
F = self.invariant_bilinear_form()
xadFx = xadjoint * F * x
result = ( F == xadFx )
if result == True:
return result
try:
Fmat = F.matrix()
except AttributeError:
Fmat = F
try:
xadFxmat = xadFx.matrix()
except AttributeError:
xadFxmat = xadFx
diff = Fmat - xadFxmat
result = diff.is_zero()
return result
else:
return x.is_unitary()
if self._special and x.determinant() != _sage_const_1 :
raise TypeError('matrix must have determinant one')
if not check_unitary(self, x ):
raise TypeError('matrix must be unitary')
def _test_extensions(self, **options):
"""
Method called by TestSuite
If you don't see this well formatted type
sage: print CubicBraidGroup_class._test_constructions.__doc__
the following is checked:
- if _check_matrix works
- if subgroup works
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
try:
genList = self.gens()
except AttributeError:
genList = self.some_elements()
g = genList[_sage_const_0 ]
gm = g.matrix()
test_check_matrix = self._tester(**options)
test_check_matrix.assert_( gm in self )
SubGroup = self.subgroup( [gm] )
test_subgroup = self._tester(**options)
test_subgroup.assert_( SubGroup.ambient() == self )
class local_UnitaryMatrixGroup_gap(local_UnitaryMatrixGroup_generic, NamedMatrixGroup_gap):
pass
def GU(n, R, var='a', hermitian_form=None ):
"""
This function overwrites the sage-Funtion with the same name. It uses the extended classes
local_UnitaryMatrixGroup_gap and local_UnitaryMatrixGroup_generic in stead of the original classes
UnitaryMatrixGroup_gap and UnitaryMatrixGroup_generic additional features are:
- use a special hermitain form via keyword-paremeter: "hermitian_form= ..."
- ask for the hermitian form to which GU is related via method "invariant_form"
- define a subgroup of GU to a list of generators via method "subgroup"
to read to original docstring of GU type:
sage print sage.groups.matrix_gps.unitary.GU.__doc__
EXAMPLES:
sage: from lib.local_matrix_group import *
sage: K=UniversalCyclotomicField()
sage: h=matrix(K, 3,3,[[0,0,1],[0,1,0],[1,0,0]] )
sage: GU3h=GU(3, K, hermitian_form=h); GU3h
Unitary Group of degree 3 over Universal Cyclotomic Field with respect to hermitian form
[0 0 1]
[0 1 0]
[1 0 0]
sage: GU3=GU(3, K); GU3
General Unitary Group of degree 3 over Universal Cyclotomic Field
sage: GU3 == GU3h
False
sage: x=matrix(K, 3,3,[[0,1,0],[0,0,1],[1,0,0]] ); x
[0 1 0]
[0 0 1]
[1 0 0]
sage: x in GU3h
False
sage: x in GU3
True
AUTHOR
- Sebastian Oehms, Sept. 2016
"""
degree, ring = normalize_args_vectorspace(n, R, var=var)
if is_FiniteField(ring):
q = ring.cardinality()
if var not in ring:
ring = GF(q ** _sage_const_2 , name=var)
if hermitian_form == None:
name = 'General Unitary Group of degree {0} over {1}'.format(degree, ring)
ltx = r'\text{{GU}}_{{{0}}}({1})'.format(degree, latex(ring))
else:
name = 'Unitary Group of degree {0} over {1} with respect to hermitian form\n{2}'.format(degree, ring, hermitian_form)
ltx = r'\text{{GU}}_{{{0}}}({1},{2})'.format(degree, latex(ring), latex(hermitian_form))
if is_FiniteField(ring):
cmd = 'GU({0}, {1})'.format(degree, q)
UnitaryGroup = local_UnitaryMatrixGroup_gap(degree, ring, False, name, ltx, cmd )
else:
UnitaryGroup = local_UnitaryMatrixGroup_generic(degree, ring, False, name, ltx )
UnitaryGroup.__set_hermitian_form__(hermitian_form)
return UnitaryGroup