Path: blob/master/src/sage/matrix/matrix_integer_2x2.pyx
8815 views
"""1Two by two matrices over the integers.2"""34include "sage/ext/interrupt.pxi"5include "sage/ext/stdsage.pxi"6from cpython.list cimport *7from cpython.number cimport *8from cpython.ref cimport *910from sage.rings.all import polygen, QQ,ZZ11from sage.rings.integer cimport Integer12from sage.structure.element cimport ModuleElement, Element13from sage.structure.mutability cimport Mutability1415cimport matrix_dense16import matrix_dense1718cimport matrix1920from matrix_space import MatrixSpace21from sage.misc.lazy_attribute import lazy_attribute2223class MatrixSpace_ZZ_2x2_class(MatrixSpace):24"""25Return a space of 2x2 matrices over ZZ, whose elements are of26type sage.matrix.matrix_integer_2x2.Matrix_integer_2x2 instead of27sage.matrix.matrix_integer_dense.Matrix_integer_dense.2829NOTE: This class exists only for quickly creating and testing30elements of this type. Once these become the default for 2x231matrices over ZZ, this class should be removed.3233EXAMPLES::3435sage: sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()36Space of 2x2 integer matrices3738By trac ticket #12290, it is a unique parent::3940sage: from sage.matrix.matrix_integer_2x2 import MatrixSpace_ZZ_2x241sage: MatrixSpace_ZZ_2x2() is MatrixSpace_ZZ_2x2()42True43sage: M = MatrixSpace_ZZ_2x2()44sage: loads(dumps(M)) is M45True4647"""48@staticmethod49def __classcall__(cls):50"""51EXAMPLES::5253sage: from sage.matrix.matrix_integer_2x2 import MatrixSpace_ZZ_2x254sage: MatrixSpace_ZZ_2x2() is MatrixSpace_ZZ_2x2() # indirect doctest55True5657"""58return super(MatrixSpace,cls).__classcall__(cls)5960def __init__(self):61"""62EXAMPLES::6364sage: sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()65Space of 2x2 integer matrices66"""67MatrixSpace.__init__(self, ZZ, 2, 2, False)6869def _repr_(self):70"""71EXAMPLES::7273sage: sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()74Space of 2x2 integer matrices75"""76return "Space of 2x2 integer matrices"7778def _get_matrix_class(self):79"""80EXAMPLES::8182sage: A = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()83sage: A._get_matrix_class()84<type 'sage.matrix.matrix_integer_2x2.Matrix_integer_2x2'>85"""86return Matrix_integer_2x28788ZZ_2x2_parent = MatrixSpace_ZZ_2x2_class()89def MatrixSpace_ZZ_2x2():90"""91Return the space of 2x2 integer matrices. (This function92exists to maintain uniqueness of parents.)9394EXAMPLES::9596sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()97sage: M98Space of 2x2 integer matrices99sage: M is sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()100True101"""102return ZZ_2x2_parent103104105cdef class Matrix_integer_2x2(matrix_dense.Matrix_dense):106r"""107The \class{Matrix_integer_2x2} class derives from108\class{Matrix_dense}, and defines fast functionality for 2x2109matrices over the integers. Matrices are represented by four gmp110integer fields: a, b, c, and d.111112EXAMPLES::113114sage: MS = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()115sage: m = MS([1,2,3,4]) ; m116[1 2]117[3 4]118sage: TestSuite(m).run()119120We check that #13949 is fixed::121122sage: x = MS([1,2,3,4])123sage: y = MS([4,5,6,7])124sage: z = x * y125sage: z.set_immutable()126"""127########################################################################128# LEVEL 1 functionality129# x * __cinit__130# x * __init__131# x * __dealloc__132# x * set_unsafe133# x * get_unsafe134# x * def _pickle135# x * def _unpickle136########################################################################137138def __cinit__(self, parent, entries,copy, coerce):139mpz_init(self.a)140mpz_init(self.b)141mpz_init(self.c)142mpz_init(self.d)143self._entries = &self.a144self._parent = parent145self._base_ring = ZZ146self._nrows = 2147self._ncols = 2148149def __init__(self, parent, entries, copy, coerce):150"""151EXAMPLES::152153sage: MS = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()154sage: MS()155[0 0]156[0 0]157sage: MS(5)158[5 0]159[0 5]160sage: MS([3,4,5,6])161[3 4]162[5 6]163sage: MS([11,3])164Traceback (most recent call last):165...166TypeError: cannot construct an element of167Space of 2x2 integer matrices from [11, 3]!168"""169cdef Py_ssize_t i, n170171if entries is None:172entries = 0173174if not isinstance(entries, list):175try:176x = ZZ(entries)177is_list = 0178except TypeError:179if hasattr(entries, "list"):180entries = entries.list()181is_list = 1182else:183try:184entries = list(entries)185is_list = 1186except TypeError:187raise TypeError("entries must be coercible to a list or the base ring")188189else:190is_list = 1191192if is_list:193194if len(entries) != self._nrows * self._ncols:195raise TypeError("entries has the wrong length")196197if coerce:198mpz_set(self.a, (<Integer>ZZ(entries[0])).value)199mpz_set(self.b, (<Integer>ZZ(entries[1])).value)200mpz_set(self.c, (<Integer>ZZ(entries[2])).value)201mpz_set(self.d, (<Integer>ZZ(entries[3])).value)202else:203mpz_set(self.a, (<Integer>entries[0]).value)204mpz_set(self.b, (<Integer>entries[1]).value)205mpz_set(self.c, (<Integer>entries[2]).value)206mpz_set(self.d, (<Integer>entries[3]).value)207208else:209210mpz_set(self.a, (<Integer>x).value)211mpz_set_si(self.b, 0)212mpz_set_si(self.c, 0)213mpz_set(self.d, (<Integer>x).value)214215def __dealloc__(self):216mpz_clear(self.a)217mpz_clear(self.b)218mpz_clear(self.c)219mpz_clear(self.d)220221def testit(self):222print "testing",self._base_ring223224cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):225mpz_set(self._entries[(i << 1) | j], (<Integer>value).value)226227cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):228cdef Integer z229z = Integer.__new__(Integer)230mpz_set(z.value, self._entries[(i << 1) | j])231return z232233def _pickle(self):234"""235EXAMPLES:236sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()237sage: m = M([9,8,7,6])238sage: m == loads(dumps(m))239True240"""241return self.list(), 0242243def _unpickle(self, data, int version):244"""245EXAMPLES::246247sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()248sage: m = M(5)249sage: m == loads(dumps(m))250True251"""252if version == 0:253mpz_set(self.a, (<Integer>ZZ(data[0])).value)254mpz_set(self.b, (<Integer>ZZ(data[1])).value)255mpz_set(self.c, (<Integer>ZZ(data[2])).value)256mpz_set(self.d, (<Integer>ZZ(data[3])).value)257else:258raise RuntimeError("unknown matrix version")259260def __richcmp__(matrix.Matrix self, right, int op):261"""262EXAMPLES::263264sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()265sage: m = M([1,2,3,4])266sage: n = M([-1,2,3,4])267sage: m < n268False269sage: m > n270True271sage: m == m272True273"""274return self._richcmp(right, op)275276def __hash__(self):277"""278Return a hash of self.279280EXAMPLES::281282sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()283sage: m = M([1,2,3,4])284sage: m.set_immutable()285sage: m.__hash__()2868287"""288return self._hash()289290def __iter__(self):291"""292Return an iterator over the entries of self.293294EXAMPLES::295296sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()297sage: m = M([1,2,3,4])298sage: m.__iter__()299<listiterator object at ...>300"""301return iter(self.list())302303cdef Matrix_integer_2x2 _new_c(self):304cdef Matrix_integer_2x2 x305x = <Matrix_integer_2x2> Matrix_integer_2x2.__new__(Matrix_integer_2x2, self._parent, None, False, False)306x._parent = self._parent307x._nrows = 2308x._ncols = 2309return x310311312########################################################################313# LEVEL 2 functionality314# x * cdef _add_315# * cdef _mul_316# * cdef _cmp_c_impl317# x * __neg__318# x * __invert__319# x * __copy__320# x * _multiply_classical321# x * _list -- copy of the list of underlying elements322# * _dict -- copy of the sparse dictionary of underlying elements323########################################################################324325def __copy__(self):326"""327Return a copy of self.328329EXAMPLES::330331sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()332sage: m = M([1,3,4,5])333sage: n = copy(m)334sage: n == m335True336sage: n is m337False338sage: m.is_mutable()339True340sage: n.is_mutable()341True342343The following test against a bug fixed in trac ticket #12290::344345sage: n.base_ring()346Integer Ring347348"""349cdef Matrix_integer_2x2 x350x = self._new_c()351mpz_set(x.a, self.a)352mpz_set(x.b, self.b)353mpz_set(x.c ,self.c)354mpz_set(x.d, self.d)355x._is_immutable = False356x._base_ring = self._base_ring357if self._subdivisions is not None:358x.subdivide(*self.subdivisions())359return x360361cpdef ModuleElement _add_(left, ModuleElement right):362"""363EXAMPLES::364365sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()366sage: M([8,7,6,5]) + M([4,5,6,7])367[12 12]368[12 12]369"""370cdef Matrix_integer_2x2 A371A = left._new_c()372mpz_add(A.a, left.a, (<Matrix_integer_2x2>right).a)373mpz_add(A.b, left.b, (<Matrix_integer_2x2>right).b)374mpz_add(A.c, left.c, (<Matrix_integer_2x2>right).c)375mpz_add(A.d, left.d, (<Matrix_integer_2x2>right).d)376return A377378cdef int _cmp_c_impl(left, Element right) except -2:379"""380EXAMPLES::381382sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()383sage: m = M([4,3,7,9])384sage: n = M([3,3,7,9])385sage: m < n386False387sage: m > n388True389sage: m == m390True391sage: m == n392False393394TEST:395396Check that :trac:`14688` is fixed::397398sage: from sage.matrix.matrix_integer_2x2 import MatrixSpace_ZZ_2x2399sage: M2ZSpace = MatrixSpace_ZZ_2x2()400sage: A = M2ZSpace([-5, -3, 7, 4])401sage: B = M2ZSpace([1,0,-2,1])402sage: A < B403True404"""405cdef int c = mpz_cmp(left.a, (<Matrix_integer_2x2>right).a) or \406mpz_cmp(left.b, (<Matrix_integer_2x2>right).b) or \407mpz_cmp(left.c, (<Matrix_integer_2x2>right).c) or \408mpz_cmp(left.d, (<Matrix_integer_2x2>right).d)409if c < 0: return -1410if c > 0: return 1411return 0412413def __neg__(self):414"""415Return the additive inverse of self.416417EXAMPLES::418419sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()420sage: m = M([1,-1,-1,1])421sage: -m422[-1 1]423[ 1 -1]424"""425cdef Matrix_integer_2x2 A426A = self._new_c()427mpz_neg(A.a, self.a)428mpz_neg(A.b, self.b)429mpz_neg(A.c, self.c)430mpz_neg(A.d, self.d)431return A432433def __invert__(self):434"""435Return the inverse of self, as a 2x2 matrix over QQ.436437EXAMPLES::438439sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()440sage: m = M([2,0,0,2])441sage: m^-1442[1/2 0]443[ 0 1/2]444sage: type(m^-1)445<type 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>446"""447MS = self._matrix_(QQ).parent()448D = self.determinant()449return MS([self.get_unsafe(1,1)/D, -self.get_unsafe(0,1)/D, -self.get_unsafe(1,0)/D, self.get_unsafe(0,0)/D], coerce=False, copy=False)450451def __invert__unit(self):452"""453If self is a unit, i.e. if its determinant is 1 or -1, return454the inverse of self as a 2x2 matrix over ZZ. If not, raise a455ZeroDivisionError.456457EXAMPLES::458459sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()460sage: m = M([1,8,3,25])461sage: m.__invert__unit()462[25 -8]463[-3 1]464sage: type(m.__invert__unit())465<type 'sage.matrix.matrix_integer_2x2.Matrix_integer_2x2'>466sage: m^-1467[25 -8]468[-3 1]469sage: type(m^-1)470<type 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>471sage: m.__invert__unit() == m^-1472True473sage: M([2,0,0,2]).__invert__unit()474Traceback (most recent call last):475...476ZeroDivisionError: Not a unit!477"""478cdef Matrix_integer_2x2 A479cdef Integer D480D = self.determinant()481if D.is_one():482A = self._new_c()483mpz_set(A.a, self.d)484mpz_neg(A.b, self.b)485mpz_neg(A.c, self.c)486mpz_set(A.d, self.a)487return A488489elif D.is_unit():490A = self._new_c()491mpz_neg(A.a, self.d)492mpz_set(A.b, self.b)493mpz_set(A.c, self.c)494mpz_neg(A.d, self.a)495return A496497else:498raise ZeroDivisionError("Not a unit!")499_invert_unit = __invert__unit500501def _multiply_classical(left, matrix.Matrix _right):502"""503Multiply the matrices left and right using the classical $O(n^3)$504algorithm.505506EXAMPLES::507508sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()509sage: m = M([9,7,6,4])510sage: n = M([7,1,3,0])511sage: m*n512[84 9]513[54 6]514sage: m._multiply_classical(n)515[84 9]516[54 6]517"""518cdef Matrix_integer_2x2 A, right519cdef mpz_t tmp520mpz_init(tmp)521522right = _right523A = left._new_c()524525mpz_mul(A.a, left.a, right.a)526mpz_mul(tmp, left.b, right.c)527mpz_add(A.a, A.a, tmp)528529mpz_mul(A.b, left.a, right.b)530mpz_mul(tmp, left.b, right.d)531mpz_add(A.b, A.b, tmp)532533mpz_mul(A.c, left.c, right.a)534mpz_mul(tmp, left.d, right.c)535mpz_add(A.c, A.c, tmp)536537mpz_mul(A.d, left.c, right.b)538mpz_mul(tmp, left.d, right.d)539mpz_add(A.d, A.d, tmp)540541mpz_clear(tmp)542return A543544def _list(self):545"""546EXAMPLES::547548sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()549sage: M([8,6,0,8])._list()550[8, 6, 0, 8]551"""552return [self.get_unsafe(0,0), self.get_unsafe(0,1),553self.get_unsafe(1,0), self.get_unsafe(1,1)]554555########################################################################556# LEVEL 3 functionality (Optional)557# x * cdef _sub_558# x * __deepcopy__559# * Matrix windows -- only if you need strassen for that base560# * Other functions (list them here):561# x * determinant562# x * charpoly563########################################################################564565cpdef ModuleElement _sub_(left, ModuleElement right):566"""567EXAMPLES::568569sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()570sage: M([3,4,5,6]) - M([1,2,3,4])571[2 2]572[2 2]573"""574cdef Matrix_integer_2x2 A575A = left._new_c()576mpz_sub(A.a, left.a, (<Matrix_integer_2x2>right).a)577mpz_sub(A.b, left.b, (<Matrix_integer_2x2>right).b)578mpz_sub(A.c, left.c, (<Matrix_integer_2x2>right).c)579mpz_sub(A.d, left.d, (<Matrix_integer_2x2>right).d)580return A581582def determinant(self):583"""584Return the determinant of self, which is just ad-bc.585586EXAMPLES::587588sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()589sage: M([9,0,8,7]).determinant()59063591"""592cdef Integer z593cdef mpz_t tmp594mpz_init(tmp)595z = Integer.__new__(Integer)596mpz_mul(z.value, self.a, self.d)597mpz_mul(tmp, self.b, self.c)598mpz_sub(z.value, z.value, tmp)599mpz_clear(tmp)600return z601602def trace(self):603"""604Return the trace of self, which is just a+d.605606EXAMPLES::607608sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()609sage: M([9,0,8,7]).trace()61016611"""612cdef Integer z = PY_NEW(Integer)613mpz_add(z.value, self._entries[0], self._entries[3])614return z615616def charpoly(self, var='x'):617"""618Return the charpoly of self as a polynomial in var. Since self619is 2x2, this is just var^2 - self.trace() * var +620self.determinant().621622EXAMPLES::623624sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()625sage: M([9,0,8,7]).charpoly()626x^2 - 16*x + 63627sage: M(range(4)).charpoly()628x^2 - 3*x - 2629sage: M(range(4)).charpoly('t')630t^2 - 3*t - 2631"""632t = polygen(ZZ,name=var)633return t*t - t*self.trace() + self.determinant()634635def __deepcopy__(self, *args, **kwds):636"""637EXAMPLES::638639sage: M = sage.matrix.matrix_integer_2x2.MatrixSpace_ZZ_2x2()640sage: m = M([5,5,5,5])641sage: n = deepcopy(m)642sage: n == m643True644sage: n is m645False646sage: m[0,0] == n[0,0]647True648sage: m[0,0] is n[0,0]649False650"""651return self.__copy__()652653654#######################################################################655656657