# This file was *autogenerated* from the file local_matrix_group.sage
from sage.all_cmdline import *   # import sage library
_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]>
#
#
#  The full text of the GPL is available at:
#
##############################################################################

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

"""

####################################################################################################
# Extension to UnitaryMatrixGroup_gap and GU()
####################################################################################################
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.

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

- 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

#    @cached_method
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

#    @cached_method
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):
# Note: UniversalCyclotomicField.is_finite() gives True, a bug?
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

"""
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]
[    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

- 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:

F = self.invariant_bilinear_form()
result = ( F == xadFx )
if result == True:
return result

# if result is not True this may be caused by differnt types

try:
Fmat = F.matrix()
except AttributeError:
Fmat = F
try:
except AttributeError:
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')

# -------------------------------------------------------------------------------
# Methods for test_suite
# -------------------------------------------------------------------------------
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

"""

#------------------------------------------------------------------------------------
# Test _check_matrix
#------------------------------------------------------------------------------------
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 )

#------------------------------------------------------------------------------------
# Test subgroup
#------------------------------------------------------------------------------------
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

###############################################################################
# General Unitary Group
###############################################################################

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):
# unitary groups in gap are defined for finite fields only
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