From 989b6ea199030fff1fc8e95118eac0283ade67c4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 12 Jan 2016 00:52:36 -0600 Subject: [PATCH 1/5] Adding changes from #19821. --- src/sage/groups/matrix_gps/group_element.py | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/sage/groups/matrix_gps/group_element.py b/src/sage/groups/matrix_gps/group_element.py index 434c7ce8027..6ba66e4e716 100644 --- a/src/sage/groups/matrix_gps/group_element.py +++ b/src/sage/groups/matrix_gps/group_element.py @@ -322,8 +322,10 @@ def _mul_(self,other): [0 1] """ parent = self.parent() - return parent.element_class(parent, self._matrix * other._matrix, - check=False, convert=False) + M = self._matrix * other._matrix + # Make it immutable so the constructor doesn't make a copy + M.set_immutable() + return parent.element_class(parent, M, check=False, convert=False) def __invert__(self): """ @@ -347,7 +349,10 @@ def __invert__(self): [0 1] """ parent = self.parent() - return parent.element_class(parent, ~self._matrix, check=False, convert=False) + M = ~self._matrix + # Make it immutable so the constructor doesn't make a copy + M.set_immutable() + return parent.element_class(parent, M, check=False, convert=False) inverse = __invert__ @@ -427,14 +432,19 @@ def matrix(self): sage: F = GF(3); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] sage: G = MatrixGroup(gens) - sage: G.gen(0).matrix() + sage: m = G.gen(0).matrix(); m [1 0] [0 1] - sage: _.parent() + sage: m.parent() Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3 """ - g = self.gap() - m = g.matrix(self.base_ring()) + # We do a slightly specialized version of sage.libs.gap.element.GapElement.matrix() + # in order to use our current matrix space directly and avoid + # some overhead safety checks. + entries = self.gap().Flat() + MS = self.parent().matrix_space() + ring = MS.base_ring() + m = MS([x.sage(ring=ring) for x in entries]) m.set_immutable() return m From 6358d539440f548b4fac34cfcaed78a4563f2868 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 12 Jan 2016 04:36:29 -0600 Subject: [PATCH 2/5] Cythonized matrix_gp/group_element.py and simplified the class structure. --- src/module_list.py | 2 + src/sage/groups/affine_gps/group_element.py | 27 +- src/sage/groups/libgap_mixin.py | 170 ----- src/sage/groups/matrix_gps/group_element.py | 450 ------------ src/sage/groups/matrix_gps/group_element.pyx | 735 +++++++++++++++++++ 5 files changed, 762 insertions(+), 622 deletions(-) delete mode 100644 src/sage/groups/matrix_gps/group_element.py create mode 100644 src/sage/groups/matrix_gps/group_element.pyx diff --git a/src/module_list.py b/src/module_list.py index 8854f92f5f7..3954e7e9c91 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -409,6 +409,8 @@ def uname_specific(name, value, alternative): Extension('sage.groups.semimonomial_transformations.semimonomial_transformation', sources = ['sage/groups/semimonomial_transformations/semimonomial_transformation.pyx']), + Extension('sage.groups.matrix_gps.group_element', + sources = ['sage/groups/matrix_gps/group_element.pyx']), ################################### ## diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index 8e2a058b0de..8a84ab5aafc 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -40,9 +40,9 @@ from sage.matrix.matrix import is_Matrix from sage.misc.cachefunc import cached_method -from sage.groups.matrix_gps.group_element import MatrixGroupElement_base +from sage.structure.element import MultiplicativeGroupElement -class AffineGroupElement(MatrixGroupElement_base): +class AffineGroupElement(MultiplicativeGroupElement): """ An affine group element. @@ -416,3 +416,26 @@ def __cmp__(self, other): return c return cmp(self._b, other._b) + def list(self): + """ + Return list representation of ``self``. + + EXAMPLES:: + + sage: F = AffineGroup(3, QQ) + sage: g = F([1,2,3,4,5,6,7,8,0], [10,11,12]) + sage: g + [1 2 3] [10] + x |-> [4 5 6] x + [11] + [7 8 0] [12] + sage: g.matrix() + [ 1 2 3|10] + [ 4 5 6|11] + [ 7 8 0|12] + [--------+--] + [ 0 0 0| 1] + sage: g.list() + [[1, 2, 3, 10], [4, 5, 6, 11], [7, 8, 0, 12], [0, 0, 0, 1]] + """ + return [r.list() for r in self.matrix().rows()] + diff --git a/src/sage/groups/libgap_mixin.py b/src/sage/groups/libgap_mixin.py index 8a06581cac1..8d903873c3b 100644 --- a/src/sage/groups/libgap_mixin.py +++ b/src/sage/groups/libgap_mixin.py @@ -14,176 +14,6 @@ from sage.libs.all import libgap from sage.misc.cachefunc import cached_method - -class GroupElementMixinLibGAP(object): - - @cached_method - def order(self): - """ - Return the order of this group element, which is the smallest - positive integer `n` such that `g^n = 1`, or - +Infinity if no such integer exists. - - EXAMPLES:: - - sage: k = GF(7); - sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G - Matrix group over Finite Field of size 7 with 2 generators ( - [1 1] [1 0] - [0 1], [0 2] - ) - sage: G.order() - 21 - sage: G.gen(0).order(), G.gen(1).order() - (7, 3) - - sage: k = QQ; - sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G - Matrix group over Rational Field with 2 generators ( - [1 1] [1 0] - [0 1], [0 2] - ) - sage: G.order() - +Infinity - sage: G.gen(0).order(), G.gen(1).order() - (+Infinity, +Infinity) - - sage: gl = GL(2, ZZ); gl - General Linear Group of degree 2 over Integer Ring - sage: g = gl.gen(2); g - [1 1] - [0 1] - sage: g.order() - +Infinity - """ - order = self.gap().Order() - if order.IsInt(): - return order.sage() - else: - assert order.IsInfinity() - from sage.rings.all import Infinity - return Infinity - - def word_problem(self, gens=None): - r""" - Solve the word problem. - - This method writes the group element as a product of the - elements of the list ``gens``, or the standard generators of - the parent of self if ``gens`` is None. - - INPUT: - - - ``gens`` -- a list/tuple/iterable of elements (or objects - that can be converted to group elements), or ``None`` - (default). By default, the generators of the parent group - are used. - - OUTPUT: - - A factorization object that contains information about the - order of factors and the exponents. A ``ValueError`` is raised - if the group element cannot be written as a word in ``gens``. - - ALGORITHM: - - Use GAP, which has optimized algorithms for solving the word - problem (the GAP functions ``EpimorphismFromFreeGroup`` and - ``PreImagesRepresentative``). - - EXAMPLE:: - - sage: G = GL(2,5); G - General Linear Group of degree 2 over Finite Field of size 5 - sage: G.gens() - ( - [2 0] [4 1] - [0 1], [4 0] - ) - sage: G(1).word_problem([G.gen(0)]) - 1 - sage: type(_) - - - sage: g = G([0,4,1,4]) - sage: g.word_problem() - ([4 1] - [4 0])^-1 - - Next we construct a more complicated element of the group from the - generators:: - - sage: s,t = G.0, G.1 - sage: a = (s * t * s); b = a.word_problem(); b - ([2 0] - [0 1]) * - ([4 1] - [4 0]) * - ([2 0] - [0 1]) - sage: flatten(b) - [ - [2 0] [4 1] [2 0] - [0 1], 1, [4 0], 1, [0 1], 1 - ] - sage: b.prod() == a - True - - We solve the word problem using some different generators:: - - sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0]) - sage: a.word_problem([s,t,u]) - ([2 0] - [0 1])^-1 * - ([1 1] - [0 1])^-1 * - ([0 4] - [1 0]) * - ([2 0] - [0 1])^-1 - - We try some elements that don't actually generate the group:: - - sage: a.word_problem([t,u]) - Traceback (most recent call last): - ... - ValueError: word problem has no solution - - AUTHORS: - - - David Joyner and William Stein - - David Loeffler (2010): fixed some bugs - - Volker Braun (2013): LibGAP - """ - from sage.libs.gap.libgap import libgap - G = self.parent() - if gens: - gen = lambda i:gens[i] - H = libgap.Group([G(x).gap() for x in gens]) - else: - gen = G.gen - H = G.gap() - hom = H.EpimorphismFromFreeGroup() - preimg = hom.PreImagesRepresentative(self.gap()) - - if preimg.is_bool(): - assert preimg == libgap.eval('fail') - raise ValueError('word problem has no solution') - - result = [] - n = preimg.NumberSyllables().sage() - exponent_syllable = libgap.eval('ExponentSyllable') - generator_syllable = libgap.eval('GeneratorSyllable') - for i in range(n): - exponent = exponent_syllable(preimg, i+1).sage() - generator = gen(generator_syllable(preimg, i+1).sage() - 1) - result.append( (generator, exponent) ) - from sage.structure.factorization import Factorization - result = Factorization(result) - result._set_cr(True) - return result - - class GroupMixinLibGAP(object): @cached_method diff --git a/src/sage/groups/matrix_gps/group_element.py b/src/sage/groups/matrix_gps/group_element.py deleted file mode 100644 index 6ba66e4e716..00000000000 --- a/src/sage/groups/matrix_gps/group_element.py +++ /dev/null @@ -1,450 +0,0 @@ -""" -Matrix Group Elements - -EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens); G - Matrix group over Finite Field of size 3 with 2 generators ( - [1 0] [1 1] - [0 1], [0 1] - ) - sage: g = G([[1,1],[0,1]]) - sage: h = G([[1,2],[0,1]]) - sage: g*h - [1 0] - [0 1] - -You cannot add two matrices, since this is not a group operation. -You can coerce matrices back to the matrix space and add them -there:: - - sage: g + h - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for +: - 'FinitelyGeneratedMatrixGroup_gap_with_category.element_class' and - 'FinitelyGeneratedMatrixGroup_gap_with_category.element_class' - - sage: g.matrix() + h.matrix() - [2 0] - [0 2] - -Similarly, you cannot multiply group elements by scalars but you can -do it with the underlying matrices:: - - sage: 2*g - Traceback (most recent call last): - ... - TypeError: unsupported operand parent(s) for '*': 'Integer Ring' and 'Matrix group over Finite Field of size 3 with 2 generators ( - [1 0] [1 1] - [0 1], [0 1] - )' - -AUTHORS: - -- David Joyner (2006-05): initial version David Joyner - -- David Joyner (2006-05): various modifications to address William - Stein's TODO's. - -- William Stein (2006-12-09): many revisions. - -- Volker Braun (2013-1) port to new Parent, libGAP. -""" - -#***************************************************************************** -# Copyright (C) 2006 David Joyner and William Stein -# Copyright (C) 2013 Volker Braun -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.interfaces.gap import gap -from sage.structure.element import MultiplicativeGroupElement -from sage.matrix.matrix import Matrix, is_Matrix -from sage.structure.factorization import Factorization -from sage.structure.element import have_same_parent -from sage.libs.gap.element import GapElement, GapElement_List -from sage.misc.cachefunc import cached_method -from sage.groups.libgap_wrapper import ElementLibGAP -from sage.groups.libgap_mixin import GroupElementMixinLibGAP - - -def is_MatrixGroupElement(x): - """ - Test whether ``x`` is a matrix group element - - INPUT: - - - ``x`` -- anything. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.groups.matrix_gps.group_element import is_MatrixGroupElement - sage: is_MatrixGroupElement('helloooo') - False - - sage: G = GL(2,3) - sage: is_MatrixGroupElement(G.an_element()) - True - """ - return isinstance(x, MatrixGroupElement_base) - - -class MatrixGroupElement_base(MultiplicativeGroupElement): - """ - Base class for elements of matrix groups. - - You should use one of the two subclasses: - - * :class:`MatrixGroupElement_sage` implements the group - multiplication using Sage matrices. - - * :class:`MatrixGroupElement_gap` implements the group - multiplication using libGAP matrices. - - The base class only assumes that derived classes implement - :meth:`matrix`. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G.random_element() - sage: type(g) - - """ - def __hash__(self): - r""" - TESTS:: - - sage: MS = MatrixSpace(GF(3), 2) - sage: G = MatrixGroup([MS([1,1,0,1]), MS([1,0,1,1])]) - sage: g = G.an_element() - sage: hash(g) - 0 - """ - return hash(self.matrix()) - - def _repr_(self): - """ - Return string representation of this matrix. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G([[1, 1], [0, 1]]) - sage: g # indirect doctest - [1 1] - [0 1] - sage: g._repr_() - '[1 1]\n[0 1]' - """ - return str(self.matrix()) - - def _latex_(self): - r""" - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G([[1, 1], [0, 1]]) - sage: print g._latex_() - \left(\begin{array}{rr} - 1 & 1 \\ - 0 & 1 - \end{array}\right) - - Type ``view(g._latex_())`` to see the object in an - xdvi window (assuming you have latex and xdvi installed). - """ - return self.matrix()._latex_() - - def _act_on_(self, x, self_on_left): - """ - EXAMPLES:: - - sage: G = GL(4,7) - sage: G.0 * vector([1,2,3,4]) - (3, 2, 3, 4) - sage: v = vector(GF(7), [3,2,1,-1]) - sage: g = G.1 - sage: v * g == v * g.matrix() # indirect doctest - True - """ - if not is_MatrixGroupElement(x) and x not in self.parent().base_ring(): - try: - if self_on_left: - return self.matrix() * x - else: - return x * self.matrix() - except TypeError: - return None - - def _cmp_(self, other): - """ - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2) - sage: gens = [MS([1,0, 0,1]), MS([1,1, 0,1])] - sage: G = MatrixGroup(gens) - sage: g = G([1,1, 0,1]) - sage: h = G([1,1, 0,1]) - sage: g == h - True - sage: g == G.one() - False - """ - return cmp(self.matrix(), other.matrix()) - - __cmp__ = _cmp_ - - def list(self): - """ - Return list representation of this matrix. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G.0 - sage: g - [1 0] - [0 1] - sage: g.list() - [[1, 0], [0, 1]] - """ - return [list(_) for _ in self.matrix().rows()] - - -################################################################### -# -# Matrix groups elements implemented with Sage matrices -# -################################################################### - -class MatrixGroupElement_generic(MatrixGroupElement_base): - - def __init__(self, parent, M, check=True, convert=True): - r""" - Element of a matrix group over a generic ring. - - The group elements are implemented as Sage matrices. - - INPUT: - - - ``M`` -- a matrix. - - - ``parent`` -- the parent. - - - ``check`` -- bool (default: ``True``). If true does some - type checking. - - - ``convert`` -- bool (default: ``True``). If true convert - ``M`` to the right matrix space. - - TESTS:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G.random_element() - sage: TestSuite(g).run() - """ - if convert: - M = parent.matrix_space()(M) - if check: - if not is_Matrix(M): - raise TypeError('M must be a matrix') - if M.parent() is not parent.matrix_space(): - raise TypeError('M must be a in the matrix space of the group') - parent._check_matrix(M) - super(MatrixGroupElement_generic, self).__init__(parent) - if M.is_immutable(): - self._matrix = M - else: - self._matrix = M.__copy__() - self._matrix.set_immutable() - - def matrix(self): - """ - Obtain the usual matrix (as an element of a matrix space) - associated to this matrix group element. - - One reason to compute the associated matrix is that matrices - support a huge range of functionality. - - EXAMPLES:: - - sage: k = GF(7); G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]) - sage: g = G.0 - sage: g.matrix() - [1 1] - [0 1] - sage: parent(g.matrix()) - Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7 - - Matrices have extra functionality that matrix group elements - do not have:: - - sage: g.matrix().charpoly('t') - t^2 + 5*t + 1 - """ - return self._matrix - - def _mul_(self,other): - """ - Return the product of self and other, which must have identical - parents. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2) - sage: gens = [MS([1,0, 0,1]), MS([1,1, 0,1])] - sage: G = MatrixGroup(gens) - sage: g = G([1,1, 0,1]) - sage: h = G([1,1, 0,1]) - sage: g*h # indirect doctest - [1 2] - [0 1] - """ - parent = self.parent() - M = self._matrix * other._matrix - # Make it immutable so the constructor doesn't make a copy - M.set_immutable() - return parent.element_class(parent, M, check=False, convert=False) - - def __invert__(self): - """ - Return the inverse group element - - OUTPUT: - - A matrix group element. - - EXAMPLES:: - - sage: G = GL(2,3) - sage: g = G([1,2,1,0]); g - [1 2] - [1 0] - sage: g.__invert__() - [0 1] - [2 1] - sage: g * (~g) - [1 0] - [0 1] - """ - parent = self.parent() - M = ~self._matrix - # Make it immutable so the constructor doesn't make a copy - M.set_immutable() - return parent.element_class(parent, M, check=False, convert=False) - - inverse = __invert__ - -################################################################### -# -# Matrix group elements implemented in GAP -# -################################################################### - -class MatrixGroupElement_gap(GroupElementMixinLibGAP, MatrixGroupElement_base, ElementLibGAP): - - def __init__(self, parent, M, check=True, convert=True): - r""" - Element of a matrix group over a generic ring. - - The group elements are implemented as Sage matrices. - - INPUT: - - - ``M`` -- a matrix. - - - ``parent`` -- the parent. - - - ``check`` -- bool (default: ``True``). If true does some - type checking. - - - ``convert`` -- bool (default: ``True``). If true convert - ``M`` to the right matrix space. - - TESTS:: - - sage: MS = MatrixSpace(GF(3),2,2) - sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) - sage: G.gen(0) - [1 0] - [0 1] - sage: g = G.random_element() - sage: TestSuite(g).run() - """ - if isinstance(M, GapElement): - ElementLibGAP.__init__(self, parent, M) - return - if convert: - M = parent.matrix_space()(M) - from sage.libs.gap.libgap import libgap - M_gap = libgap(M) - if check: - if not is_Matrix(M): - raise TypeError('M must be a matrix') - if M.parent() is not parent.matrix_space(): - raise TypeError('M must be a in the matrix space of the group') - parent._check_matrix(M, M_gap) - ElementLibGAP.__init__(self, parent, M_gap) - - def __reduce__(self): - """ - Implement pickling. - - TESTS:: - - sage: MS = MatrixSpace(GF(3), 2, 2) - sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) - sage: loads(G.gen(0).dumps()) - [1 0] - [0 1] - """ - return (self.parent(), (self.matrix(),)) - - @cached_method - def matrix(self): - """ - Obtain the usual matrix (as an element of a matrix space) - associated to this matrix group element. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: m = G.gen(0).matrix(); m - [1 0] - [0 1] - sage: m.parent() - Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3 - """ - # We do a slightly specialized version of sage.libs.gap.element.GapElement.matrix() - # in order to use our current matrix space directly and avoid - # some overhead safety checks. - entries = self.gap().Flat() - MS = self.parent().matrix_space() - ring = MS.base_ring() - m = MS([x.sage(ring=ring) for x in entries]) - m.set_immutable() - return m - diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx new file mode 100644 index 00000000000..8fef7c3e505 --- /dev/null +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -0,0 +1,735 @@ +""" +Matrix Group Elements + +EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens); G + Matrix group over Finite Field of size 3 with 2 generators ( + [1 0] [1 1] + [0 1], [0 1] + ) + sage: g = G([[1,1],[0,1]]) + sage: h = G([[1,2],[0,1]]) + sage: g*h + [1 0] + [0 1] + +You cannot add two matrices, since this is not a group operation. +You can coerce matrices back to the matrix space and add them +there:: + + sage: g + h + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for +: + 'sage.groups.matrix_gps.group_element.MatrixGroupElement_gap' and + 'sage.groups.matrix_gps.group_element.MatrixGroupElement_gap' + + sage: g.matrix() + h.matrix() + [2 0] + [0 2] + +Similarly, you cannot multiply group elements by scalars but you can +do it with the underlying matrices:: + + sage: 2*g + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for '*': 'Integer Ring' and 'Matrix group over Finite Field of size 3 with 2 generators ( + [1 0] [1 1] + [0 1], [0 1] + )' + +AUTHORS: + +- David Joyner (2006-05): initial version David Joyner + +- David Joyner (2006-05): various modifications to address William + Stein's TODO's. + +- William Stein (2006-12-09): many revisions. + +- Volker Braun (2013-1) port to new Parent, libGAP. + +- Travis Scrimshaw (2015-01): reworks class hierarchy in order + to cythonize +""" + +#***************************************************************************** +# Copyright (C) 2006 David Joyner and William Stein +# Copyright (C) 2013 Volker Braun +# Copyright (C) 2016 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.element cimport MultiplicativeGroupElement, Element, MonoidElement, Matrix +from sage.structure.parent cimport Parent +from sage.libs.gap.element cimport GapElement, GapElement_List +from sage.groups.libgap_wrapper cimport ElementLibGAP + +from sage.matrix.matrix import is_Matrix +from sage.structure.factorization import Factorization +from sage.misc.cachefunc import cached_method +from sage.structure.element import have_same_parent + + +cpdef is_MatrixGroupElement(x): + """ + Test whether ``x`` is a matrix group element + + INPUT: + + - ``x`` -- anything. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.groups.matrix_gps.group_element import is_MatrixGroupElement + sage: is_MatrixGroupElement('helloooo') + False + + sage: G = GL(2,3) + sage: is_MatrixGroupElement(G.an_element()) + True + """ + return isinstance(x, (MatrixGroupElement_generic, MatrixGroupElement_gap)) + +################################################################### +# +# Matrix group elements implemented in Sage +# +################################################################### + +cdef class MatrixGroupElement_generic(MultiplicativeGroupElement): + """ + Element of a matrix group over a generic ring. + + The group elements are implemented as Sage matrices. + + INPUT: + + - ``M`` -- a matrix + + - ``parent`` -- the parent + + - ``check`` -- bool (default: ``True``); if ``True``, then + does some type checking + + - ``convert`` -- bool (default: ``True``); if ``True``, then + convert ``M`` to the right matrix space + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: g + [ 0 0 -1] + [ 1 0 -1] + [ 0 1 -1] + """ + def __init__(self, parent, M, check=True, convert=True): + r""" + Initialize ``self``. + + TESTS:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: TestSuite(g).run() + """ + if convert: + M = parent.matrix_space()(M) + if check: + if not is_Matrix(M): + raise TypeError('M must be a matrix') + if M.parent() is not parent.matrix_space(): + raise TypeError('M must be a in the matrix space of the group') + parent._check_matrix(M) + super(MatrixGroupElement_generic, self).__init__(parent) + if M.is_immutable(): + self._matrix = M + else: + self._matrix = M.__copy__() + self._matrix.set_immutable() + + cdef Matrix _matrix + + def __hash__(self): + r""" + TESTS:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: hash(g) + -2 + """ + return hash(self._matrix) + + def __reduce__(self): + """ + Implement pickling. + + TESTS:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: loads(g.dumps()) == g + True + """ + return (self.parent(), (self._matrix,)) + + def _repr_(self): + """ + Return string representation of this matrix. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: W.an_element() + [ 0 0 -1] + [ 1 0 -1] + [ 0 1 -1] + """ + return str(self._matrix) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: latex(g) + \left(\begin{array}{rrr} + 0 & 0 & -1 \\ + 1 & 0 & -1 \\ + 0 & 1 & -1 + \end{array}\right) + """ + return self._matrix._latex_() + + cpdef _act_on_(self, x, bint self_on_left): + """ + EXAMPLES:: + + sage: W = CoxeterGroup(['A',4], base_ring=ZZ) + sage: g = W.gen(0) + sage: g * vector([1,1,1,1]) + (0, 1, 1, 1) + sage: v = vector([3,2,1,-1]) + sage: g = W.gen(1) + sage: v * g == v * g.matrix() # indirect doctest + True + """ + if not is_MatrixGroupElement(x) and x not in self.parent().base_ring(): + try: + if self_on_left: + return self._matrix * x + else: + return x * self._matrix + except TypeError: + return None + + cpdef int _cmp_(self, Element other) except -2: + """ + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: TestSuite(g).run() + sage: h = W.gen(0) * W.gen(1) * W.gen(2) + sage: g == h + True + sage: a = W.gen(0) + sage: a == g + False + sage: a != g + True + """ + cdef MatrixGroupElement_generic x = self + cdef MatrixGroupElement_generic y = other + return cmp(x._matrix, y._matrix) + + __cmp__ = _cmp_ + + cpdef list list(self): + """ + Return list representation of this matrix. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.gen(0) + sage: g + [-1 1 0] + [ 0 1 0] + [ 0 0 1] + sage: g.list() + [[-1, 1, 0], [0, 1, 0], [0, 0, 1]] + """ + return [r.list() for r in self._matrix.rows()] + + cpdef matrix(self): + """ + Obtain the usual matrix (as an element of a matrix space) + associated to this matrix group element. + + One reason to compute the associated matrix is that matrices + support a huge range of functionality. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.gen(0) + sage: g.matrix() + [-1 1 0] + [ 0 1 0] + [ 0 0 1] + sage: parent(g.matrix()) + Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring + + Matrices have extra functionality that matrix group elements + do not have:: + + sage: g.matrix().charpoly('t') + t^3 - t^2 - t + 1 + """ + return self._matrix + + cpdef MonoidElement _mul_(self, MonoidElement other): + """ + Return the product of ``self`` and`` other``, which must + have identical parents. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.gen(0) + sage: h = W.an_element() + sage: g * h + [ 1 0 0] + [ 1 0 -1] + [ 0 1 -1] + """ + cdef Parent parent = self.parent() + cdef MatrixGroupElement_generic y = other + cdef Matrix M = self._matrix * y._matrix + # Make it immutable so the constructor doesn't make a copy + M.set_immutable() + return parent.element_class(parent, M, check=False, convert=False) + + def __invert__(self): + """ + Return the inverse group element + + OUTPUT: + + A matrix group element. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: ~g + [-1 1 0] + [-1 0 1] + [-1 0 0] + sage: g * ~g == W.one() + True + sage: ~g * g == W.one() + True + """ + cdef Parent parent = self.parent() + cdef Matrix M = ~self._matrix + # Make it immutable so the constructor doesn't make a copy + M.set_immutable() + return parent.element_class(parent, M, check=False, convert=False) + + inverse = __invert__ + +################################################################### +# +# Matrix group elements implemented in GAP +# +################################################################### + +cdef class MatrixGroupElement_gap(ElementLibGAP): + """ + Element of a matrix group over a generic ring. + + The group elements are implemented as wrappers around libGAP matrices. + + INPUT: + + - ``M`` -- a matrix + + - ``parent`` -- the parent + + - ``check`` -- bool (default: ``True``); if ``True`` does some + type checking + + - ``convert`` -- bool (default: ``True``); if ``True`` convert + ``M`` to the right matrix space + """ + def __init__(self, parent, M, check=True, convert=True): + r""" + Initialize ``self``. + + TESTS:: + + sage: MS = MatrixSpace(GF(3),2,2) + sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) + sage: G.gen(0) + [1 0] + [0 1] + sage: g = G.random_element() + sage: TestSuite(g).run() + """ + if isinstance(M, GapElement): + ElementLibGAP.__init__(self, parent, M) + return + if convert: + M = parent.matrix_space()(M) + from sage.libs.gap.libgap import libgap + M_gap = libgap(M) + if check: + if not is_Matrix(M): + raise TypeError('M must be a matrix') + if M.parent() is not parent.matrix_space(): + raise TypeError('M must be a in the matrix space of the group') + parent._check_matrix(M, M_gap) + ElementLibGAP.__init__(self, parent, M_gap) + + def __reduce__(self): + """ + Implement pickling. + + TESTS:: + + sage: MS = MatrixSpace(GF(3), 2, 2) + sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) + sage: loads(G.gen(0).dumps()) + [1 0] + [0 1] + """ + return (self.parent(), (self.matrix(),)) + + def __hash__(self): + r""" + TESTS:: + + sage: MS = MatrixSpace(GF(3), 2) + sage: G = MatrixGroup([MS([1,1,0,1]), MS([1,0,1,1])]) + sage: g = G.an_element() + sage: hash(g) + 0 + """ + return hash(self.matrix()) + + def _repr_(self): + """ + Return string representation of this matrix. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: g = G([[1, 1], [0, 1]]) + sage: g # indirect doctest + [1 1] + [0 1] + sage: g._repr_() + '[1 1]\n[0 1]' + """ + return str(self.matrix()) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: g = G([[1, 1], [0, 1]]) + sage: print g._latex_() + \left(\begin{array}{rr} + 1 & 1 \\ + 0 & 1 + \end{array}\right) + + Type ``view(g._latex_())`` to see the object in an + xdvi window (assuming you have latex and xdvi installed). + """ + return self.matrix()._latex_() + + cpdef _act_on_(self, x, bint self_on_left): + """ + EXAMPLES:: + + sage: G = GL(4,7) + sage: G.0 * vector([1,2,3,4]) + (3, 2, 3, 4) + sage: v = vector(GF(7), [3,2,1,-1]) + sage: g = G.1 + sage: v * g == v * g.matrix() # indirect doctest + True + """ + if not is_MatrixGroupElement(x) and x not in self.parent().base_ring(): + try: + if self_on_left: + return self.matrix() * x + else: + return x * self.matrix() + except TypeError: + return None + + cpdef int _cmp_(self, Element other) except -2: + """ + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2) + sage: gens = [MS([1,0, 0,1]), MS([1,1, 0,1])] + sage: G = MatrixGroup(gens) + sage: g = G([1,1, 0,1]) + sage: h = G([1,1, 0,1]) + sage: g == h + True + sage: g == G.one() + False + """ + return cmp(self.matrix(), other.matrix()) + + __cmp__ = _cmp_ + + @cached_method + def matrix(self): + """ + Obtain the usual matrix (as an element of a matrix space) + associated to this matrix group element. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: m = G.gen(0).matrix(); m + [1 0] + [0 1] + sage: m.parent() + Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3 + + sage: k = GF(7); G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]) + sage: g = G.0 + sage: g.matrix() + [1 1] + [0 1] + sage: parent(g.matrix()) + Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7 + + Matrices have extra functionality that matrix group elements + do not have:: + + sage: g.matrix().charpoly('t') + t^2 + 5*t + 1 + """ + # We do a slightly specialized version of sage.libs.gap.element.GapElement.matrix() + # in order to use our current matrix space directly and avoid + # some overhead safety checks. + entries = self.gap().Flat() + MS = self.parent().matrix_space() + ring = MS.base_ring() + m = MS([x.sage(ring=ring) for x in entries]) + m.set_immutable() + return m + + cpdef list list(self): + """ + Return list representation of this matrix. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: g = G.0 + sage: g + [1 0] + [0 1] + sage: g.list() + [[1, 0], [0, 1]] + """ + return [r.list() for r in self.matrix().rows()] + + @cached_method + def order(self): + """ + Return the order of this group element, which is the smallest + positive integer `n` such that `g^n = 1`, or + +Infinity if no such integer exists. + + EXAMPLES:: + + sage: k = GF(7); + sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G + Matrix group over Finite Field of size 7 with 2 generators ( + [1 1] [1 0] + [0 1], [0 2] + ) + sage: G.order() + 21 + sage: G.gen(0).order(), G.gen(1).order() + (7, 3) + + sage: k = QQ; + sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G + Matrix group over Rational Field with 2 generators ( + [1 1] [1 0] + [0 1], [0 2] + ) + sage: G.order() + +Infinity + sage: G.gen(0).order(), G.gen(1).order() + (+Infinity, +Infinity) + + sage: gl = GL(2, ZZ); gl + General Linear Group of degree 2 over Integer Ring + sage: g = gl.gen(2); g + [1 1] + [0 1] + sage: g.order() + +Infinity + """ + order = self.gap().Order() + if order.IsInt(): + return order.sage() + else: + assert order.IsInfinity() + from sage.rings.all import Infinity + return Infinity + + def word_problem(self, gens=None): + r""" + Solve the word problem. + + This method writes the group element as a product of the + elements of the list ``gens``, or the standard generators of + the parent of self if ``gens`` is None. + + INPUT: + + - ``gens`` -- a list/tuple/iterable of elements (or objects + that can be converted to group elements), or ``None`` + (default). By default, the generators of the parent group + are used. + + OUTPUT: + + A factorization object that contains information about the + order of factors and the exponents. A ``ValueError`` is raised + if the group element cannot be written as a word in ``gens``. + + ALGORITHM: + + Use GAP, which has optimized algorithms for solving the word + problem (the GAP functions ``EpimorphismFromFreeGroup`` and + ``PreImagesRepresentative``). + + EXAMPLE:: + + sage: G = GL(2,5); G + General Linear Group of degree 2 over Finite Field of size 5 + sage: G.gens() + ( + [2 0] [4 1] + [0 1], [4 0] + ) + sage: G(1).word_problem([G.gen(0)]) + 1 + sage: type(_) + + + sage: g = G([0,4,1,4]) + sage: g.word_problem() + ([4 1] + [4 0])^-1 + + Next we construct a more complicated element of the group from the + generators:: + + sage: s,t = G.0, G.1 + sage: a = (s * t * s); b = a.word_problem(); b + ([2 0] + [0 1]) * + ([4 1] + [4 0]) * + ([2 0] + [0 1]) + sage: flatten(b) + [ + [2 0] [4 1] [2 0] + [0 1], 1, [4 0], 1, [0 1], 1 + ] + sage: b.prod() == a + True + + We solve the word problem using some different generators:: + + sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0]) + sage: a.word_problem([s,t,u]) + ([2 0] + [0 1])^-1 * + ([1 1] + [0 1])^-1 * + ([0 4] + [1 0]) * + ([2 0] + [0 1])^-1 + + We try some elements that don't actually generate the group:: + + sage: a.word_problem([t,u]) + Traceback (most recent call last): + ... + ValueError: word problem has no solution + + AUTHORS: + + - David Joyner and William Stein + - David Loeffler (2010): fixed some bugs + - Volker Braun (2013): LibGAP + """ + from sage.libs.gap.libgap import libgap + G = self.parent() + if gens: + gen = lambda i:gens[i] + H = libgap.Group([G(x).gap() for x in gens]) + else: + gen = G.gen + H = G.gap() + hom = H.EpimorphismFromFreeGroup() + preimg = hom.PreImagesRepresentative(self.gap()) + + if preimg.is_bool(): + assert preimg == libgap.eval('fail') + raise ValueError('word problem has no solution') + + result = [] + n = preimg.NumberSyllables().sage() + exponent_syllable = libgap.eval('ExponentSyllable') + generator_syllable = libgap.eval('GeneratorSyllable') + for i in range(n): + exponent = exponent_syllable(preimg, i+1).sage() + generator = gen(generator_syllable(preimg, i+1).sage() - 1) + result.append( (generator, exponent) ) + result = Factorization(result) + result._set_cr(True) + return result + From 65ce4655663d34627215292696d3b1720b24ebc6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 18 Jan 2016 11:01:45 -0600 Subject: [PATCH 3/5] Fixing modform_hecketriangle due to changes. --- .../hecke_triangle_group_element.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py index 3e970989641..b0880c6fc5a 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -159,8 +159,8 @@ def __init__(self, parent, M, check=True, **kwargs): # Instead it is called here in the __init__ method of the element # (after the prelimenary checks). if check: - if self._matrix.determinant() != 1: - raise TypeError("The matrix is not an element of {}, it has determinant {} != 1.".format(parent, self._matrix.determinant())) + if self.matrix().determinant() != 1: + raise TypeError("The matrix is not an element of {}, it has determinant {} != 1.".format(parent, self.matrix().determinant())) self._word_S_T_data() @cached_method @@ -199,10 +199,10 @@ def _word_S_T_data(self): (((1, 1), (0, 1)), 1) """ res = [] - ID = self.parent().I()._matrix - T = self.parent().T()._matrix - S = self.parent().S()._matrix - M = self._matrix + ID = self.parent().I().matrix() + T = self.parent().T().matrix() + S = self.parent().S().matrix() + M = self.matrix() lam = self.parent().lam() zero = ZZ.zero() one = ZZ.one() @@ -428,7 +428,7 @@ def string_repr(self, method="default"): S*T^3*S*T^(-2) """ if method == "default": - return super(MatrixGroupElement_generic, self)._repr_() + return MatrixGroupElement_generic._repr_(self) elif method == "basic": (L, sgn) = self._word_S_T_data() @@ -1238,7 +1238,7 @@ def sign(self): [-1 0] [ 0 -1] """ - sgn = coerce_AA(self._matrix.trace()).sign() + sgn = coerce_AA(self.matrix().trace()).sign() if sgn > 0: return self.parent().I() @@ -2070,7 +2070,7 @@ def __neg__(self): [-lam 1] [ -1 0] """ - return self.parent().element_class(self.parent(), -self._matrix, check=False) + return self.parent().element_class(self.parent(), -self.matrix(), check=False) def __getitem__(self, key): r""" @@ -2089,7 +2089,7 @@ def __getitem__(self, key): sage: U[1][0].parent() Maximal Order in Number Field in lam with defining polynomial x^3 - x^2 - 2*x + 1 """ - return self._matrix[key] + return self.matrix()[key] def a(self): r""" @@ -2104,7 +2104,7 @@ def a(self): sage: U.a().parent() Maximal Order in Number Field in lam with defining polynomial x^3 - x^2 - 2*x + 1 """ - return self._matrix[0][0] + return self.matrix()[0][0] def b(self): r""" @@ -2119,7 +2119,7 @@ def b(self): sage: U.b().parent() Maximal Order in Number Field in lam with defining polynomial x^3 - x^2 - 2*x + 1 """ - return self._matrix[0][1] + return self.matrix()[0][1] def c(self): r""" @@ -2132,7 +2132,7 @@ def c(self): sage: U.c() 1 """ - return self._matrix[1][0] + return self.matrix()[1][0] def d(self): r""" @@ -2145,7 +2145,7 @@ def d(self): sage: U.d() 0 """ - return self._matrix[1][1] + return self.matrix()[1][1] def trace(self): r""" @@ -2160,7 +2160,7 @@ def trace(self): sage: G.S().trace() 0 """ - return self._matrix.trace() + return self.matrix().trace() def discriminant(self): r""" @@ -2194,7 +2194,7 @@ def is_translation(self, exclude_one=False): sage: (-HeckeTriangleGroup(n=7).I()).is_translation(exclude_one=True) False """ - a,b,c,d = self._matrix.list() + a,b,c,d = self.matrix().list() if not (c.is_zero() and a == d and (a.is_one() or (-a).is_one())): return False @@ -2476,7 +2476,7 @@ def is_simple(self): return False # The last condition is/should be equivalent to: - a,b,c,d = self._matrix.list() + a,b,c,d = self.matrix().list() return (coerce_AA(a) > 0 and coerce_AA(b) > 0 and coerce_AA(c) > 0 and coerce_AA(d) > 0) def is_hecke_symmetric(self): @@ -2669,12 +2669,12 @@ def rational_period_function(self, k): L1 = [] for v in self.simple_elements(): - a,b,c,d = v._matrix.list() + a,b,c,d = v.matrix().list() Q = c*z**2 + (d - a)*z - b s += Q**(-k/ZZ(2)) for v in self.inverse().simple_elements(): - a,b,c,d = v._matrix.list() + a,b,c,d = v.matrix().list() Q = c*z**2 + (d - a)*z - b s -= ZZ(-1)**(k/ZZ(2)) * Q**(-k/ZZ(2)) @@ -3032,7 +3032,7 @@ def fixed_points(self, embedded=False, order="default"): else: e = self.root_extension_field().gen() - a,b,c,d = self._matrix.list() + a,b,c,d = self.matrix().list() if order == "none": sgn = ZZ(1) @@ -3147,7 +3147,7 @@ def acton(self, tau): model = tau.model() tau = tau.to_model('UHP').coordinates() - a,b,c,d = self._matrix.list() + a,b,c,d = self.matrix().list() if tau == infinity: if c.is_zero(): From 11109267de4a9eee93b6a32126ff253e485f1910 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 18 Jan 2016 11:34:21 -0600 Subject: [PATCH 4/5] Fixing last doctests. --- src/sage/categories/primer.py | 12 +++++++----- src/sage/groups/matrix_gps/group_element.pyx | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index caa84bf43d1..9f3a4583d64 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -462,16 +462,18 @@ class implements: of classes for organizing the code. As we have seen above, the design of the class hierarchy is easy since it can be modelled upon the hierarchy of categories (bookshelves). Here is for example a piece of -the hierarchy of classes for an element of a group of matrices:: +the hierarchy of classes for an element of a group of permutations:: - sage: G = GL(2,ZZ) - sage: m = G.an_element() + sage: P = Permutations(4) + sage: m = P.an_element() sage: for cls in m.__class__.mro(): print cls - - + + + ... + ... ... diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx index 8fef7c3e505..3ec3195d891 100644 --- a/src/sage/groups/matrix_gps/group_element.pyx +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -184,7 +184,7 @@ cdef class MatrixGroupElement_generic(MultiplicativeGroupElement): sage: loads(g.dumps()) == g True """ - return (self.parent(), (self._matrix,)) + return (_unpickle_generic_element, (self.parent(), self._matrix,)) def _repr_(self): """ @@ -733,3 +733,19 @@ cdef class MatrixGroupElement_gap(ElementLibGAP): result._set_cr(True) return result +def _unpickle_generic_element(G, mat): + """ + Unpickle the element in ``G`` given by ``mat``. + + EXAMPLES:: + + sage: m1 = matrix(SR, [[1,2],[3,4]]) + sage: m2 = matrix(SR, [[1,3],[-1,0]]) + sage: G = MatrixGroup(m1, m2) + sage: m = G.an_element() + sage: from sage.groups.matrix_gps.group_element import _unpickle_generic_element + sage: _unpickle_generic_element(G, m.matrix()) == m + True + """ + return G.element_class(G, mat, False, False) + From 29f72539754e8efba97deb07a93d735af559a552 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 21 Jan 2016 10:54:21 -0600 Subject: [PATCH 5/5] Special case for dense matrices over ZZ and making sure the inverse is in the base ring. --- src/sage/groups/matrix_gps/group_element.pyx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx index 3ec3195d891..09127baedd2 100644 --- a/src/sage/groups/matrix_gps/group_element.pyx +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -76,6 +76,7 @@ from sage.matrix.matrix import is_Matrix from sage.structure.factorization import Factorization from sage.misc.cachefunc import cached_method from sage.structure.element import have_same_parent +from sage.rings.all import ZZ cpdef is_MatrixGroupElement(x): @@ -345,9 +346,25 @@ cdef class MatrixGroupElement_generic(MultiplicativeGroupElement): True sage: ~g * g == W.one() True + + sage: W = CoxeterGroup(['B',3]) + sage: W.base_ring() + Universal Cyclotomic Field + sage: g = W.an_element() + sage: ~g + [ -1 1 0] + [ -1 0 E(8) - E(8)^3] + [-E(8) + E(8)^3 0 1] """ cdef Parent parent = self.parent() - cdef Matrix M = ~self._matrix + cdef Matrix M = self._matrix + # We have a special method for dense matrices over ZZ + if M.base_ring() is ZZ and M.is_dense(): + M = M._invert_unit() + else: + M = ~M + if M.base_ring() is not parent.base_ring(): + M = M.change_ring(parent.base_ring()) # Make it immutable so the constructor doesn't make a copy M.set_immutable() return parent.element_class(parent, M, check=False, convert=False)