Path: blob/master/src/sage/matrix/matrix_double_dense.pyx
8815 views
"""1Dense matrices using a NumPy backend.234This serves as a base class for dense matrices over5Real Double Field and Complex Double Field.67AUTHORS:89- Jason Grout, Sep 2008: switch to NumPy backend, factored out the Matrix_double_dense class1011- Josh Kantor1213- William Stein: many bug fixes and touch ups.141516EXAMPLES::1718sage: b=Mat(RDF,2,3).basis()19sage: b[0]20[1.0 0.0 0.0]21[0.0 0.0 0.0]222324We deal with the case of zero rows or zero columns::2526sage: m = MatrixSpace(RDF,0,3)27sage: m.zero_matrix()28[]2930TESTS::3132sage: a = matrix(RDF,2,range(4), sparse=False)33sage: TestSuite(a).run()34sage: a = matrix(CDF,2,range(4), sparse=False)35sage: TestSuite(a).run()36"""3738#*****************************************************************************39# Copyright (C) 2004,2005,2006 Joshua Kantor <[email protected]>40#41# Distributed under the terms of the GNU General Public License (GPL)42# as published by the Free Software Foundation; either version 2 of43# the License, or (at your option) any later version.44# http://www.gnu.org/licenses/45#*****************************************************************************4647import math4849import sage.rings.real_double50import sage.rings.complex_double5152from matrix cimport Matrix53from sage.structure.element cimport ModuleElement,Vector54from constructor import matrix55from sage.modules.free_module_element import vector56cimport sage.structure.element57from matrix_space import MatrixSpace58from sage.misc.decorators import rename_keyword5960cimport numpy as cnumpy6162numpy=None63scipy=None6465# This is for the Numpy C API to work66cnumpy.import_array()676869cdef class Matrix_double_dense(matrix_dense.Matrix_dense):70"""71Base class for matrices over the Real Double Field and the Complex72Double Field. These are supposed to be fast matrix operations73using C doubles. Most operations are implemented using numpy which74will call the underlying BLAS on the system.7576This class cannot be instantiated on its own. The numpy matrix77creation depends on several variables that are set in the78subclasses.7980EXAMPLES::8182sage: m = Matrix(RDF, [[1,2],[3,4]])83sage: m**284[ 7.0 10.0]85[15.0 22.0]86sage: n= m^(-1); n87[-2.0 1.0]88[ 1.5 -0.5]89"""909192########################################################################93# LEVEL 1 functionality94# * __cinit__95# * __dealloc__96# * __init__97# * set_unsafe98# * get_unsafe99# * __richcmp__ -- always the same100# * __hash__ -- always simple101########################################################################102def __cinit__(self, parent, entries, copy, coerce):103"""104Set up a new matrix105"""106matrix_dense.Matrix_dense.__init__(self,parent)107return108109def __create_matrix__(self):110"""111Create a new uninitialized numpy matrix to hold the data for the class.112113This function assumes that self._numpy_dtypeint and114self._nrows and self._ncols have already been initialized.115116EXAMPLE:117In this example, we throw away the current matrix and make a118new uninitialized matrix representing the data for the class.::119120sage: a=matrix(RDF, 3, range(9))121sage: a.__create_matrix__()122"""123cdef cnumpy.npy_intp dims[2]124dims[0] = self._nrows125dims[1] = self._ncols126self._matrix_numpy = cnumpy.PyArray_SimpleNew(2, dims, self._numpy_dtypeint)127return128129def __dealloc__(self):130""" Deallocate any memory that was initialized."""131return132133def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.134return self._richcmp(right, op)135136def __hash__(self):137"""138Hash this matrix if it's immutable.139140EXAMPLES::141142sage: A = matrix(RDF,3,range(1,10))143sage: hash(A)144Traceback (most recent call last):145...146TypeError: mutable matrices are unhashable147sage: A.set_immutable()148sage: hash(A)14988150sage: A = matrix(CDF,3,range(1,10))151sage: hash(A)152Traceback (most recent call last):153...154TypeError: mutable matrices are unhashable155sage: A.set_immutable()156sage: hash(A)15788158"""159return self._hash()160161def LU_valid(self):162r"""163Returns ``True`` if the LU form of this matrix has164already been computed.165166EXAMPLES::167168sage: A = random_matrix(RDF,3) ; A.LU_valid()169False170sage: P, L, U = A.LU()171sage: A.LU_valid()172True173"""174return self.fetch('PLU_factors') is not None175176def __init__(self, parent, entries, copy, coerce):177"""178Fill the matrix with entries.179180The numpy matrix must have already been allocated.181182EXAMPLES::183184sage: matrix(RDF,3,range(9))185[0.0 1.0 2.0]186[3.0 4.0 5.0]187[6.0 7.0 8.0]188sage: matrix(CDF,3,3,2)189[2.0 0.0 0.0]190[0.0 2.0 0.0]191[0.0 0.0 2.0]192193TESTS::194195sage: matrix(RDF,3,0)196[]197sage: matrix(RDF,3,3,0)198[0.0 0.0 0.0]199[0.0 0.0 0.0]200[0.0 0.0 0.0]201sage: matrix(RDF,3,3,1)202[1.0 0.0 0.0]203[0.0 1.0 0.0]204[0.0 0.0 1.0]205sage: matrix(RDF,3,3,2)206[2.0 0.0 0.0]207[0.0 2.0 0.0]208[0.0 0.0 2.0]209sage: matrix(CDF,3,0)210[]211sage: matrix(CDF,3,3,0)212[0.0 0.0 0.0]213[0.0 0.0 0.0]214[0.0 0.0 0.0]215sage: matrix(CDF,3,3,1)216[1.0 0.0 0.0]217[0.0 1.0 0.0]218[0.0 0.0 1.0]219sage: matrix(CDF,3,3,range(9))220[0.0 1.0 2.0]221[3.0 4.0 5.0]222[6.0 7.0 8.0]223sage: matrix(CDF,2,2,[CDF(1+I)*j for j in range(4)])224[ 0.0 1.0 + 1.0*I]225[2.0 + 2.0*I 3.0 + 3.0*I]226"""227cdef Py_ssize_t i,j228cdef cnumpy.npy_intp dims[2]229dims[0] = self._nrows230dims[1] = self._ncols231if isinstance(entries,(tuple, list)):232if len(entries)!=self._nrows*self._ncols:233raise TypeError("entries has wrong length")234235if coerce:236for i from 0<=i<self._nrows:237for j from 0<=j<self._ncols:238self.set_unsafe(i,j,self._python_dtype(entries[i*self._ncols+j]))239else:240for i from 0<=i<self._nrows:241for j from 0<=j<self._ncols:242self.set_unsafe(i,j,entries[i*self._ncols+j])243244else:245cnumpy.PyArray_FILLWBYTE(self._matrix_numpy, 0)246247if entries is None:248z = self._python_dtype(0.0)249else:250try:251z = self._python_dtype(entries)252except TypeError:253raise TypeError("entries must be coercible to a list or float")254if z != 0:255if self._nrows != self._ncols:256raise TypeError("scalar matrix must be square")257for i from 0<=i<self._ncols:258self.set_unsafe(i,i,z)259260261262cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object value):263"""264Set the (i,j) entry to value without any bounds checking,265mutability checking, etc.266"""267# We assume that Py_ssize_t is the same as cnumpy.npy_intp268269# We must patch the ndarrayobject.h file so that the SETITEM270# macro does not have a semicolon at the end for this to work.271# Cython wraps the macro in a function that converts the272# returned int to a python object, which leads to compilation273# errors because after preprocessing you get something that274# looks like "););". This is bug275# http://scipy.org/scipy/numpy/ticket/918276277# We call the self._python_dtype function on the value since278# numpy does not know how to deal with complex numbers other279# than the built-in complex number type.280cdef int status281status = cnumpy.PyArray_SETITEM(self._matrix_numpy,282cnumpy.PyArray_GETPTR2(self._matrix_numpy, i, j),283self._python_dtype(value))284#TODO: Throw an error if status == -1285286287cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):288"""289Get the (i,j) entry without any bounds checking, etc.290"""291# We assume that Py_ssize_t is the same as cnumpy.npy_intp292return self._sage_dtype(cnumpy.PyArray_GETITEM(self._matrix_numpy,293cnumpy.PyArray_GETPTR2(self._matrix_numpy, i, j)))294295cdef Matrix_double_dense _new(self, int nrows=-1, int ncols=-1):296"""297Return a new uninitialized matrix with same parent as self.298299INPUT:300301nrows -- (default self._nrows) number of rows in returned matrix302ncols -- (default self._ncols) number of columns in returned matrix303304"""305cdef Matrix_double_dense m306if nrows == -1:307nrows = self._nrows308if ncols == -1:309ncols = self._ncols310parent = self.matrix_space(nrows, ncols)311m = self.__class__.__new__(self.__class__,parent,None,None,None)312return m313314315316317########################################################################318# LEVEL 2 functionality319# * def _pickle320# * def _unpickle321cpdef ModuleElement _add_(self, ModuleElement right):322"""323Add two matrices together.324325EXAMPLES::326327sage: A = matrix(RDF,3,range(1,10))328sage: A+A329[ 2.0 4.0 6.0]330[ 8.0 10.0 12.0]331[14.0 16.0 18.0]332"""333if self._nrows == 0 or self._ncols == 0:334return self.__copy__()335336cdef Matrix_double_dense M, _right, _left337_right = right338_left = self339340M = self._new()341M._matrix_numpy = _left._matrix_numpy + _right._matrix_numpy342return M343344cpdef ModuleElement _sub_(self, ModuleElement right):345"""346Return self - right347348349EXAMPLES::350351sage: A = matrix(RDF,3,range(1,10))352sage: (A-A).is_zero()353True354"""355if self._nrows == 0 or self._ncols == 0:356return self.__copy__()357358cdef Matrix_double_dense M,_right,_left359_right = right360_left = self361362M = self._new()363M._matrix_numpy = _left._matrix_numpy - _right._matrix_numpy364return M365366def __neg__(self):367"""368Negate this matrix369370EXAMPLES::371372sage: A = matrix(RDF,3,range(1,10))373sage: -A374[-1.0 -2.0 -3.0]375[-4.0 -5.0 -6.0]376[-7.0 -8.0 -9.0]377sage: B = -A ; (A+B).is_zero()378True379"""380if self._nrows == 0 or self._ncols == 0:381return self.__copy__()382383cdef Matrix_double_dense M384M = self._new()385M._matrix_numpy = -self._matrix_numpy386return M387388389# * cdef _cmp_c_impl390# x * __copy__391# * _list -- list of underlying elements (need not be a copy)392# * _dict -- sparse dictionary of underlying elements (need not be a copy)393########################################################################394# def _pickle(self): #unsure how to implement395# def _unpickle(self, data, int version): # use version >= 0 #unsure how to implement396######################################################################397cdef sage.structure.element.Matrix _matrix_times_matrix_(self, sage.structure.element.Matrix right):398"""399Multiply self*right as matrices.400401EXAMPLES::402403sage: A = matrix(RDF,3,range(1,10))404sage: B = matrix(RDF,3,range(1,13))405sage: A*B406[ 38.0 44.0 50.0 56.0]407[ 83.0 98.0 113.0 128.0]408[128.0 152.0 176.0 200.0]409"""410if self._ncols!=right._nrows:411raise IndexError("Number of columns of self must equal number of rows of right")412413if self._nrows == 0 or self._ncols == 0 or right._nrows == 0 or right._ncols == 0:414return self.matrix_space(self._nrows, right._ncols).zero_matrix()415416cdef Matrix_double_dense M,_right,_left417M = self._new(self._nrows, right._ncols)418_right = right419_left = self420global numpy421if numpy is None:422import numpy423424M._matrix_numpy = numpy.dot(_left._matrix_numpy, _right._matrix_numpy)425return M426427# cdef int _cmp_c_impl(self, Matrix right) except -2:428def __invert__(self):429"""430Invert this matrix.431432EXAMPLES::433434sage: A = Matrix(RDF, [[10, 0], [0, 100]])435sage: (~A).det()4360.001437438sage: A = matrix(RDF,3,[2,3,5,7,8,9,11,13,17]);A439[ 2.0 3.0 5.0]440[ 7.0 8.0 9.0]441[11.0 13.0 17.0]442sage: ~A443[ -2.71428571429 -2.0 1.85714285714]444[ 2.85714285714 3.0 -2.42857142857]445[-0.428571428571 -1.0 0.714285714286]446447Note that if this matrix is (nearly) singular, finding448its inverse will not help much and will give slightly different449answers on similar platforms depending on the hardware450and tuning options given to ATLAS::451452sage: A = matrix(RDF,3,range(1,10));A453[1.0 2.0 3.0]454[4.0 5.0 6.0]455[7.0 8.0 9.0]456457sage: A.determinant() < 10e-12458True459460461TESTS::462463sage: ~Matrix(RDF, 0,0)464[]465sage: ~Matrix(RDF, 0,3)466Traceback (most recent call last):467...468ArithmeticError: self must be a square matrix469"""470# see trac ticket 4502 --- there is an issue with the "#random" pragma that needs to be fixed471# as for the mathematical side, scipy v0.7 is expected to fix the invertibility failures472#473# sage: A = Matrix(RDF, [[1, 0], [0, 0]])474# sage: A.inverse().det() # random - on some computers, this will be invertible due to numerical error.475# Traceback (most recent call last):476# ...477# LinAlgError: singular matrix478# sage: A = matrix(RDF,3,range(1,10));A479# [1.0 2.0 3.0]480# [4.0 5.0 6.0]481# [7.0 8.0 9.0]482#483# sage: A.determinant() < 10e-12484# True485# sage: ~A # random - on some computers, this will be invertible due to numerical error.486# Traceback (most recent call last):487# ...488# ZeroDivisionError: singular matrix489#490if self._nrows != self._ncols:491raise ArithmeticError("self must be a square matrix")492if self._nrows == 0 and self._ncols == 0:493return self.__copy__()494495# Maybe we should cache the (P)LU decomposition and use scipy.lu_solve?496cdef Matrix_double_dense M497M = self._new()498global scipy499if scipy is None:500import scipy501import scipy.linalg502from numpy.linalg import LinAlgError503try: ## Standard error reporting for Sage.504M._matrix_numpy = scipy.linalg.inv(self._matrix_numpy)505except LinAlgError:506raise ZeroDivisionError("input matrix must be nonsingular")507return M508509def __copy__(self):510r"""511Returns a new copy of this matrix.512513EXAMPLES::514515sage: a = matrix(RDF,1,3, [1,2,-3])516sage: a517[ 1.0 2.0 -3.0]518sage: b = a.__copy__()519sage: b520[ 1.0 2.0 -3.0]521sage: b is a522False523sage: b == a524True525sage: b[0,0] = 3526sage: a[0,0] # note that a hasn't changed5271.0528529::530531sage: copy(MatrixSpace(RDF,0,0,sparse=False).zero_matrix())532[]533"""534if self._nrows == 0 or self._ncols == 0:535# Create a brand new empy matrix. This is needed to prevent a536# recursive loop: a copy of zero_matrix is asked otherwise.537return self.__class__(self.parent(), [], self._nrows, self._ncols)538539cdef Matrix_double_dense A540A = self._new(self._nrows, self._ncols)541A._matrix_numpy = self._matrix_numpy.copy()542if self._subdivisions is not None:543A.subdivide(*self.subdivisions())544return A545546547# def _list(self):548# def _dict(self):549550551########################################################################552# LEVEL 3 functionality (Optional)553# * cdef _sub_554# * __deepcopy__555# * __invert__556# * Matrix windows -- only if you need strassen for that base557# * Other functions (list them here):558#559# compute_LU(self)560#561########################################################################562563564def condition(self, p='frob'):565r"""566Returns the condition number of a square nonsingular matrix.567568Roughly speaking, this is a measure of how sensitive569the matrix is to round-off errors in numerical computations.570The minimum possible value is 1.0, and larger numbers indicate571greater sensitivity.572573INPUT:574575- ``p`` - default: 'frob' - controls which norm is used576to compute the condition number, allowable values are577'frob' (for the Frobenius norm), integers -2, -1, 1, 2,578positive and negative infinity. See output discussion579for specifics.580581OUTPUT:582583The condition number of a matrix is the product of a norm584of the matrix times the norm of the inverse of the matrix.585This requires that the matrix be square and invertible586(nonsingular, full rank).587588Returned value is a double precision floating point value589in ``RDF``, or ``Infinity``. Row and column sums described below are590sums of the absolute values of the entries, where the591absolute value of the complex number `a+bi` is `\sqrt{a^2+b^2}`.592Singular values are the "diagonal" entries of the "S" matrix in593the singular value decomposition.594595- ``p = 'frob'``: the default norm employed in computing596the condition number, the Frobenius norm, which for a597matrix `A=(a_{ij})` computes598599.. math::600601\left(\sum_{i,j}\left\lvert{a_{i,j}}\right\rvert^2\right)^{1/2}602603- ``p = 'sv'``: the quotient of the maximal and minimal singular value.604- ``p = Infinity`` or ``p = oo``: the maximum row sum.605- ``p = -Infinity`` or ``p = -oo``: the minimum column sum.606- ``p = 1``: the maximum column sum.607- ``p = -1``: the minimum column sum.608- ``p = 2``: the 2-norm, equal to the maximum singular value.609- ``p = -2``: the minimum singular value.610611ALGORITHM:612613Computation is performed by the ``cond()`` function of614the SciPy/NumPy library.615616EXAMPLES:617618First over the reals. ::619620sage: A = matrix(RDF, 4, [(1/4)*x^3 for x in range(16)]); A621[ 0.0 0.25 2.0 6.75]622[ 16.0 31.25 54.0 85.75]623[ 128.0 182.25 250.0 332.75]624[ 432.0 549.25 686.0 843.75]625sage: A.condition()6269923.88955...627sage: A.condition(p='frob')6289923.88955...629sage: A.condition(p=Infinity)63022738.5631sage: A.condition(p=-Infinity)63217.5633sage: A.condition(p=1)63412139.21...635sage: A.condition(p=-1)636550.0637sage: A.condition(p=2)6389897.8088...639sage: A.condition(p=-2)6400.000101032462...641642And over the complex numbers. ::643644sage: B = matrix(CDF, 3, [x + x^2*I for x in range(9)]); B645[ 0.0 1.0 + 1.0*I 2.0 + 4.0*I]646[ 3.0 + 9.0*I 4.0 + 16.0*I 5.0 + 25.0*I]647[6.0 + 36.0*I 7.0 + 49.0*I 8.0 + 64.0*I]648sage: B.condition()649203.851798...650sage: B.condition(p='frob')651203.851798...652sage: B.condition(p=Infinity)653369.55630...654sage: B.condition(p=-Infinity)6555.46112969...656sage: B.condition(p=1)657289.251481...658sage: B.condition(p=-1)65920.4566639...660sage: B.condition(p=2)661202.653543...662sage: B.condition(p=-2)6630.00493453005...664665Hilbert matrices are famously ill-conditioned, while666an identity matrix can hit the minimum with the right norm. ::667668sage: A = matrix(RDF, 10, [1/(i+j+1) for i in range(10) for j in range(10)])669sage: A.condition()6701.633...e+13671sage: id = identity_matrix(CDF, 10)672sage: id.condition(p=1)6731.0674675Return values are in `RDF`. ::676677sage: A = matrix(CDF, 2, range(1,5))678sage: A.condition() in RDF679True680681Rectangular and singular matrices raise errors if p is not 'sv'. ::682683sage: A = matrix(RDF, 2, 3, range(6))684sage: A.condition()685Traceback (most recent call last):686...687TypeError: matrix must be square if p is not 'sv', not 2 x 3688689sage: A.condition('sv')6907.34...691692sage: A = matrix(QQ, 5, range(25))693sage: A.is_singular()694True695sage: B = A.change_ring(CDF)696sage: B.condition()697Traceback (most recent call last):698...699LinAlgError: Singular matrix700701Improper values of ``p`` are caught. ::702703sage: A = matrix(CDF, 2, range(1,5))704sage: A.condition(p='bogus')705Traceback (most recent call last):706...707ValueError: condition number 'p' must be +/- infinity, 'frob', 'sv' or an integer, not bogus708sage: A.condition(p=632)709Traceback (most recent call last):710...711ValueError: condition number integer values of 'p' must be -2, -1, 1 or 2, not 632712713TESTS:714715Some condition numbers, first by the definition which also exercises716:meth:`norm`, then by this method. ::717718sage: A = matrix(CDF, [[1,2,4],[5,3,9],[7,8,6]])719sage: c = A.norm(2)*A.inverse().norm(2)720sage: d = A.condition(2)721sage: abs(c-d) < 1.0e-12722True723sage: c = A.norm(1)*A.inverse().norm(1)724sage: d = A.condition(1)725sage: abs(c-d) < 1.0e-12726True727"""728if not self.is_square() and p != 'sv':729raise TypeError("matrix must be square if p is not 'sv', not %s x %s" % (self.nrows(), self.ncols()))730global numpy731if numpy is None:732import numpy733import sage.rings.infinity734import sage.rings.integer735import sage.rings.real_double736if p == sage.rings.infinity.Infinity:737p = numpy.inf738elif p == -sage.rings.infinity.Infinity:739p = -numpy.inf740elif p == 'frob':741p = 'fro'742elif p == 'sv' :743p = None744else:745try:746p = sage.rings.integer.Integer(p)747except TypeError:748raise ValueError("condition number 'p' must be +/- infinity, 'frob', 'sv' or an integer, not %s" % p)749if p not in [-2,-1,1,2]:750raise ValueError("condition number integer values of 'p' must be -2, -1, 1 or 2, not %s" % p)751# may raise a LinAlgError if matrix is singular752c = numpy.linalg.cond(self._matrix_numpy, p=p)753if c == numpy.inf:754return sage.rings.infinity.Infinity755else:756return sage.rings.real_double.RDF(c)757758def norm(self, p=None):759r"""760Returns the norm of the matrix.761762INPUT:763764- ``p`` - default: 'frob' - controls which norm is computed,765allowable values are 'frob' (for the Frobenius norm),766integers -2, -1, 1, 2, positive and negative infinity. See767output discussion for specifics. The default (``p='frob'``)768is deprecated and will change to a default of ``p=2`` soon.769770OUTPUT:771772Returned value is a double precision floating point value773in ``RDF``. Row and column sums described below are774sums of the absolute values of the entries, where the775absolute value of the complex number `a+bi` is `\sqrt{a^2+b^2}`.776Singular values are the "diagonal" entries of the "S" matrix in777the singular value decomposition.778779- ``p = 'frob'``: the Frobenius norm, which for780a matrix `A=(a_{ij})` computes781782.. math::783784\left(\sum_{i,j}\left\lvert{a_{i,j}}\right\rvert^2\right)^{1/2}785786- ``p = Infinity`` or ``p = oo``: the maximum row sum.787- ``p = -Infinity`` or ``p = -oo``: the minimum column sum.788- ``p = 1``: the maximum column sum.789- ``p = -1``: the minimum column sum.790- ``p = 2``: the induced 2-norm, equal to the maximum singular value.791- ``p = -2``: the minimum singular value.792793ALGORITHM:794795Computation is performed by the ``norm()`` function of796the SciPy/NumPy library.797798EXAMPLES:799800First over the reals. ::801802sage: A = matrix(RDF, 3, range(-3, 6)); A803[-3.0 -2.0 -1.0]804[ 0.0 1.0 2.0]805[ 3.0 4.0 5.0]806sage: A.norm()807doctest:...: DeprecationWarning: The default norm will be changing from p='frob' to p=2. Use p='frob' explicitly to continue calculating the Frobenius norm.808See http://trac.sagemath.org/13643 for details.8098.30662386...810sage: A.norm(p='frob')8118.30662386...812sage: A.norm(p=Infinity)81312.0814sage: A.norm(p=-Infinity)8153.0816sage: A.norm(p=1)8178.0818sage: A.norm(p=-1)8196.0820sage: A.norm(p=2)8217.99575670...822sage: A.norm(p=-2) < 10^-15823True824825And over the complex numbers. ::826827sage: B = matrix(CDF, 2, [[1+I, 2+3*I],[3+4*I,3*I]]); B828[1.0 + 1.0*I 2.0 + 3.0*I]829[3.0 + 4.0*I 3.0*I]830sage: B.norm()8317.0832sage: B.norm(p='frob')8337.0834sage: B.norm(p=Infinity)8358.0836sage: B.norm(p=-Infinity)8375.01976483...838sage: B.norm(p=1)8396.60555127...840sage: B.norm(p=-1)8416.41421356...842sage: B.norm(p=2)8436.66189877...844sage: B.norm(p=-2)8452.14921023...846847Since it is invariant under unitary multiplication, the848Frobenius norm is equal to the square root of the sum of849squares of the singular values. ::850851sage: A = matrix(RDF, 5, range(1,26))852sage: f = A.norm(p='frob')853sage: U, S, V = A.SVD()854sage: s = sqrt(sum([S[i,i]^2 for i in range(5)]))855sage: abs(f-s) < 1.0e-12856True857858Return values are in `RDF`. ::859860sage: A = matrix(CDF, 2, range(4))861sage: A.norm() in RDF862True863864Improper values of ``p`` are caught. ::865866sage: A.norm(p='bogus')867Traceback (most recent call last):868...869ValueError: matrix norm 'p' must be +/- infinity, 'frob' or an integer, not bogus870sage: A.norm(p=632)871Traceback (most recent call last):872...873ValueError: matrix norm integer values of 'p' must be -2, -1, 1 or 2, not 632874"""875global numpy876if numpy is None:877import numpy878879if p is None:880from sage.misc.superseded import deprecation881msg = "The default norm will be changing from p='frob' to p=2. Use p='frob' explicitly to continue calculating the Frobenius norm."882deprecation(13643, msg)883# change to default of p=2 when deprecation period has elapsed884p='frob'885886import sage.rings.infinity887import sage.rings.integer888import sage.rings.real_double889if p == sage.rings.infinity.Infinity:890p = numpy.inf891elif p == -sage.rings.infinity.Infinity:892p = -numpy.inf893elif p == 'frob':894p = 'fro'895else:896try:897p = sage.rings.integer.Integer(p)898except TypeError:899raise ValueError("matrix norm 'p' must be +/- infinity, 'frob' or an integer, not %s" % p)900if not p in [-2,-1,1,2]:901raise ValueError("matrix norm integer values of 'p' must be -2, -1, 1 or 2, not %s" % p)902return sage.rings.real_double.RDF(numpy.linalg.norm(self._matrix_numpy, ord=p))903904def singular_values(self, eps=None):905r"""906Returns a sorted list of the singular values of the matrix.907908INPUT:909910- ``eps`` - default: ``None`` - the largest number which911will be considered to be zero. May also be set to the912string 'auto'. See the discussion below.913914OUTPUT:915916A sorted list of the singular values of the matrix, which are the917diagonal entries of the "S" matrix in the SVD decomposition. As such,918the values are real and are returned as elements of ``RDF``. The919list is sorted with larger values first, and since theory predicts920these values are always positive, for a rank-deficient matrix the921list should end in zeros (but in practice may not). The length of922the list is the minimum of the row count and column count for the923matrix.924925The number of non-zero singular values will be the rank of the926matrix. However, as a numerical matrix, it is impossible to927control the difference between zero entries and very small928non-zero entries. As an informed consumer it is up to you929to use the output responsibly. We will do our best, and give930you the tools to work with the output, but we cannot931give you a guarantee.932933With ``eps`` set to ``None`` you will get the raw singular934values and can manage them as you see fit. You may also set935``eps`` to any positive floating point value you wish. If you936set ``eps`` to 'auto' this routine will compute a reasonable937cutoff value, based on the size of the matrix, the largest938singular value and the smallest nonzero value representable939by the 53-bit precision values used. See the discussion940at page 268 of [WATKINS]_.941942See the examples for a way to use the "verbose" facility943to easily watch the zero cutoffs in action.944945ALGORITHM:946947The singular values come from the SVD decomposition948computed by SciPy/NumPy.949950EXAMPLES:951952Singular values close to zero have trailing digits that may vary953on different hardware. For exact matrices, the number of non-zero954singular values will equal the rank of the matrix. So for some of955the doctests we round the small singular values that ideally would956be zero, to control the variability across hardware.957958This matrix has a determinant of one. A chain of two or959three theorems implies the product of the singular values960must also be one. ::961962sage: A = matrix(QQ, [[ 1, 0, 0, 0, 0, 1, 3],963... [-2, 1, 1, -2, 0, -4, 0],964... [ 1, 0, 1, -4, -6, -3, 7],965... [-2, 2, 1, 1, 7, 1, -1],966... [-1, 0, -1, 5, 8, 4, -6],967... [ 4, -2, -2, 1, -3, 0, 8],968... [-2, 1, 0, 2, 7, 3, -4]])969sage: A.determinant()9701971sage: B = A.change_ring(RDF)972sage: sv = B.singular_values(); sv973[20.5239806589, 8.48683702854, 5.86168134845, 2.44291658993,9740.583197014472, 0.269332872866, 0.00255244880761]975sage: prod(sv)9761.0977978An exact matrix that is obviously not of full rank, and then979a computation of the singular values after conversion980to an approximate matrix. ::981982sage: A = matrix(QQ, [[1/3, 2/3, 11/3],983... [2/3, 1/3, 7/3],984... [2/3, 5/3, 27/3]])985sage: A.rank()9862987sage: B = A.change_ring(CDF)988sage: sv = B.singular_values()989sage: sv[0:2]990[10.1973039..., 0.487045871...]991sage: sv[2] < 1e-14992True993994A matrix of rank 3 over the complex numbers. ::995996sage: A = matrix(CDF, [[46*I - 28, -47*I - 50, 21*I + 51, -62*I - 782, 13*I + 22],997... [35*I - 20, -32*I - 46, 18*I + 43, -57*I - 670, 7*I + 3],998... [22*I - 13, -23*I - 23, 9*I + 24, -26*I - 347, 7*I + 13],999... [-44*I + 23, 41*I + 57, -19*I - 54, 60*I + 757, -11*I - 9],1000... [30*I - 18, -30*I - 34, 14*I + 34, -42*I - 522, 8*I + 12]])1001sage: sv = A.singular_values()1002sage: sv[0:3]1003[1440.733666, 18.4044034134, 6.83970779714]1004sage: (10^-15 < sv[3]) and (sv[3] < 10^-13)1005True1006sage: (10^-16 < sv[4]) and (sv[4] < 10^-14)1007True10081009A full-rank matrix that is ill-conditioned. We use this to1010illustrate ways of using the various possibilities for ``eps``,1011including one that is ill-advised. Notice that the automatically1012computed cutoff gets this (difficult) example slightly wrong.1013This illustrates the impossibility of any automated process always1014getting this right. Use with caution and judgement. ::10151016sage: entries = [1/(i+j+1) for i in range(12) for j in range(12)]1017sage: B = matrix(QQ, 12, 12, entries)1018sage: B.rank()1019121020sage: A = B.change_ring(RDF)1021sage: A.condition() > 1.6e16 or A.condition()1022True10231024sage: A.singular_values(eps=None) # abs tol 1e-161025[1.79537205956, 0.380275245955, 0.0447385487522, 0.00372231223789, 0.000233089089022, 1.11633574832e-05, 4.08237611034e-07, 1.12286106622e-08, 2.25196451406e-10, 3.11134627616e-12, 2.65006299814e-14, 1.00050293715e-16]10261027sage: A.singular_values(eps='auto') # abs tol 1e-161028[1.79537205956, 0.380275245955, 0.0447385487522, 0.00372231223789, 0.000233089089022, 1.11633574832e-05, 4.08237611034e-07, 1.12286106622e-08, 2.25196451406e-10, 3.11134627616e-12, 2.65006299814e-14, 0.0]10291030sage: A.singular_values(eps=1e-4)1031[1.79537205956, 0.380275245955, 0.0447385487522, 0.00372231223789, 0.000233089089022, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]10321033With Sage's "verbose" facility, you can compactly see the cutoff1034at work. In any application of this routine, or those that build upon1035it, it would be a good idea to conduct this exercise on samples.1036We also test here that all the values are returned in `RDF` since1037singular values are always real. ::10381039sage: A = matrix(CDF, 4, range(16))1040sage: set_verbose(1)1041sage: sv = A.singular_values(eps='auto'); sv1042verbose 1 (<module>) singular values,1043smallest-non-zero:cutoff:largest-zero,10442.2766...:6.2421...e-14:...1045[35.139963659, 2.27661020871, 0.0, 0.0]1046sage: set_verbose(0)10471048sage: all([s in RDF for s in sv])1049True10501051TESTS:10521053Bogus values of the ``eps`` keyword will be caught. ::10541055sage: A.singular_values(eps='junk')1056Traceback (most recent call last):1057...1058ValueError: could not convert string to float: junk10591060REFERENCES:10611062.. [WATKINS] Watkins, David S. Fundamentals of Matrix Computations,1063Third Edition. Wiley, Hoboken, New Jersey, 2010.10641065AUTHOR:10661067- Rob Beezer - (2011-02-18)1068"""1069from sage.misc.misc import verbose1070from sage.rings.real_double import RDF1071global scipy1072# get SVD decomposition, which is a cached quantity1073_, S, _ = self.SVD()1074diag = min(self._nrows, self._ncols)1075sv = [RDF(S[i,i]) for i in range(diag)]1076# no cutoff, send raw data back1077if eps is None:1078verbose("singular values, no zero cutoff specified", level=1)1079return sv1080# set cutoff as RDF element1081if eps == 'auto':1082if scipy is None: import scipy1083eps = 2*max(self._nrows, self._ncols)*scipy.finfo(float).eps*sv[0]1084eps = RDF(eps)1085# locate non-zero entries1086rank = 01087while rank < diag and sv[rank] > eps:1088rank = rank + 11089# capture info for watching zero cutoff behavior at verbose level 11090if rank == 0:1091small_nonzero = None1092else:1093small_nonzero = sv[rank-1]1094if rank < diag:1095large_zero = sv[rank]1096else:1097large_zero = None1098# convert small values to zero, then done1099for i in range(rank, diag):1100sv[i] = RDF(0)1101verbose("singular values, smallest-non-zero:cutoff:largest-zero, %s:%s:%s" % (small_nonzero, eps, large_zero), level=1)1102return sv11031104def LU(self):1105r"""1106Returns a decomposition of the (row-permuted) matrix as a product of1107a lower-triangular matrix ("L") and an upper-triangular matrix ("U").11081109OUTPUT:11101111For an `m\times n` matrix ``A`` this method returns a triple of1112immutable matrices ``P, L, U`` such that11131114- ``P*A = L*U``1115- ``P`` is a square permutation matrix, of size `m\times m`,1116so is all zeroes, but with exactly a single one in each1117row and each column.1118- ``L`` is lower-triangular, square of size `m\times m`,1119with every diagonal entry equal to one.1120- ``U`` is upper-triangular with size `m\times n`, i.e.1121entries below the "diagonal" are all zero.11221123The computed decomposition is cached and returned on1124subsequent calls, thus requiring the results to be immutable.11251126Effectively, ``P`` permutes the rows of ``A``. Then ``L``1127can be viewed as a sequence of row operations on this matrix,1128where each operation is adding a multiple of a row to a1129subsequent row. There is no scaling (thus 1's on the diagonal1130of ``L``) and no row-swapping (``P`` does that). As a result1131``U`` is close to being the result of Gaussian-elimination.1132However, round-off errors can make it hard to determine1133the zero entries of ``U``.11341135.. NOTE::11361137Sometimes this decomposition is written as ``A=P*L*U``,1138where ``P`` represents the inverse permutation and is1139the matrix inverse of the ``P`` returned by this method.1140The computation of this matrix inverse can be accomplished1141quickly with just a transpose as the matrix is orthogonal/unitary.11421143EXAMPLES::11441145sage: m = matrix(RDF,4,range(16))1146sage: P,L,U = m.LU()1147sage: P*m1148[12.0 13.0 14.0 15.0]1149[ 0.0 1.0 2.0 3.0]1150[ 8.0 9.0 10.0 11.0]1151[ 4.0 5.0 6.0 7.0]1152sage: L*U1153[12.0 13.0 14.0 15.0]1154[ 0.0 1.0 2.0 3.0]1155[ 8.0 9.0 10.0 11.0]1156[ 4.0 5.0 6.0 7.0]11571158Trac 10839 made this routine available for rectangular matrices. ::11591160sage: A = matrix(RDF, 5, 6, range(30)); A1161[ 0.0 1.0 2.0 3.0 4.0 5.0]1162[ 6.0 7.0 8.0 9.0 10.0 11.0]1163[12.0 13.0 14.0 15.0 16.0 17.0]1164[18.0 19.0 20.0 21.0 22.0 23.0]1165[24.0 25.0 26.0 27.0 28.0 29.0]1166sage: P, L, U = A.LU()1167sage: P1168[0.0 0.0 0.0 0.0 1.0]1169[1.0 0.0 0.0 0.0 0.0]1170[0.0 0.0 1.0 0.0 0.0]1171[0.0 0.0 0.0 1.0 0.0]1172[0.0 1.0 0.0 0.0 0.0]1173sage: L.zero_at(0) # Use zero_at(0) to get rid of signed zeros1174[ 1.0 0.0 0.0 0.0 0.0]1175[ 0.0 1.0 0.0 0.0 0.0]1176[ 0.5 0.5 1.0 0.0 0.0]1177[0.75 0.25 0.0 1.0 0.0]1178[0.25 0.75 0.0 0.0 1.0]1179sage: U.zero_at(0) # Use zero_at(0) to get rid of signed zeros1180[24.0 25.0 26.0 27.0 28.0 29.0]1181[ 0.0 1.0 2.0 3.0 4.0 5.0]1182[ 0.0 0.0 0.0 0.0 0.0 0.0]1183[ 0.0 0.0 0.0 0.0 0.0 0.0]1184[ 0.0 0.0 0.0 0.0 0.0 0.0]1185sage: P*A-L*U1186[0.0 0.0 0.0 0.0 0.0 0.0]1187[0.0 0.0 0.0 0.0 0.0 0.0]1188[0.0 0.0 0.0 0.0 0.0 0.0]1189[0.0 0.0 0.0 0.0 0.0 0.0]1190[0.0 0.0 0.0 0.0 0.0 0.0]1191sage: P.transpose()*L*U1192[ 0.0 1.0 2.0 3.0 4.0 5.0]1193[ 6.0 7.0 8.0 9.0 10.0 11.0]1194[12.0 13.0 14.0 15.0 16.0 17.0]1195[18.0 19.0 20.0 21.0 22.0 23.0]1196[24.0 25.0 26.0 27.0 28.0 29.0]11971198Trivial cases return matrices of the right size and1199characteristics. ::12001201sage: A = matrix(RDF, 5, 0, entries=0)1202sage: P, L, U = A.LU()1203sage: P.parent()1204Full MatrixSpace of 5 by 5 dense matrices over Real Double Field1205sage: L.parent()1206Full MatrixSpace of 5 by 5 dense matrices over Real Double Field1207sage: U.parent()1208Full MatrixSpace of 5 by 0 dense matrices over Real Double Field1209sage: P*A-L*U1210[]12111212The results are immutable since they are cached. ::12131214sage: P, L, U = matrix(RDF, 2, 2, range(4)).LU()1215sage: L[0,0] = 01216Traceback (most recent call last):1217...1218ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).1219sage: P[0,0] = 01220Traceback (most recent call last):1221...1222ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).1223sage: U[0,0] = 01224Traceback (most recent call last):1225...1226ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).1227"""1228global scipy, numpy1229cdef Matrix_double_dense P, L, U1230m = self._nrows1231n = self._ncols12321233# scipy fails on trivial cases1234if m == 0 or n == 0:1235P = self._new(m, m)1236for i in range(m):1237P[i,i]=11238P.set_immutable()1239L = P1240U = self._new(m,n)1241U.set_immutable()1242return P, L, U12431244PLU = self.fetch('PLU_factors')1245if not PLU is None:1246return PLU1247if scipy is None:1248import scipy1249import scipy.linalg1250if numpy is None:1251import numpy1252PM, LM, UM = scipy.linalg.lu(self._matrix_numpy)1253# Numpy has a different convention than we had with GSL1254# So we invert (transpose) the P to match our prior behavior1255# TODO: It's an awful waste to store a huge matrix for P, which1256# is just a simple permutation, really.1257P = self._new(m, m)1258L = self._new(m, m)1259U = self._new(m, n)1260P._matrix_numpy = PM.T.copy()1261L._matrix_numpy = numpy.ascontiguousarray(LM)1262U._matrix_numpy = numpy.ascontiguousarray(UM)1263PLU = (P, L, U)1264for M in PLU:1265M.set_immutable()1266self.cache('PLU_factors', PLU)1267return PLU12681269def eigenspaces_left(self, var='a', algebraic_multiplicity=False):1270r"""1271Computes the left eigenspaces of a matrix of double precision1272real or complex numbers (i.e. RDF or CDF).12731274.. warning::12751276This method returns eigenspaces that are all of1277dimension one, since it is impossible to ascertain1278if the numerical results belong to the same eigenspace.1279So this is deprecated in favor of the eigenmatrix routines,1280such as :meth:`sage.matrix.matrix2.Matrix.eigenmatrix_right`.12811282INPUT:12831284- ``var`` - ignored for numerical matrices1285- ``algebraic_multiplicity`` - must be set to ``False``1286for numerical matrices, and will raise an error otherwise.12871288OUTPUT:12891290Return a list of pairs ``(e, V)`` where ``e`` is a (complex)1291eigenvalue and ``V`` is the associated left eigenspace as a1292vector space.12931294No attempt is made to determine if an eigenvalue has multiplicity1295greater than one, so all the eigenspaces returned have dimension one.12961297The SciPy routines used for these computations produce eigenvectors1298normalized to have length 1, but on different hardware they may vary1299by a sign. So for doctests we have normalized output by creating an1300eigenspace with a canonical basis.13011302EXAMPLES:13031304This first test simply raises the deprecation warning. ::13051306sage: A = identity_matrix(RDF, 2)1307sage: es = A.eigenspaces_left()1308doctest:...: DeprecationWarning: Eigenspaces of RDF/CDF matrices are1309deprecated as of Sage version 5.0,1310please use "eigenmatrix_left" instead1311See http://trac.sagemath.org/11603 for details.13121313::13141315sage: m = matrix(RDF, [[-5, 3, 2, 8],[10, 2, 4, -2],[-1, -10, -10, -17],[-2, 7, 6, 13]])1316sage: spectrum = m.eigenspaces_left()1317sage: spectrum[0][0]13182.01319sage: (RDF^4).subspace(spectrum[0][1].basis())1320Vector space of degree 4 and dimension 1 over Real Double Field1321Basis matrix:1322[1.0 1.0 1.0 1.0]13231324sage: e, V = spectrum[2]1325sage: v = V.basis()[0]1326sage: diff = (v*m).change_ring(CDF) - e*v1327sage: abs(abs(diff)) < 1e-141328True13291330TESTS::13311332sage: m.eigenspaces_left(algebraic_multiplicity=True)1333Traceback (most recent call last):1334...1335ValueError: algebraic_multiplicity must be set to False for double precision matrices1336"""1337from sage.misc.superseded import deprecation1338msg = ('Eigenspaces of RDF/CDF matrices are deprecated as of ',1339'Sage version 5.0',1340', please use "eigenmatrix_left" instead')1341deprecation(11603, ''.join(msg))1342# For numerical values we leave decisions about1343# multiplicity to the calling routine1344if algebraic_multiplicity:1345raise ValueError("algebraic_multiplicity must be set to False for double precision matrices")1346spectrum = self.left_eigenvectors()1347pairs = []1348for evalue,evectors,_ in spectrum:1349evector = evectors[0]1350espace = evector.parent().span_of_basis([evector],check=False)1351pairs.append((evalue, espace))1352return pairs13531354left_eigenspaces = eigenspaces_left13551356def eigenspaces_right(self, var='a', algebraic_multiplicity=False):1357r"""1358Computes the right eigenspaces of a matrix of double precision1359real or complex numbers (i.e. RDF or CDF).13601361.. warning::13621363This method returns eigenspaces that are all of1364dimension one, since it is impossible to ascertain1365if the numerical results belong to the same eigenspace.1366So this is deprecated in favor of the eigenmatrix routines,1367such as :meth:`sage.matrix.matrix2.Matrix.eigenmatrix_right`.13681369INPUT:13701371- ``var`` - ignored for numerical matrices1372- ``algebraic_multiplicity`` - must be set to ``False``1373for numerical matrices, and will raise an error otherwise.13741375OUTPUT:13761377Return a list of pairs ``(e, V)`` where ``e`` is a (complex)1378eigenvalue and ``V`` is the associated right eigenspace as a1379vector space.13801381No attempt is made to determine if an eigenvalue has multiplicity1382greater than one, so all the eigenspaces returned have dimension one.13831384The SciPy routines used for these computations produce eigenvectors1385normalized to have length 1, but on different hardware they may vary1386by a sign. So for doctests we have normalized output by creating an1387eigenspace with a canonical basis.138813891390EXAMPLES:13911392This first test simply raises the deprecation warning. ::13931394sage: A = identity_matrix(RDF, 2)1395sage: es = A.eigenspaces_right()1396doctest:...: DeprecationWarning: Eigenspaces of RDF/CDF matrices are1397deprecated as of Sage version 5.0,1398please use "eigenmatrix_right" instead1399See http://trac.sagemath.org/11603 for details.14001401::14021403sage: m = matrix(RDF, [[-9, -14, 19, -74],[-1, 2, 4, -11],[-4, -12, 6, -32],[0, -2, -1, 1]])1404sage: m1405[ -9.0 -14.0 19.0 -74.0]1406[ -1.0 2.0 4.0 -11.0]1407[ -4.0 -12.0 6.0 -32.0]1408[ 0.0 -2.0 -1.0 1.0]1409sage: spectrum = m.eigenspaces_right()1410sage: spectrum[0][0]14112.01412sage: (RDF^4).subspace(spectrum[0][1].basis())1413Vector space of degree 4 and dimension 1 over Real Double Field1414Basis matrix:1415[ 1.0 -2.0 3.0 1.0]14161417sage: e, V = spectrum[2]1418sage: v = V.basis()[0]1419sage: diff = (m*v).change_ring(CDF) - e*v1420sage: abs(abs(diff)) < 3e-141421True14221423TESTS::14241425sage: m.eigenspaces_right(algebraic_multiplicity=True)1426Traceback (most recent call last):1427...1428ValueError: algebraic_multiplicity must be set to False for double precision matrices1429"""1430from sage.misc.superseded import deprecation1431msg = ('Eigenspaces of RDF/CDF matrices are deprecated as of ',1432'Sage version 5.0',1433', please use "eigenmatrix_right" instead')1434deprecation(11603, ''.join(msg))1435# For numerical values we leave decisions about1436# multiplicity to the calling routine1437if algebraic_multiplicity:1438raise ValueError("algebraic_multiplicity must be set to False for double precision matrices")1439spectrum = self.right_eigenvectors()1440pairs = []1441for evalue,evectors,_ in spectrum:1442evector = evectors[0]1443espace = evector.parent().span_of_basis([evector],check=False)1444pairs.append((evalue, espace))1445return pairs14461447right_eigenspaces = eigenspaces_right14481449def eigenvalues(self, algorithm='default', tol=None):1450r"""1451Returns a list of eigenvalues.145214531454INPUT:14551456- ``self`` - a square matrix14571458- ``algorithm`` - default: ``'default'``14591460- ``'default'`` - applicable to any matrix1461with double-precision floating point entries.1462Uses the :meth:`~scipy.linalg.eigvals` method from SciPy.14631464- ``'symmetric'`` - converts the matrix into a real matrix1465(i.e. with entries from :class:`~sage.rings.real_double.RDF`),1466then applies the algorithm for Hermitian matrices. This1467algorithm can be significantly faster than the1468``'default'`` algorithm.14691470- ``'hermitian'`` - uses the :meth:`~scipy.linalg.eigh` method1471from SciPy, which applies only to real symmetric or complex1472Hermitian matrices. Since Hermitian is defined as a matrix1473equaling its conjugate-transpose, for a matrix with real1474entries this property is equivalent to being symmetric.1475This algorithm can be significantly faster than the1476``'default'`` algorithm.14771478- ``'tol'`` - default: ``None`` - if set to a value other than1479``None`` this is interpreted as a small real number used to aid in1480grouping eigenvalues that are numerically similar. See the output1481description for more information.14821483.. WARNING::14841485When using the ``'symmetric'`` or ``'hermitian'`` algorithms,1486no check is made on the input matrix, and only the entries below,1487and on, the main diagonal are employed in the computation.14881489Methods such as :meth:`is_symmetric` and :meth:`is_hermitian`1490could be used to verify this beforehand.14911492OUTPUT:14931494Default output for a square matrix of size $n$ is a list of $n$1495eigenvalues from the complex double field,1496:class:`~sage.rings.complex_double.CDF`. If the ``'symmetric'``1497or ``'hermitian'`` algorithms are chosen, the returned eigenvalues1498are from the real double field,1499:class:`~sage.rings.real_double.RDF`.15001501If a tolerance is specified, an attempt is made to group eigenvalues1502that are numerically similar. The return is then a list of pairs,1503where each pair is an eigenvalue followed by its multiplicity.1504The eigenvalue reported is the mean of the eigenvalues computed,1505and these eigenvalues are contained in an interval (or disk) whose1506radius is less than ``5*tol`` for $n < 10,000$ in the worst case.15071508More precisely, for an $n\times n$ matrix, the diameter of the1509interval containing similar eigenvalues could be as large as sum1510of the reciprocals of the first $n$ integers times ``tol``.15111512.. WARNING::15131514Use caution when using the ``tol`` parameter to group1515eigenvalues. See the examples below to see how this can go wrong.15161517EXAMPLES::15181519sage: m = matrix(RDF, 2, 2, [1,2,3,4])1520sage: ev = m.eigenvalues(); ev1521[-0.372281323..., 5.37228132...]1522sage: ev[0].parent()1523Complex Double Field15241525sage: m = matrix(RDF, 2, 2, [0,1,-1,0])1526sage: m.eigenvalues(algorithm='default')1527[1.0*I, -1.0*I]15281529sage: m = matrix(CDF, 2, 2, [I,1,-I,0])1530sage: m.eigenvalues()1531[-0.624810533... + 1.30024259...*I, 0.624810533... - 0.30024259...*I]15321533The adjacency matrix of a graph will be symmetric, and the1534eigenvalues will be real. ::15351536sage: A = graphs.PetersenGraph().adjacency_matrix()1537sage: A = A.change_ring(RDF)1538sage: ev = A.eigenvalues(algorithm='symmetric'); ev1539[-2.0, -2.0, -2.0, -2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0]1540sage: ev[0].parent()1541Real Double Field15421543The matrix ``A`` is "random", but the construction of ``B``1544provides a positive-definite Hermitian matrix. Note that1545the eigenvalues of a Hermitian matrix are real, and the1546eigenvalues of a positive-definite matrix will be positive. ::15471548sage: A = matrix([[ 4*I + 5, 8*I + 1, 7*I + 5, 3*I + 5],1549... [ 7*I - 2, -4*I + 7, -2*I + 4, 8*I + 8],1550... [-2*I + 1, 6*I + 6, 5*I + 5, -I - 4],1551... [ 5*I + 1, 6*I + 2, I - 4, -I + 3]])1552sage: B = (A*A.conjugate_transpose()).change_ring(CDF)1553sage: ev = B.eigenvalues(algorithm='hermitian'); ev1554[2.68144025..., 49.5167998..., 274.086188..., 390.71557...]1555sage: ev[0].parent()1556Real Double Field15571558A tolerance can be given to aid in grouping eigenvalues that1559are similar numerically. However, if the parameter is too small1560it might split too finely. Too large, and it can go wrong very1561badly. Use with care. ::15621563sage: G = graphs.PetersenGraph()1564sage: G.spectrum()1565[3, 1, 1, 1, 1, 1, -2, -2, -2, -2]15661567sage: A = G.adjacency_matrix().change_ring(RDF)1568sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5)1569[(-2.0, 4), (1.0, 5), (3.0, 1)]15701571sage: A.eigenvalues(algorithm='symmetric', tol=2.5)1572[(-2.0, 4), (1.33333333333, 6)]15731574An (extreme) example of properly grouping similar eigenvalues. ::15751576sage: G = graphs.HigmanSimsGraph()1577sage: A = G.adjacency_matrix().change_ring(RDF)1578sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5)1579[(-8.0, 22), (2.0, 77), (22.0, 1)]15801581TESTS:15821583Testing bad input. ::15841585sage: A = matrix(CDF, 2, range(4))1586sage: A.eigenvalues(algorithm='junk')1587Traceback (most recent call last):1588...1589ValueError: algorithm must be 'default', 'symmetric', or 'hermitian', not junk15901591sage: A = matrix(CDF, 2, 3, range(6))1592sage: A.eigenvalues()1593Traceback (most recent call last):1594...1595ValueError: matrix must be square, not 2 x 315961597sage: A = matrix(CDF, 2, [1, 2, 3, 4*I])1598sage: A.eigenvalues(algorithm='symmetric')1599Traceback (most recent call last):1600...1601TypeError: cannot apply symmetric algorithm to matrix with complex entries16021603sage: A = matrix(CDF, 2, 2, range(4))1604sage: A.eigenvalues(tol='junk')1605Traceback (most recent call last):1606...1607TypeError: tolerance parameter must be a real number, not junk16081609sage: A = matrix(CDF, 2, 2, range(4))1610sage: A.eigenvalues(tol=-0.01)1611Traceback (most recent call last):1612...1613ValueError: tolerance parameter must be positive, not -0.0116141615A very small matrix. ::16161617sage: matrix(CDF,0,0).eigenvalues()1618[]1619"""1620import sage.rings.real_double1621import sage.rings.complex_double1622import numpy1623if not algorithm in ['default', 'symmetric', 'hermitian']:1624msg = "algorithm must be 'default', 'symmetric', or 'hermitian', not {0}"1625raise ValueError(msg.format(algorithm))1626if not self.is_square():1627msg = 'matrix must be square, not {0} x {1}'1628raise ValueError(msg.format(self.nrows(), self.ncols()))1629if algorithm == 'symmetric' and self.base_ring() == sage.rings.complex_double.CDF:1630try:1631self = self.change_ring(sage.rings.real_double.RDF) # check side effect1632except TypeError:1633raise TypeError('cannot apply symmetric algorithm to matrix with complex entries')1634if algorithm == 'symmetric':1635algorithm = 'hermitian'1636multiplicity = not tol is None1637if multiplicity:1638try:1639tol = float(tol)1640except (ValueError, TypeError):1641msg = 'tolerance parameter must be a real number, not {0}'1642raise TypeError(msg.format(tol))1643if tol < 0:1644msg = 'tolerance parameter must be positive, not {0}'1645raise ValueError(msg.format(tol))16461647if self._nrows == 0:1648return []1649global scipy1650if scipy is None:1651import scipy1652import scipy.linalg1653if self._nrows == 0:1654return []1655global scipy1656if scipy is None:1657import scipy1658import scipy.linalg1659global numpy1660if numpy is None:1661import numpy1662# generic eigenvalues, or real eigenvalues for Hermitian1663if algorithm == 'default':1664return_class = sage.rings.complex_double.CDF1665evalues = scipy.linalg.eigvals(self._matrix_numpy)1666elif algorithm=='hermitian':1667return_class = sage.rings.real_double.RDF1668evalues = scipy.linalg.eigh(self._matrix_numpy, eigvals_only=True)1669if not multiplicity:1670return [return_class(e) for e in evalues]1671else:1672# pairs in ev_group are1673# slot 0: the sum of "equal" eigenvalues, "s"1674# slot 1: number of eigenvalues in this sum, "m"1675# slot 2: average of these eigenvalues, "avg"1676# we test if "new" eigenvalues are close to the group average1677ev_group = []1678for e in evalues:1679location = None1680best_fit = tol1681for i in range(len(ev_group)):1682s, m, avg = ev_group[i]1683d = numpy.abs(avg - e)1684if d < best_fit:1685best_fit = d1686location = i1687if location is None:1688ev_group.append([e, 1, e])1689else:1690ev_group[location][0] += e1691ev_group[location][1] += 11692ev_group[location][2] = ev_group[location][0]/ev_group[location][1]1693return [(return_class(avg), m) for _, m, avg in ev_group]16941695def left_eigenvectors(self):1696r"""1697Compute the left eigenvectors of a matrix of double precision1698real or complex numbers (i.e. RDF or CDF).16991700OUTPUT:1701Returns a list of triples, each of the form ``(e,[v],1)``,1702where ``e`` is the eigenvalue, and ``v`` is an associated1703left eigenvector. If the matrix is of size `n`, then there are1704`n` triples. Values are computed with the SciPy library.17051706The format of this output is designed to match the format1707for exact results. However, since matrices here have numerical1708entries, the resulting eigenvalues will also be numerical. No1709attempt is made to determine if two eigenvalues are equal, or if1710eigenvalues might actually be zero. So the algebraic multiplicity1711of each eigenvalue is reported as 1. Decisions about equal1712eigenvalues or zero eigenvalues should be addressed in the1713calling routine.17141715The SciPy routines used for these computations produce eigenvectors1716normalized to have length 1, but on different hardware they may vary1717by a sign. So for doctests we have normalized output by forcing their1718eigenvectors to have their first non-zero entry equal to one.17191720EXAMPLES::17211722sage: m = matrix(RDF, [[-5, 3, 2, 8],[10, 2, 4, -2],[-1, -10, -10, -17],[-2, 7, 6, 13]])1723sage: m1724[ -5.0 3.0 2.0 8.0]1725[ 10.0 2.0 4.0 -2.0]1726[ -1.0 -10.0 -10.0 -17.0]1727[ -2.0 7.0 6.0 13.0]1728sage: spectrum = m.left_eigenvectors()1729sage: for i in range(len(spectrum)):1730... spectrum[i][1][0]=matrix(RDF, spectrum[i][1]).echelon_form()[0]1731sage: spectrum[0]1732(2.0, [(1.0, 1.0, 1.0, 1.0)], 1)1733sage: spectrum[1]1734(1.0, [(1.0, 0.8, 0.8, 0.6)], 1)1735sage: spectrum[2]1736(-2.0, [(1.0, 0.4, 0.6, 0.2)], 1)1737sage: spectrum[3]1738(-1.0, [(1.0, 1.0, 2.0, 2.0)], 1)1739"""1740if not self.is_square():1741raise ArithmeticError("self must be a square matrix")1742if self._nrows == 0:1743return [], self.__copy__()1744global scipy1745if scipy is None:1746import scipy1747import scipy.linalg1748v,eig = scipy.linalg.eig(self._matrix_numpy, right=False, left=True)1749# scipy puts eigenvectors in columns, we will extract from rows1750eig = matrix(eig.T)1751return [(sage.rings.complex_double.CDF(v[i]), [eig[i]], 1) for i in range(len(v))]17521753eigenvectors_left = left_eigenvectors17541755def right_eigenvectors(self):1756r"""1757Compute the right eigenvectors of a matrix of double precision1758real or complex numbers (i.e. RDF or CDF).17591760OUTPUT:17611762Returns a list of triples, each of the form ``(e,[v],1)``,1763where ``e`` is the eigenvalue, and ``v`` is an associated1764right eigenvector. If the matrix is of size `n`, then there1765are `n` triples. Values are computed with the SciPy library.17661767The format of this output is designed to match the format1768for exact results. However, since matrices here have numerical1769entries, the resulting eigenvalues will also be numerical. No1770attempt is made to determine if two eigenvalues are equal, or if1771eigenvalues might actually be zero. So the algebraic multiplicity1772of each eigenvalue is reported as 1. Decisions about equal1773eigenvalues or zero eigenvalues should be addressed in the1774calling routine.17751776The SciPy routines used for these computations produce eigenvectors1777normalized to have length 1, but on different hardware they may vary1778by a sign. So for doctests we have normalized output by forcing their1779eigenvectors to have their first non-zero entry equal to one.17801781EXAMPLES::17821783sage: m = matrix(RDF, [[-9, -14, 19, -74],[-1, 2, 4, -11],[-4, -12, 6, -32],[0, -2, -1, 1]])1784sage: m1785[ -9.0 -14.0 19.0 -74.0]1786[ -1.0 2.0 4.0 -11.0]1787[ -4.0 -12.0 6.0 -32.0]1788[ 0.0 -2.0 -1.0 1.0]1789sage: spectrum = m.right_eigenvectors()1790sage: for i in range(len(spectrum)):1791... spectrum[i][1][0]=matrix(RDF, spectrum[i][1]).echelon_form()[0]1792sage: spectrum[0]1793(2.0, [(1.0, -2.0, 3.0, 1.0)], 1)1794sage: spectrum[1]1795(1.0, [(1.0, -0.666666666667, 1.33333333333, 0.333333333333)], 1)1796sage: spectrum[2]1797(-2.0, [(1.0, -0.2, 1.0, 0.2)], 1)1798sage: spectrum[3]1799(-1.0, [(1.0, -0.5, 2.0, 0.5)], 1)1800"""1801if not self.is_square():1802raise ArithmeticError("self must be a square matrix")1803if self._nrows == 0:1804return [], self.__copy__()1805global scipy1806if scipy is None:1807import scipy1808import scipy.linalg1809v,eig = scipy.linalg.eig(self._matrix_numpy, right=True, left=False)1810# scipy puts eigenvectors in columns, we will extract from rows1811eig = matrix(eig.T)1812return [(sage.rings.complex_double.CDF(v[i]), [eig[i]], 1) for i in range(len(v))]18131814eigenvectors_right = right_eigenvectors18151816def solve_left_LU(self, b):1817"""1818Solve the equation `A x = b` using LU decomposition.18191820.. WARNING::18211822This function is broken. See trac 4932.18231824INPUT:18251826- self -- an invertible matrix1827- b -- a vector18281829.. NOTE::18301831This method precomputes and stores the LU decomposition1832before solving. If many equations of the form Ax=b need to be1833solved for a singe matrix A, then this method should be used1834instead of solve. The first time this method is called it will1835compute the LU decomposition. If the matrix has not changed1836then subsequent calls will be very fast as the precomputed LU1837decomposition will be reused.18381839EXAMPLES::18401841sage: A = matrix(RDF, 3,3, [1,2,5,7.6,2.3,1,1,2,-1]); A1842[ 1.0 2.0 5.0]1843[ 7.6 2.3 1.0]1844[ 1.0 2.0 -1.0]1845sage: b = vector(RDF,[1,2,3])1846sage: x = A.solve_left_LU(b); x1847Traceback (most recent call last):1848...1849NotImplementedError: this function is not finished (see trac 4932)185018511852TESTS:18531854We test two degenerate cases::18551856sage: A = matrix(RDF, 0, 3, [])1857sage: A.solve_left_LU(vector(RDF,[]))1858(0.0, 0.0, 0.0)1859sage: A = matrix(RDF, 3, 0, [])1860sage: A.solve_left_LU(vector(RDF,3, [1,2,3]))1861()18621863"""1864if self._nrows != b.degree():1865raise ValueError("number of rows of self must equal degree of b")1866if self._nrows == 0 or self._ncols == 0:1867return self._row_ambient_module().zero_vector()18681869raise NotImplementedError("this function is not finished (see trac 4932)")1870self._c_compute_LU() # so self._L_M and self._U_M are defined below.1871cdef Matrix_double_dense M = self._new()1872lu = self._L_M*self._U_M1873global scipy1874if scipy is None:1875import scipy1876import scipy.linalg1877M._matrix_numpy = scipy.linalg.lu_solve((lu, self._P_M), b)1878return M18791880def solve_right(self, b):1881r"""1882Solve the vector equation ``A*x = b`` for a nonsingular ``A``.18831884INPUT:18851886- ``self`` - a square matrix that is nonsigular (of full rank).1887- ``b`` - a vector of the correct size. Elements of the vector1888must coerce into the base ring of the coefficient matrix. In1889particular, if ``b`` has entries from ``CDF`` then ``self``1890must have ``CDF`` as its base ring.18911892OUTPUT:18931894The unique solution ``x`` to the matrix equation ``A*x = b``,1895as a vector over the same base ring as ``self``.18961897ALGORITHM:18981899Uses the ``solve()`` routine from the SciPy ``scipy.linalg`` module.19001901EXAMPLES:19021903Over the reals. ::19041905sage: A = matrix(RDF, 3,3, [1,2,5,7.6,2.3,1,1,2,-1]); A1906[ 1.0 2.0 5.0]1907[ 7.6 2.3 1.0]1908[ 1.0 2.0 -1.0]1909sage: b = vector(RDF,[1,2,3])1910sage: x = A.solve_right(b); x1911(-0.113695090439, 1.39018087855, -0.333333333333)1912sage: x.parent()1913Vector space of dimension 3 over Real Double Field1914sage: A*x1915(1.0, 2.0, 3.0)19161917Over the complex numbers. ::19181919sage: A = matrix(CDF, [[ 0, -1 + 2*I, 1 - 3*I, I],1920... [2 + 4*I, -2 + 3*I, -1 + 2*I, -1 - I],1921... [ 2 + I, 1 - I, -1, 5],1922... [ 3*I, -1 - I, -1 + I, -3 + I]])1923sage: b = vector(CDF, [2 -3*I, 3, -2 + 3*I, 8])1924sage: x = A.solve_right(b); x1925(1.96841637... - 1.07606761...*I, -0.614323843... + 1.68416370...*I, 0.0733985765... + 1.73487544...*I, -1.6018683... + 0.524021352...*I)1926sage: x.parent()1927Vector space of dimension 4 over Complex Double Field1928sage: abs(A*x - b) < 1e-141929True19301931The vector of constants, ``b``, can be given in a1932variety of forms, so long as it coerces to a vector1933over the same base ring as the coefficient matrix. ::19341935sage: A=matrix(CDF, 5, [1/(i+j+1) for i in range(5) for j in range(5)])1936sage: A.solve_right([1]*5) # rel tol 1e-111937(5.0, -120.0, 630.0, -1120.0, 630.0)19381939TESTS:19401941A degenerate case. ::19421943sage: A = matrix(RDF, 0, 0, [])1944sage: A.solve_right(vector(RDF,[]))1945()19461947The coefficent matrix must be square. ::19481949sage: A = matrix(RDF, 2, 3, range(6))1950sage: b = vector(RDF, [1,2,3])1951sage: A.solve_right(b)1952Traceback (most recent call last):1953...1954ValueError: coefficient matrix of a system over RDF/CDF must be square, not 2 x 319551956The coefficient matrix must be nonsingular. ::19571958sage: A = matrix(RDF, 5, range(25))1959sage: b = vector(RDF, [1,2,3,4,5])1960sage: A.solve_right(b)1961Traceback (most recent call last):1962...1963LinAlgError: singular matrix19641965The vector of constants needs the correct degree. ::19661967sage: A = matrix(RDF, 5, range(25))1968sage: b = vector(RDF, [1,2,3,4])1969sage: A.solve_right(b)1970Traceback (most recent call last):1971...1972TypeError: vector of constants over Real Double Field incompatible with matrix over Real Double Field19731974The vector of constants needs to be compatible with1975the base ring of the coefficient matrix. ::19761977sage: F.<a> = FiniteField(27)1978sage: b = vector(F, [a,a,a,a,a])1979sage: A.solve_right(b)1980Traceback (most recent call last):1981...1982TypeError: vector of constants over Finite Field in a of size 3^3 incompatible with matrix over Real Double Field19831984With a coefficient matrix over ``RDF``, a vector of constants1985over ``CDF`` can be accomodated by converting the base ring1986of the coefficient matrix. ::19871988sage: A = matrix(RDF, 2, range(4))1989sage: b = vector(CDF, [1+I,2])1990sage: A.solve_right(b)1991Traceback (most recent call last):1992...1993TypeError: vector of constants over Complex Double Field incompatible with matrix over Real Double Field19941995sage: B = A.change_ring(CDF)1996sage: B.solve_right(b)1997(-0.5 - 1.5*I, 1.0 + 1.0*I)1998"""1999if not self.is_square():2000raise ValueError("coefficient matrix of a system over RDF/CDF must be square, not %s x %s " % (self.nrows(), self.ncols()))2001M = self._column_ambient_module()2002try:2003vec = M(b)2004except TypeError:2005raise TypeError("vector of constants over %s incompatible with matrix over %s" % (b.base_ring(), self.base_ring()))2006if vec.degree() != self.ncols():2007raise ValueError("vector of constants in linear system over RDF/CDF must have degree equal to the number of columns for the coefficient matrix, not %s" % vec.degree() )20082009if self._ncols == 0:2010return M.zero_vector()20112012global scipy2013if scipy is None:2014import scipy2015import scipy.linalg2016# may raise a LinAlgError for a singular matrix2017return M(scipy.linalg.solve(self._matrix_numpy, vec.numpy()))20182019def solve_left(self, b):2020r"""2021Solve the vector equation ``x*A = b`` for a nonsingular ``A``.20222023INPUT:20242025- ``self`` - a square matrix that is nonsigular (of full rank).2026- ``b`` - a vector of the correct size. Elements of the vector2027must coerce into the base ring of the coefficient matrix. In2028particular, if ``b`` has entries from ``CDF`` then ``self``2029must have ``CDF`` as its base ring.20302031OUTPUT:20322033The unique solution ``x`` to the matrix equation ``x*A = b``,2034as a vector over the same base ring as ``self``.20352036ALGORITHM:20372038Uses the ``solve()`` routine from the SciPy ``scipy.linalg`` module,2039after taking the tranpose of the coefficient matrix.20402041EXAMPLES:20422043Over the reals. ::20442045sage: A = matrix(RDF, 3,3, [1,2,5,7.6,2.3,1,1,2,-1]); A2046[ 1.0 2.0 5.0]2047[ 7.6 2.3 1.0]2048[ 1.0 2.0 -1.0]2049sage: b = vector(RDF,[1,2,3])2050sage: x = A.solve_left(b); x.zero_at(1e-18) # fix noisy zeroes2051(0.666666666..., 0.0, 0.333333333...)2052sage: x.parent()2053Vector space of dimension 3 over Real Double Field2054sage: x*A2055(1.0, 2.0, 3.0)20562057Over the complex numbers. ::20582059sage: A = matrix(CDF, [[ 0, -1 + 2*I, 1 - 3*I, I],2060... [2 + 4*I, -2 + 3*I, -1 + 2*I, -1 - I],2061... [ 2 + I, 1 - I, -1, 5],2062... [ 3*I, -1 - I, -1 + I, -3 + I]])2063sage: b = vector(CDF, [2 -3*I, 3, -2 + 3*I, 8])2064sage: x = A.solve_left(b); x2065(-1.55765124... - 0.644483985...*I, 0.183274021... + 0.286476868...*I, 0.270818505... + 0.246619217...*I, -1.69003558... - 0.828113879...*I)2066sage: x.parent()2067Vector space of dimension 4 over Complex Double Field2068sage: abs(x*A - b) < 1e-142069True20702071The vector of constants, ``b``, can be given in a2072variety of forms, so long as it coerces to a vector2073over the same base ring as the coefficient matrix. ::20742075sage: A=matrix(CDF, 5, [1/(i+j+1) for i in range(5) for j in range(5)])2076sage: A.solve_left([1]*5) # rel tol 1e-112077(5.0, -120.0, 630.0, -1120.0, 630.0)20782079TESTS:20802081A degenerate case. ::20822083sage: A = matrix(RDF, 0, 0, [])2084sage: A.solve_left(vector(RDF,[]))2085()20862087The coefficent matrix must be square. ::20882089sage: A = matrix(RDF, 2, 3, range(6))2090sage: b = vector(RDF, [1,2,3])2091sage: A.solve_left(b)2092Traceback (most recent call last):2093...2094ValueError: coefficient matrix of a system over RDF/CDF must be square, not 2 x 320952096The coefficient matrix must be nonsingular. ::20972098sage: A = matrix(RDF, 5, range(25))2099sage: b = vector(RDF, [1,2,3,4,5])2100sage: A.solve_left(b)2101Traceback (most recent call last):2102...2103LinAlgError: singular matrix21042105The vector of constants needs the correct degree. ::21062107sage: A = matrix(RDF, 5, range(25))2108sage: b = vector(RDF, [1,2,3,4])2109sage: A.solve_left(b)2110Traceback (most recent call last):2111...2112TypeError: vector of constants over Real Double Field incompatible with matrix over Real Double Field21132114The vector of constants needs to be compatible with2115the base ring of the coefficient matrix. ::21162117sage: F.<a> = FiniteField(27)2118sage: b = vector(F, [a,a,a,a,a])2119sage: A.solve_left(b)2120Traceback (most recent call last):2121...2122TypeError: vector of constants over Finite Field in a of size 3^3 incompatible with matrix over Real Double Field21232124With a coefficient matrix over ``RDF``, a vector of constants2125over ``CDF`` can be accomodated by converting the base ring2126of the coefficient matrix. ::21272128sage: A = matrix(RDF, 2, range(4))2129sage: b = vector(CDF, [1+I,2])2130sage: A.solve_left(b)2131Traceback (most recent call last):2132...2133TypeError: vector of constants over Complex Double Field incompatible with matrix over Real Double Field21342135sage: B = A.change_ring(CDF)2136sage: B.solve_left(b)2137(0.5 - 1.5*I, 0.5 + 0.5*I)2138"""2139if not self.is_square():2140raise ValueError("coefficient matrix of a system over RDF/CDF must be square, not %s x %s " % (self.nrows(), self.ncols()))2141M = self._row_ambient_module()2142try:2143vec = M(b)2144except TypeError:2145raise TypeError("vector of constants over %s incompatible with matrix over %s" % (b.base_ring(), self.base_ring()))2146if vec.degree() != self.nrows():2147raise ValueError("vector of constants in linear system over RDF/CDF must have degree equal to the number of rows for the coefficient matrix, not %s" % vec.degree() )21482149if self._nrows == 0:2150return M.zero_vector()21512152global scipy2153if scipy is None:2154import scipy2155import scipy.linalg2156# may raise a LinAlgError for a singular matrix2157# call "right solve" routine with the transpose2158return M(scipy.linalg.solve(self._matrix_numpy.T, vec.numpy()))21592160def determinant(self):2161"""2162Return the determinant of self.21632164ALGORITHM:21652166Use numpy21672168EXAMPLES::21692170sage: m = matrix(RDF,2,range(4)); m.det()2171-2.02172sage: m = matrix(RDF,0,[]); m.det()21731.02174sage: m = matrix(RDF, 2, range(6)); m.det()2175Traceback (most recent call last):2176...2177ValueError: self must be a square matrix2178"""2179if not self.is_square():2180raise ValueError("self must be a square matrix")2181if self._nrows == 0 or self._ncols == 0:2182return self._sage_dtype(1)2183global scipy2184if scipy is None:2185import scipy2186import scipy.linalg21872188return self._sage_dtype(scipy.linalg.det(self._matrix_numpy))218921902191def log_determinant(self):2192"""2193Compute the log of the absolute value of the determinant2194using LU decomposition.21952196.. NOTE::21972198This is useful if the usual determinant overflows.21992200EXAMPLES::22012202sage: m = matrix(RDF,2,2,range(4)); m2203[0.0 1.0]2204[2.0 3.0]2205sage: RDF(log(abs(m.determinant())))22060.693147180562207sage: m.log_determinant()22080.693147180562209sage: m = matrix(RDF,0,0,[]); m2210[]2211sage: m.log_determinant()22120.02213sage: m = matrix(CDF,2,2,range(4)); m2214[0.0 1.0]2215[2.0 3.0]2216sage: RDF(log(abs(m.determinant())))22170.693147180562218sage: m.log_determinant()22190.693147180562220sage: m = matrix(CDF,0,0,[]); m2221[]2222sage: m.log_determinant()22230.022242225"""2226global numpy2227cdef Matrix_double_dense U22282229if self._nrows == 0 or self._ncols == 0:2230return sage.rings.real_double.RDF(0)22312232if not self.is_square():2233raise ArithmeticError("self must be a square matrix")22342235P, L, U = self.LU()2236if numpy is None:2237import numpy22382239return sage.rings.real_double.RDF(sum(numpy.log(abs(numpy.diag(U._matrix_numpy)))))22402241def transpose(self):2242"""2243Return the transpose of this matrix, without changing self.22442245EXAMPLES::22462247sage: m = matrix(RDF,2,3,range(6)); m2248[0.0 1.0 2.0]2249[3.0 4.0 5.0]2250sage: m2 = m.transpose()2251sage: m[0,0] = 22252sage: m2 #note that m2 hasn't changed2253[0.0 3.0]2254[1.0 4.0]2255[2.0 5.0]22562257``.T`` is a convenient shortcut for the transpose::22582259sage: m.T2260[2.0 3.0]2261[1.0 4.0]2262[2.0 5.0]22632264sage: m = matrix(RDF,0,3); m2265[]2266sage: m.transpose()2267[]2268sage: m.transpose().parent()2269Full MatrixSpace of 3 by 0 dense matrices over Real Double Field22702271"""2272if self._nrows == 0 or self._ncols == 0:2273return self.new_matrix(self._ncols, self._nrows)22742275cdef Matrix_double_dense trans2276trans = self._new(self._ncols, self._nrows)2277trans._matrix_numpy = self._matrix_numpy.transpose().copy()2278if self._subdivisions is not None:2279row_divs, col_divs = self.subdivisions()2280trans.subdivide(col_divs, row_divs)2281return trans22822283def SVD(self, *args, **kwds):2284r"""2285Return the singular value decomposition of this matrix.22862287The U and V matrices are not unique and may be returned with different2288values in the future or on different systems. The S matrix is unique2289and contains the singular values in descending order.22902291The computed decomposition is cached and returned on subsequent calls.22922293INPUT:22942295- A -- a matrix22962297OUTPUT:22982299- U, S, V -- immutable matrices such that $A = U*S*V.conj().transpose()$2300where U and V are orthogonal and S is zero off of the diagonal.23012302Note that if self is m-by-n, then the dimensions of the2303matrices that this returns are (m,m), (m,n), and (n, n).23042305.. NOTE::23062307If all you need is the singular values of the matrix, see2308the more convenient :meth:`singular_values`.23092310EXAMPLES::23112312sage: m = matrix(RDF,4,range(1,17))2313sage: U,S,V = m.SVD()2314sage: U*S*V.transpose()2315[ 1.0 2.0 3.0 4.0]2316[ 5.0 6.0 7.0 8.0]2317[ 9.0 10.0 11.0 12.0]2318[13.0 14.0 15.0 16.0]23192320A non-square example::23212322sage: m = matrix(RDF, 2, range(1,7)); m2323[1.0 2.0 3.0]2324[4.0 5.0 6.0]2325sage: U, S, V = m.SVD()2326sage: U*S*V.transpose()2327[1.0 2.0 3.0]2328[4.0 5.0 6.0]23292330S contains the singular values::23312332sage: S.round(4)2333[ 9.508 0.0 0.0]2334[ 0.0 0.7729 0.0]2335sage: [round(sqrt(abs(x)),4) for x in (S*S.transpose()).eigenvalues()]2336[9.508, 0.7729]23372338U and V are orthogonal matrices::23392340sage: U # random, SVD is not unique2341[-0.386317703119 -0.922365780077]2342[-0.922365780077 0.386317703119]2343[-0.274721127897 -0.961523947641]2344[-0.961523947641 0.274721127897]2345sage: (U*U.transpose()).zero_at(1e-15)2346[1.0 0.0]2347[0.0 1.0]2348sage: V # random, SVD is not unique2349[-0.428667133549 0.805963908589 0.408248290464]2350[-0.566306918848 0.112382414097 -0.816496580928]2351[-0.703946704147 -0.581199080396 0.408248290464]2352sage: (V*V.transpose()).zero_at(1e-15)2353[1.0 0.0 0.0]2354[0.0 1.0 0.0]2355[0.0 0.0 1.0]23562357TESTS::23582359sage: m = matrix(RDF,3,2,range(1, 7)); m2360[1.0 2.0]2361[3.0 4.0]2362[5.0 6.0]2363sage: U,S,V = m.SVD()2364sage: U*S*V.transpose()2365[1.0 2.0]2366[3.0 4.0]2367[5.0 6.0]23682369sage: m = matrix(RDF, 3, 0, []); m2370[]2371sage: m.SVD()2372([], [], [])2373sage: m = matrix(RDF, 0, 3, []); m2374[]2375sage: m.SVD()2376([], [], [])2377sage: def shape(x): return (x.nrows(), x.ncols())2378sage: m = matrix(RDF, 2, 3, range(6))2379sage: map(shape, m.SVD())2380[(2, 2), (2, 3), (3, 3)]2381sage: for x in m.SVD(): x.is_immutable()2382True2383True2384True2385"""2386global scipy, numpy2387cdef Py_ssize_t i2388cdef Matrix_double_dense U, S, V23892390if len(args)>0 or len(kwds)>0:2391from sage.misc.superseded import deprecation2392deprecation(7852, "Arguments passed to SVD, but SVD no longer supports different methods (it only uses numpy now).")23932394if self._nrows == 0 or self._ncols == 0:2395U_t = self.new_matrix(self._nrows, self._ncols)2396S_t = self.new_matrix(self._nrows, self._ncols)2397V_t = self.new_matrix(self._ncols, self._nrows)2398return U_t, S_t, V_t23992400USV = self.fetch('SVD_factors')2401if USV is None:2402# TODO: More efficient representation of non-square diagonal matrix S2403if scipy is None:2404import scipy2405import scipy.linalg2406if numpy is None:2407import numpy2408U_mat, S_diagonal, V_mat = scipy.linalg.svd(self._matrix_numpy)24092410U = self._new(self._nrows, self._nrows)2411S = self._new(self._nrows, self._ncols)2412V = self._new(self._ncols, self._ncols)24132414S_mat = numpy.zeros((self._nrows, self._ncols), dtype=self._numpy_dtype)2415for i in range(S_diagonal.shape[0]):2416S_mat[i,i] = S_diagonal[i]24172418U._matrix_numpy = numpy.ascontiguousarray(U_mat)2419S._matrix_numpy = S_mat2420V._matrix_numpy = numpy.ascontiguousarray(V_mat.conj().T)2421USV = U, S, V2422for M in USV: M.set_immutable()2423self.cache('SVD_factors', USV)24242425return USV24262427def QR(self):2428r"""2429Returns a factorization into a unitary matrix and an2430upper-triangular matrix.24312432INPUT:24332434Any matrix over ``RDF`` or ``CDF``.24352436OUTPUT:24372438``Q``, ``R`` -- a pair of matrices such that if `A`2439is the original matrix, then24402441.. math::24422443A = QR, \quad Q^\ast Q = I24442445where `R` is upper-triangular. `Q^\ast` is the2446conjugate-transpose in the complex case, and just2447the transpose in the real case. So `Q` is a unitary2448matrix (or rather, orthogonal, in the real case),2449or equivalently `Q` has orthogonal columns. For a2450matrix of full rank this factorization is unique2451up to adjustments via multiples of rows and columns2452by multiples with scalars having modulus `1`. So2453in the full-rank case, `R` is unique if the diagonal2454entries are required to be positive real numbers.24552456The resulting decomposition is cached.24572458ALGORITHM:24592460Calls "linalg.qr" from SciPy, which is in turn an2461interface to LAPACK routines.24622463EXAMPLES:24642465Over the reals, the inverse of ``Q`` is its transpose,2466since including a conjugate has no effect. In the real2467case, we say ``Q`` is orthogonal. ::24682469sage: A = matrix(RDF, [[-2, 0, -4, -1, -1],2470... [-2, 1, -6, -3, -1],2471... [1, 1, 7, 4, 5],2472... [3, 0, 8, 3, 3],2473... [-1, 1, -6, -6, 5]])2474sage: Q, R = A.QR()24752476At this point, ``Q`` is only well-defined up to the2477signs of its columns, and similarly for ``R`` and its2478rows, so we normalize them::24792480sage: Qnorm = Q._normalize_columns()2481sage: Rnorm = R._normalize_rows()2482sage: Qnorm.round(6).zero_at(10^-6)2483[ 0.458831 0.126051 0.381212 0.394574 0.68744]2484[ 0.458831 -0.47269 -0.051983 -0.717294 0.220963]2485[-0.229416 -0.661766 0.661923 0.180872 -0.196411]2486[-0.688247 -0.189076 -0.204468 -0.09663 0.662889]2487[ 0.229416 -0.535715 -0.609939 0.536422 -0.024551]2488sage: Rnorm.round(6).zero_at(10^-6)2489[ 4.358899 -0.458831 13.076697 6.194225 2.982405]2490[ 0.0 1.670172 0.598741 -1.29202 6.207997]2491[ 0.0 0.0 5.444402 5.468661 -0.682716]2492[ 0.0 0.0 0.0 1.027626 -3.6193]2493[ 0.0 0.0 0.0 0.0 0.024551]2494sage: (Q*Q.transpose()).zero_at(10^-14)2495[1.0 0.0 0.0 0.0 0.0]2496[0.0 1.0 0.0 0.0 0.0]2497[0.0 0.0 1.0 0.0 0.0]2498[0.0 0.0 0.0 1.0 0.0]2499[0.0 0.0 0.0 0.0 1.0]2500sage: (Q*R - A).zero_at(10^-14)2501[0.0 0.0 0.0 0.0 0.0]2502[0.0 0.0 0.0 0.0 0.0]2503[0.0 0.0 0.0 0.0 0.0]2504[0.0 0.0 0.0 0.0 0.0]2505[0.0 0.0 0.0 0.0 0.0]25062507Now over the complex numbers, demonstrating that the SciPy libraries2508are (properly) using the Hermitian inner product, so that ``Q`` is2509a unitary matrix (its inverse is the conjugate-transpose). ::25102511sage: A = matrix(CDF, [[-8, 4*I + 1, -I + 2, 2*I + 1],2512... [1, -2*I - 1, -I + 3, -I + 1],2513... [I + 7, 2*I + 1, -2*I + 7, -I + 1],2514... [I + 2, 0, I + 12, -1]])2515sage: Q, R = A.QR()2516sage: Q._normalize_columns().round(6).zero_at(10^-6)2517[ 0.730297 0.207057 + 0.538347*I 0.246305 - 0.076446*I 0.238162 - 0.10366*I]2518[ -0.091287 -0.207057 - 0.377878*I 0.378656 - 0.195222*I 0.701244 - 0.364371*I]2519[ -0.63901 - 0.091287*I 0.170822 + 0.667758*I -0.034115 + 0.040902*I 0.314017 - 0.082519*I]2520[-0.182574 - 0.091287*I -0.036235 + 0.07247*I 0.863228 + 0.063228*I -0.449969 - 0.011612*I]2521sage: R._normalize_rows().round(6).zero_at(10^-6)2522[ 10.954451 -1.917029*I 5.385938 - 2.19089*I -0.273861 - 2.19089*I]2523[ 0.0 4.829596 -0.869638 - 5.864879*I 0.993872 - 0.305409*I]2524[ 0.0 0.0 12.001608 -0.270953 + 0.442063*I]2525[ 0.0 0.0 0.0 1.942964]2526sage: (Q.conjugate().transpose()*Q).zero_at(10^-15)2527[1.0 0.0 0.0 0.0]2528[0.0 1.0 0.0 0.0]2529[0.0 0.0 1.0 0.0]2530[0.0 0.0 0.0 1.0]2531sage: (Q*R - A).zero_at(10^-14)2532[0.0 0.0 0.0 0.0]2533[0.0 0.0 0.0 0.0]2534[0.0 0.0 0.0 0.0]2535[0.0 0.0 0.0 0.0]25362537An example of a rectangular matrix that is also rank-deficient.2538If you run this example yourself, you may see a very small, nonzero2539entries in the third row, in the third column, even though the exact2540version of the matrix has rank 2. The final two columns of ``Q``2541span the left kernel of ``A`` (as evidenced by the two zero rows of2542``R``). Different platforms will compute different bases for this2543left kernel, so we do not exhibit the actual matrix. ::25442545sage: Arat = matrix(QQ, [[2, -3, 3],2546... [-1, 1, -1],2547... [-1, 3, -3],2548... [-5, 1, -1]])2549sage: Arat.rank()255022551sage: A = Arat.change_ring(CDF)2552sage: Q, R = A.QR()2553sage: R._normalize_rows().round(6).zero_at(10^-6)2554[ 5.567764 -2.69408 2.69408]2555[ 0.0 3.569585 -3.569585]2556[ 0.0 0.0 0.0]2557[ 0.0 0.0 0.0]2558sage: (Q.conjugate_transpose()*Q).zero_at(10^-14)2559[1.0 0.0 0.0 0.0]2560[0.0 1.0 0.0 0.0]2561[0.0 0.0 1.0 0.0]2562[0.0 0.0 0.0 1.0]25632564Results are cached, meaning they are immutable matrices.2565Make a copy if you need to manipulate a result. ::25662567sage: A = random_matrix(CDF, 2, 2)2568sage: Q, R = A.QR()2569sage: Q.is_mutable()2570False2571sage: R.is_mutable()2572False2573sage: Q[0,0] = 02574Traceback (most recent call last):2575...2576ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).2577sage: Qcopy = copy(Q)2578sage: Qcopy[0,0] = 6792579sage: Qcopy[0,0]2580679.025812582TESTS:25832584Trivial cases return trivial results of the correct size,2585and we check ``Q`` itself in one case, verifying a fix for2586:trac:`10795`. ::25872588sage: A = zero_matrix(RDF, 0, 10)2589sage: Q, R = A.QR()2590sage: Q.nrows(), Q.ncols()2591(0, 0)2592sage: R.nrows(), R.ncols()2593(0, 10)2594sage: A = zero_matrix(RDF, 3, 0)2595sage: Q, R = A.QR()2596sage: Q.nrows(), Q.ncols()2597(3, 3)2598sage: R.nrows(), R.ncols()2599(3, 0)2600sage: Q2601[1.0 0.0 0.0]2602[0.0 1.0 0.0]2603[0.0 0.0 1.0]2604"""2605global scipy2606cdef Matrix_double_dense Q,R26072608if self._nrows == 0 or self._ncols == 0:2609return self.new_matrix(self._nrows, self._nrows, entries=1), self.new_matrix()26102611QR = self.fetch('QR_factors')2612if QR is None:2613Q = self._new(self._nrows, self._nrows)2614R = self._new(self._nrows, self._ncols)2615if scipy is None:2616import scipy2617import scipy.linalg2618Q._matrix_numpy, R._matrix_numpy = scipy.linalg.qr(self._matrix_numpy)2619Q.set_immutable()2620R.set_immutable()2621QR = (Q, R)2622self.cache('QR_factors', QR)2623return QR26242625def is_symmetric(self, tol = 1e-12):2626"""2627Return whether this matrix is symmetric, to the given tolerance.26282629EXAMPLES::26302631sage: m = matrix(RDF,2,2,range(4)); m2632[0.0 1.0]2633[2.0 3.0]2634sage: m.is_symmetric()2635False2636sage: m[1,0] = 1.1; m2637[0.0 1.0]2638[1.1 3.0]2639sage: m.is_symmetric()2640False26412642The tolerance inequality is strict:2643sage: m.is_symmetric(tol=0.1)2644False2645sage: m.is_symmetric(tol=0.11)2646True2647"""2648cdef Py_ssize_t i, j2649tol = float(tol)2650key = 'symmetric_%s'%tol2651b = self.fetch(key)2652if not b is None:2653return b2654if self._nrows != self._ncols:2655self.cache(key, False)2656return False2657b = True2658for i from 0 < i < self._nrows:2659for j from 0 <= j < i:2660if math.fabs(self.get_unsafe(i,j) - self.get_unsafe(j,i)) > tol:2661b = False2662break2663self.cache(key, b)2664return b26652666def is_unitary(self, tol=1e-12, algorithm='orthonormal'):2667r"""2668Returns ``True`` if the columns of the matrix are an orthonormal basis.26692670For a matrix with real entries this determines if a matrix is2671"orthogonal" and for a matrix with complex entries this determines2672if the matrix is "unitary."26732674INPUT:26752676- ``tol`` - default: ``1e-12`` - the largest value of the2677absolute value of the difference between two matrix entries2678for which they will still be considered equal.26792680- ``algorithm`` - default: 'orthonormal' - set to 'orthonormal'2681for a stable procedure and set to 'naive' for a fast2682procedure.26832684OUTPUT:26852686``True`` if the matrix is square and its conjugate-transpose is2687its inverse, and ``False`` otherwise. In other words, a matrix2688is orthogonal or unitary if the product of its conjugate-transpose2689times the matrix is the identity matrix.26902691The tolerance parameter is used to allow for numerical values2692to be equal if there is a slight difference due to round-off2693and other imprecisions.26942695The result is cached, on a per-tolerance and per-algorithm basis.26962697ALGORITHMS:26982699The naive algorithm simply computes the product of the2700conjugate-transpose with the matrix and compares the entries2701to the identity matrix, with equality controlled by the2702tolerance parameter.27032704The orthonormal algorithm first computes a Schur decomposition2705(via the :meth:`schur` method) and checks that the result is a2706diagonal matrix with entries of modulus 1, which is equivalent to2707being unitary.27082709So the naive algorithm might finish fairly quickly for a matrix2710that is not unitary, once the product has been computed.2711However, the orthonormal algorithm will compute a Schur2712decomposition before going through a similar check of a2713matrix entry-by-entry.27142715EXAMPLES:27162717A matrix that is far from unitary. ::27182719sage: A = matrix(RDF, 4, range(16))2720sage: A.conjugate().transpose()*A2721[224.0 248.0 272.0 296.0]2722[248.0 276.0 304.0 332.0]2723[272.0 304.0 336.0 368.0]2724[296.0 332.0 368.0 404.0]2725sage: A.is_unitary()2726False2727sage: A.is_unitary(algorithm='naive')2728False2729sage: A.is_unitary(algorithm='orthonormal')2730False27312732The QR decoposition will produce a unitary matrix as Q and the2733SVD decomposition will create two unitary matrices, U and V. ::27342735sage: A = matrix(CDF, [[ 1 - I, -3*I, -2 + I, 1, -2 + 3*I],2736... [ 1 - I, -2 + I, 1 + 4*I, 0, 2 + I],2737... [ -1, -5 + I, -2 + I, 1 + I, -5 - 4*I],2738... [-2 + 4*I, 2 - I, 8 - 4*I, 1 - 8*I, 3 - 2*I]])2739sage: Q, R = A.QR()2740sage: Q.is_unitary()2741True2742sage: U, S, V = A.SVD()2743sage: U.is_unitary(algorithm='naive')2744True2745sage: U.is_unitary(algorithm='orthonormal')2746True2747sage: V.is_unitary(algorithm='naive') # not tested - known bug (trac #11248)2748True27492750If we make the tolerance too strict we can get misleading results. ::27512752sage: A = matrix(RDF, 10, 10, [1/(i+j+1) for i in range(10) for j in range(10)])2753sage: Q, R = A.QR()2754sage: Q.is_unitary(algorithm='naive', tol=1e-16)2755False2756sage: Q.is_unitary(algorithm='orthonormal', tol=1e-17)2757False27582759Rectangular matrices are not unitary/orthogonal, even if their2760columns form an orthonormal set. ::27612762sage: A = matrix(CDF, [[1,0], [0,0], [0,1]])2763sage: A.is_unitary()2764False27652766The smallest cases. The Schur decomposition used by the2767orthonormal algorithm will fail on a matrix of size zero. ::27682769sage: P = matrix(CDF, 0, 0)2770sage: P.is_unitary(algorithm='naive')2771True27722773sage: P = matrix(CDF, 1, 1, [1])2774sage: P.is_unitary(algorithm='orthonormal')2775True27762777sage: P = matrix(CDF, 0, 0,)2778sage: P.is_unitary(algorithm='orthonormal')2779Traceback (most recent call last):2780...2781ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (0,)27822783TESTS::27842785sage: P = matrix(CDF, 2, 2)2786sage: P.is_unitary(tol='junk')2787Traceback (most recent call last):2788...2789TypeError: tolerance must be a real number, not junk27902791sage: P.is_unitary(tol=-0.3)2792Traceback (most recent call last):2793...2794ValueError: tolerance must be positive, not -0.327952796sage: P.is_unitary(algorithm='junk')2797Traceback (most recent call last):2798...2799ValueError: algorithm must be 'naive' or 'orthonormal', not junk280028012802AUTHOR:28032804- Rob Beezer (2011-05-04)2805"""2806global numpy2807try:2808tol = float(tol)2809except Exception:2810raise TypeError('tolerance must be a real number, not {0}'.format(tol))2811if tol <= 0:2812raise ValueError('tolerance must be positive, not {0}'.format(tol))2813if not algorithm in ['naive', 'orthonormal']:2814raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm))2815key = 'unitary_{0}_{1}'.format(algorithm, tol)2816b = self.fetch(key)2817if not b is None:2818return b2819if not self.is_square():2820self.cache(key, False)2821return False2822if numpy is None:2823import numpy2824cdef Py_ssize_t i, j2825cdef Matrix_double_dense T, P2826if algorithm == 'orthonormal':2827# Schur decomposition over CDF will be unitary2828# iff diagonal with unit modulus entries2829_, T = self.schur(base_ring=sage.rings.complex_double.CDF)2830unitary = T._is_lower_triangular(tol)2831if unitary:2832for 0 <= i < self._nrows:2833if numpy.absolute(numpy.absolute(T.get_unsafe(i,i)) - 1) > tol:2834unitary = False2835break2836elif algorithm == 'naive':2837P = self.conjugate().transpose()*self2838unitary = True2839for i from 0 <= i < self._nrows:2840# off-diagonal, since P is Hermitian2841for j from 0 <= j < i:2842if numpy.absolute(P.get_unsafe(i,j)) > tol:2843unitary = False2844break2845# at diagonal2846if numpy.absolute(P.get_unsafe(i,i)-1) > tol:2847unitary = False2848if not unitary:2849break2850self.cache(key, unitary)2851return unitary28522853def _is_lower_triangular(self, tol):2854r"""2855Returns ``True`` if the entries above the diagonal are all zero.28562857INPUT:28582859- ``tol`` - the largest value of the absolute value of the2860difference between two matrix entries for which they will2861still be considered equal.28622863OUTPUT:28642865Returns ``True`` if each entry above the diagonal (entries2866with a row index less than the column index) is zero.28672868EXAMPLES::28692870sage: A = matrix(RDF, [[ 2.0, 0.0, 0.0],2871... [ 1.0, 3.0, 0.0],2872... [-4.0, 2.0, -1.0]])2873sage: A._is_lower_triangular(1.0e-17)2874True2875sage: A[1,2] = 10^-132876sage: A._is_lower_triangular(1.0e-14)2877False2878"""2879global numpy2880if numpy is None:2881import numpy2882cdef Py_ssize_t i, j2883for i in range(self._nrows):2884for j in range(i+1, self._ncols):2885if numpy.absolute(self.get_unsafe(i,j)) > tol:2886return False2887return True28882889def is_hermitian(self, tol = 1e-12, algorithm='orthonormal'):2890r"""2891Returns ``True`` if the matrix is equal to its conjugate-transpose.28922893INPUT:28942895- ``tol`` - default: ``1e-12`` - the largest value of the2896absolute value of the difference between two matrix entries2897for which they will still be considered equal.28982899- ``algorithm`` - default: 'orthonormal' - set to 'orthonormal'2900for a stable procedure and set to 'naive' for a fast2901procedure.29022903OUTPUT:29042905``True`` if the matrix is square and equal to the transpose2906with every entry conjugated, and ``False`` otherwise.29072908Note that if conjugation has no effect on elements of the base2909ring (such as for integers), then the :meth:`is_symmetric`2910method is equivalent and faster.29112912The tolerance parameter is used to allow for numerical values2913to be equal if there is a slight difference due to round-off2914and other imprecisions.29152916The result is cached, on a per-tolerance and per-algorithm basis.29172918ALGORITHMS:29192920The naive algorithm simply compares corresponing entries on either2921side of the diagonal (and on the diagonal itself) to see if they are2922conjugates, with equality controlled by the tolerance parameter.29232924The orthonormal algorithm first computes a Schur decomposition2925(via the :meth:`schur` method) and checks that the result is a2926diagonal matrix with real entries.29272928So the naive algorithm can finish quickly for a matrix that is not2929Hermitian, while the orthonormal algorithm will always compute a2930Schur decomposition before going through a similar check of the matrix2931entry-by-entry.29322933EXAMPLES::29342935sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],2936... [-3 - I, -4*I, -2],2937... [-1 + I, -2 - 8*I, 2 + I]])2938sage: A.is_hermitian(algorithm='orthonormal')2939False2940sage: A.is_hermitian(algorithm='naive')2941False2942sage: B = A*A.conjugate_transpose()2943sage: B.is_hermitian(algorithm='orthonormal')2944True2945sage: B.is_hermitian(algorithm='naive')2946True29472948A matrix that is nearly Hermitian, but for one non-real2949diagonal entry. ::29502951sage: A = matrix(CDF, [[ 2, 2-I, 1+4*I],2952... [ 2+I, 3+I, 2-6*I],2953... [1-4*I, 2+6*I, 5]])2954sage: A.is_hermitian(algorithm='orthonormal')2955False2956sage: A[1,1] = 1322957sage: A.is_hermitian(algorithm='orthonormal')2958True29592960We get a unitary matrix from the SVD routine and use this2961numerical matrix to create a matrix that should be Hermitian2962(indeed it should be the identity matrix), but with some2963imprecision. We use this to illustrate that if the tolerance2964is set too small, then we can be too strict about the equality2965of entries and may achieve the wrong result (depending on2966the system)::29672968sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],2969... [-3 - I, -4*I, -2],2970... [-1 + I, -2 - 8*I, 2 + I]])2971sage: U, _, _ = A.SVD()2972sage: B=U*U.conjugate_transpose()2973sage: B.is_hermitian(algorithm='naive')2974True2975sage: B.is_hermitian(algorithm='naive', tol=1.0e-17) # random2976False2977sage: B.is_hermitian(algorithm='naive', tol=1.0e-15)2978True29792980A square, empty matrix is trivially Hermitian. ::29812982sage: A = matrix(RDF, 0, 0)2983sage: A.is_hermitian()2984True29852986Rectangular matrices are never Hermitian, no matter which2987algorithm is requested. ::29882989sage: A = matrix(CDF, 3, 4)2990sage: A.is_hermitian()2991False29922993TESTS:29942995The tolerance must be strictly positive. ::29962997sage: A = matrix(RDF, 2, range(4))2998sage: A.is_hermitian(tol = -3.1)2999Traceback (most recent call last):3000...3001ValueError: tolerance must be positive, not -3.130023003The ``algorithm`` keyword gets checked. ::30043005sage: A = matrix(RDF, 2, range(4))3006sage: A.is_hermitian(algorithm='junk')3007Traceback (most recent call last):3008...3009ValueError: algorithm must be 'naive' or 'orthonormal', not junk30103011AUTHOR:30123013- Rob Beezer (2011-03-30)3014"""3015import sage.rings.complex_double3016global numpy3017tol = float(tol)3018if tol <= 0:3019raise ValueError('tolerance must be positive, not {0}'.format(tol))3020if not algorithm in ['naive', 'orthonormal']:3021raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm))30223023key = 'hermitian_{0}_{1}'.format(algorithm, tol)3024h = self.fetch(key)3025if not h is None:3026return h3027if not self.is_square():3028self.cache(key, False)3029return False3030if self._nrows == 0:3031self.cache(key, True)3032return True3033if numpy is None:3034import numpy3035cdef Py_ssize_t i, j3036cdef Matrix_double_dense T3037if algorithm == 'orthonormal':3038# Schur decomposition over CDF will be diagonal and real iff Hermitian3039_, T = self.schur(base_ring=sage.rings.complex_double.CDF)3040hermitian = T._is_lower_triangular(tol)3041if hermitian:3042for i in range(T._nrows):3043if numpy.absolute(numpy.imag(T.get_unsafe(i,i))) > tol:3044hermitian = False3045break3046elif algorithm == 'naive':3047hermitian = True3048for i in range(self._nrows):3049for j in range(i+1):3050if numpy.absolute(self.get_unsafe(i,j) - self.get_unsafe(j,i).conjugate()) > tol:3051hermitian = False3052break3053if not hermitian:3054break3055self.cache(key, hermitian)3056return hermitian30573058def is_normal(self, tol=1e-12, algorithm='orthonormal'):3059r"""3060Returns ``True`` if the matrix commutes with its conjugate-transpose.30613062INPUT:30633064- ``tol`` - default: ``1e-12`` - the largest value of the3065absolute value of the difference between two matrix entries3066for which they will still be considered equal.30673068- ``algorithm`` - default: 'orthonormal' - set to 'orthonormal'3069for a stable procedure and set to 'naive' for a fast3070procedure.30713072OUTPUT:30733074``True`` if the matrix is square and commutes with its3075conjugate-transpose, and ``False`` otherwise.30763077Normal matrices are precisely those that can be diagonalized3078by a unitary matrix.30793080The tolerance parameter is used to allow for numerical values3081to be equal if there is a slight difference due to round-off3082and other imprecisions.30833084The result is cached, on a per-tolerance and per-algorithm basis.30853086ALGORITHMS:30873088The naive algorithm simply compares entries of the two possible3089products of the matrix with its conjugate-transpose, with equality3090controlled by the tolerance parameter.30913092The orthonormal algorithm first computes a Schur decomposition3093(via the :meth:`schur` method) and checks that the result is a3094diagonal matrix. An orthonormal diagonalization3095is equivalent to being normal.30963097So the naive algorithm can finish fairly quickly for a matrix3098that is not normal, once the products have been computed.3099However, the orthonormal algorithm will compute a Schur3100decomposition before going through a similar check of a3101matrix entry-by-entry.31023103EXAMPLES:31043105First over the complexes. ``B`` is Hermitian, hence normal. ::31063107sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],3108... [-3 - I, -4*I, -2],3109... [-1 + I, -2 - 8*I, 2 + I]])3110sage: B = A*A.conjugate_transpose()3111sage: B.is_hermitian()3112True3113sage: B.is_normal(algorithm='orthonormal')3114True3115sage: B.is_normal(algorithm='naive')3116True3117sage: B[0,0] = I3118sage: B.is_normal(algorithm='orthonormal')3119False3120sage: B.is_normal(algorithm='naive')3121False31223123Now over the reals. Circulant matrices are normal. ::31243125sage: G = graphs.CirculantGraph(20, [3, 7])3126sage: D = digraphs.Circuit(20)3127sage: A = 3*D.adjacency_matrix() - 5*G.adjacency_matrix()3128sage: A = A.change_ring(RDF)3129sage: A.is_normal()3130True3131sage: A.is_normal(algorithm = 'naive')3132True3133sage: A[19,0] = 4.03134sage: A.is_normal()3135False3136sage: A.is_normal(algorithm = 'naive')3137False31383139Skew-Hermitian matrices are normal. ::31403141sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],3142... [-3 - I, -4*I, -2],3143... [-1 + I, -2 - 8*I, 2 + I]])3144sage: B = A - A.conjugate_transpose()3145sage: B.is_hermitian()3146False3147sage: B.is_normal()3148True3149sage: B.is_normal(algorithm='naive')3150True31513152A small matrix that does not fit into any of the usual categories3153of normal matrices. ::31543155sage: A = matrix(RDF, [[1, -1],3156... [1, 1]])3157sage: A.is_normal()3158True3159sage: not A.is_hermitian() and not A.is_skew_symmetric()3160True31613162Sage has several fields besides the entire complex numbers3163where conjugation is non-trivial. ::31643165sage: F.<b> = QuadraticField(-7)3166sage: C = matrix(F, [[-2*b - 3, 7*b - 6, -b + 3],3167... [-2*b - 3, -3*b + 2, -2*b],3168... [ b + 1, 0, -2]])3169sage: C = C*C.conjugate_transpose()3170sage: C.is_normal()3171True31723173A square, empty matrix is trivially normal. ::31743175sage: A = matrix(CDF, 0, 0)3176sage: A.is_normal()3177True31783179Rectangular matrices are never normal, no matter which3180algorithm is requested. ::31813182sage: A = matrix(CDF, 3, 4)3183sage: A.is_normal()3184False31853186TESTS:31873188The tolerance must be strictly positive. ::31893190sage: A = matrix(RDF, 2, range(4))3191sage: A.is_normal(tol = -3.1)3192Traceback (most recent call last):3193...3194ValueError: tolerance must be positive, not -3.131953196The ``algorithm`` keyword gets checked. ::31973198sage: A = matrix(RDF, 2, range(4))3199sage: A.is_normal(algorithm='junk')3200Traceback (most recent call last):3201...3202ValueError: algorithm must be 'naive' or 'orthonormal', not junk32033204AUTHOR:32053206- Rob Beezer (2011-03-31)3207"""3208import sage.rings.complex_double3209global numpy3210tol = float(tol)3211if tol <= 0:3212raise ValueError('tolerance must be positive, not {0}'.format(tol))3213if not algorithm in ['naive', 'orthonormal']:3214raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm))32153216key = 'normal_{0}_{1}'.format(algorithm, tol)3217b = self.fetch(key)3218if not b is None:3219return b3220if not self.is_square():3221self.cache(key, False)3222return False3223if self._nrows == 0:3224self.cache(key, True)3225return True3226cdef Py_ssize_t i, j3227cdef Matrix_double_dense T, left, right3228if algorithm == 'orthonormal':3229# Schur decomposition over CDF will be diagonal iff normal3230_, T = self.schur(base_ring=sage.rings.complex_double.CDF)3231normal = T._is_lower_triangular(tol)3232elif algorithm == 'naive':3233if numpy is None:3234import numpy3235CT = self.conjugate_transpose()3236left = self*CT3237right = CT*self3238normal = True3239# two products are Hermitian, need only check lower triangle3240for i in range(self._nrows):3241for j in range(i+1):3242if numpy.absolute(left.get_unsafe(i,j) - right.get_unsafe(i,j)) > tol:3243normal = False3244break3245if not normal:3246break3247self.cache(key, normal)3248return normal32493250def schur(self, base_ring=None):3251r"""3252Returns the Schur decomposition of the matrix.32533254INPUT:32553256- ``base_ring`` - optional, defaults to the base ring of ``self``.3257Use this to request the base ring of the returned matrices, which3258will affect the format of the results.32593260OUTPUT:32613262A pair of immutable matrices. The first is a unitary matrix `Q`.3263The second, `T`, is upper-triangular when returned over the complex3264numbers, while it is almost upper-triangular over the reals. In the3265latter case, there can be some `2\times 2` blocks on the diagonal3266which represent a pair of conjugate complex eigenvalues of ``self``.32673268If ``self`` is the matrix `A`, then32693270.. math::32713272A = QT({\overline Q})^t32733274where the latter matrix is the conjugate-transpose of ``Q``, which3275is also the inverse of ``Q``, since ``Q`` is unitary.32763277Note that in the case of a normal matrix (Hermitian, symmetric, and3278others), the upper-triangular matrix is a diagonal matrix with3279eigenvalues of ``self`` on the diagonal, and the unitary matrix3280has columns that form an orthonormal basis composed of eigenvectors3281of ``self``. This is known as "orthonormal diagonalization".32823283.. WARNING::32843285The Schur decomposition is not unique, as there may be numerous3286choices for the vectors of the orthonormal basis, and consequently3287different possibilities for the upper-triangular matrix. However,3288the diagonal of the upper-triangular matrix will always contain the3289eigenvalues of the matrix (in the complex version), or `2\times 2`3290block matrices in the real version representing pairs of conjugate3291complex eigenvalues.32923293In particular, results may vary across systems and processors.32943295EXAMPLES:32963297First over the complexes. The similar matrix is always3298upper-triangular in this case. ::32993300sage: A = matrix(CDF, 4, 4, range(16)) + matrix(CDF, 4, 4, [x^3*I for x in range(0, 16)])3301sage: Q, T = A.schur()3302sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)3303[1.0 0.0 0.0 0.0]3304[0.0 1.0 0.0 0.0]3305[0.0 0.0 1.0 0.0]3306[0.0 0.0 0.0 1.0]3307sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i)])3308True3309sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)3310[0.0 0.0 0.0 0.0]3311[0.0 0.0 0.0 0.0]3312[0.0 0.0 0.0 0.0]3313[0.0 0.0 0.0 0.0]3314sage: eigenvalues = [T[i,i] for i in range(4)]; eigenvalues3315[30.733... + 4648.541...*I, -0.184... - 159.057...*I, -0.523... + 11.158...*I, -0.025... - 0.642...*I]3316sage: A.eigenvalues()3317[30.733... + 4648.541...*I, -0.184... - 159.057...*I, -0.523... + 11.158...*I, -0.025... - 0.642...*I]3318sage: abs(A.norm()-T.norm()) < 1e-103319True33203321We begin with a real matrix but ask for a decomposition over the3322complexes. The result will yield an upper-triangular matrix over3323the complex numbers for ``T``. ::33243325sage: A = matrix(RDF, 4, 4, [x^3 for x in range(16)])3326sage: Q, T = A.schur(base_ring=CDF)3327sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)3328[1.0 0.0 0.0 0.0]3329[0.0 1.0 0.0 0.0]3330[0.0 0.0 1.0 0.0]3331[0.0 0.0 0.0 1.0]3332sage: T.parent()3333Full MatrixSpace of 4 by 4 dense matrices over Complex Double Field3334sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i)])3335True3336sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)3337[0.0 0.0 0.0 0.0]3338[0.0 0.0 0.0 0.0]3339[0.0 0.0 0.0 0.0]3340[0.0 0.0 0.0 0.0]33413342Now totally over the reals. But with complex eigenvalues, the3343similar matrix may not be upper-triangular. But "at worst" there3344may be some `2\times 2` blocks on the diagonal which represent3345a pair of conjugate complex eigenvalues. These blocks will then3346just interrupt the zeros below the main diagonal. This example3347has a pair of these of the blocks. ::33483349sage: A = matrix(RDF, 4, 4, [[1, 0, -3, -1],3350... [4, -16, -7, 0],3351... [1, 21, 1, -2],3352... [26, -1, -2, 1]])3353sage: Q, T = A.schur()3354sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)3355[1.0 0.0 0.0 0.0]3356[0.0 1.0 0.0 0.0]3357[0.0 0.0 1.0 0.0]3358[0.0 0.0 0.0 1.0]3359sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i)])3360False3361sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i-1)])3362True3363sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)3364[0.0 0.0 0.0 0.0]3365[0.0 0.0 0.0 0.0]3366[0.0 0.0 0.0 0.0]3367[0.0 0.0 0.0 0.0]3368sage: sorted(T[0:2,0:2].eigenvalues() + T[2:4,2:4].eigenvalues())3369[-5.710... - 8.382...*I, -5.710... + 8.382...*I, -0.789... - 2.336...*I, -0.789... + 2.336...*I]3370sage: sorted(A.eigenvalues())3371[-5.710... - 8.382...*I, -5.710... + 8.382...*I, -0.789... - 2.336...*I, -0.789... + 2.336...*I]3372sage: abs(A.norm()-T.norm()) < 1e-123373True33743375Starting with complex numbers and requesting a result over the reals3376will never happen. ::33773378sage: A = matrix(CDF, 2, 2, [[2+I, -1+3*I], [5-4*I, 2-7*I]])3379sage: A.schur(base_ring=RDF)3380Traceback (most recent call last):3381...3382TypeError: unable to convert input matrix over CDF to a matrix over RDF33833384If theory predicts your matrix is real, but it contains some3385very small imaginary parts, you can specify the cutoff for "small"3386imaginary parts, then request the output as real matrices, and let3387the routine do the rest. ::33883389sage: A = matrix(RDF, 2, 2, [1, 1, -1, 0]) + matrix(CDF, 2, 2, [1.0e-14*I]*4)3390sage: B = A.zero_at(1.0e-12)3391sage: B.parent()3392Full MatrixSpace of 2 by 2 dense matrices over Complex Double Field3393sage: Q, T = B.schur(RDF)3394sage: Q.parent()3395Full MatrixSpace of 2 by 2 dense matrices over Real Double Field3396sage: T.parent()3397Full MatrixSpace of 2 by 2 dense matrices over Real Double Field3398sage: Q.round(6)3399[ 0.707107 0.707107]3400[-0.707107 0.707107]3401sage: T.round(6)3402[ 0.5 1.5]3403[-0.5 0.5]3404sage: (Q*T*Q.conjugate().transpose()-B).zero_at(1.0e-11)3405[0.0 0.0]3406[0.0 0.0]34073408A Hermitian matrix has real eigenvalues, so the similar matrix3409will be upper-triangular. Furthermore, a Hermitian matrix is3410diagonalizable with respect to an orthonormal basis, composed3411of eigenvectors of the matrix. Here that basis is the set of3412columns of the unitary matrix. ::34133414sage: A = matrix(CDF, [[ 52, -9*I - 8, 6*I - 187, -188*I + 2],3415... [ 9*I - 8, 12, -58*I + 59, 30*I + 42],3416... [-6*I - 187, 58*I + 59, 2677, 2264*I + 65],3417... [ 188*I + 2, -30*I + 42, -2264*I + 65, 2080]])3418sage: Q, T = A.schur()3419sage: T = T.zero_at(1.0e-12).change_ring(RDF)3420sage: T.round(6)3421[4680.13301 0.0 0.0 0.0]3422[ 0.0 102.715967 0.0 0.0]3423[ 0.0 0.0 35.039344 0.0]3424[ 0.0 0.0 0.0 3.11168]3425sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)3426[1.0 0.0 0.0 0.0]3427[0.0 1.0 0.0 0.0]3428[0.0 0.0 1.0 0.0]3429[0.0 0.0 0.0 1.0]3430sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)3431[0.0 0.0 0.0 0.0]3432[0.0 0.0 0.0 0.0]3433[0.0 0.0 0.0 0.0]3434[0.0 0.0 0.0 0.0]34353436Similarly, a real symmetric matrix has only real eigenvalues,3437and there is an orthonormal basis composed of eigenvectors of3438the matrix. ::34393440sage: A = matrix(RDF, [[ 1, -2, 5, -3],3441... [-2, 9, 1, 5],3442... [ 5, 1, 3 , 7],3443... [-3, 5, 7, -8]])3444sage: Q, T = A.schur()3445sage: Q.round(4)3446[-0.3027 -0.751 0.576 -0.1121]3447[ 0.139 -0.3892 -0.2648 0.8713]3448[ 0.4361 0.359 0.7599 0.3217]3449[ -0.836 0.3945 0.1438 0.3533]3450sage: T = T.zero_at(10^-12)3451sage: all(abs(e) < 10^-4 for e in (T - diagonal_matrix(RDF, [-13.5698, -0.8508, 7.7664, 11.6542])).list())3452True3453sage: (Q*Q.transpose()).zero_at(1.0e-12)3454[1.0 0.0 0.0 0.0]3455[0.0 1.0 0.0 0.0]3456[0.0 0.0 1.0 0.0]3457[0.0 0.0 0.0 1.0]3458sage: (Q*T*Q.transpose()-A).zero_at(1.0e-11)3459[0.0 0.0 0.0 0.0]3460[0.0 0.0 0.0 0.0]3461[0.0 0.0 0.0 0.0]3462[0.0 0.0 0.0 0.0]34633464The results are cached, both as a real factorization and also as a3465complex factorization. This means the returned matrices are3466immutable. ::34673468sage: A = matrix(RDF, 2, 2, [[0, -1], [1, 0]])3469sage: Qr, Tr = A.schur(base_ring=RDF)3470sage: Qc, Tc = A.schur(base_ring=CDF)3471sage: all([M.is_immutable() for M in [Qr, Tr, Qc, Tc]])3472True3473sage: Tr.round(6) != Tc.round(6)3474True34753476TESTS:34773478The Schur factorization is only defined for square matrices. ::34793480sage: A = matrix(RDF, 4, 5, range(20))3481sage: A.schur()3482Traceback (most recent call last):3483...3484ValueError: Schur decomposition requires a square matrix, not a 4 x 5 matrix34853486A base ring request is checked. ::34873488sage: A = matrix(RDF, 3, range(9))3489sage: A.schur(base_ring=QQ)3490Traceback (most recent call last):3491...3492ValueError: base ring of Schur decomposition matrices must be RDF or CDF, not Rational Field34933494AUTHOR:34953496- Rob Beezer (2011-03-31)3497"""3498global scipy3499from sage.rings.real_double import RDF3500from sage.rings.complex_double import CDF35013502cdef Matrix_double_dense Q, T35033504if not self.is_square():3505raise ValueError('Schur decomposition requires a square matrix, not a {0} x {1} matrix'.format(self.nrows(), self.ncols()))3506if base_ring == None:3507base_ring = self.base_ring()3508if not base_ring in [RDF, CDF]:3509raise ValueError('base ring of Schur decomposition matrices must be RDF or CDF, not {0}'.format(base_ring))35103511if self.base_ring() != base_ring:3512try:3513self = self.change_ring(base_ring)3514except TypeError:3515raise TypeError('unable to convert input matrix over CDF to a matrix over RDF')3516if base_ring == CDF:3517format = 'complex'3518else:3519format = 'real'35203521schur = self.fetch('schur_factors_' + format)3522if not schur is None:3523return schur3524if scipy is None:3525import scipy3526import scipy.linalg3527Q = self._new(self._nrows, self._nrows)3528T = self._new(self._nrows, self._nrows)3529T._matrix_numpy, Q._matrix_numpy = scipy.linalg.schur(self._matrix_numpy, output=format)3530Q.set_immutable()3531T.set_immutable()3532# Our return order is the reverse of NumPy's3533schur = (Q, T)3534self.cache('schur_factors_' + format, schur)3535return schur35363537def cholesky(self):3538r"""3539Returns the Cholesky factorization of a matrix that3540is real symmetric, or complex Hermitian.35413542INPUT:35433544Any square matrix with entries from ``RDF`` that is symmetric, or3545with entries from ``CDF`` that is Hermitian. The matrix must3546be positive definite for the Cholesky decomposition to exist.35473548OUTPUT:35493550For a matrix `A` the routine returns a lower triangular3551matrix `L` such that,35523553.. math::35543555A = LL^\ast35563557where `L^\ast` is the conjugate-transpose in the complex case,3558and just the transpose in the real case. If the matrix fails3559to be positive definite (perhaps because it is not symmetric3560or Hermitian), then this function raises a ``ValueError``.35613562IMPLEMENTATION:35633564The existence of a Cholesky decomposition and the3565positive definite property are equivalent. So this3566method and the :meth:`is_positive_definite` method compute and3567cache both the Cholesky decomposition and the3568positive-definiteness. So the :meth:`is_positive_definite`3569method or catching a ``ValueError`` from the :meth:`cholesky`3570method are equally expensive computationally and if the3571decomposition exists, it is cached as a side-effect of either3572routine.35733574EXAMPLES:35753576A real matrix that is symmetric and positive definite. ::35773578sage: M = matrix(RDF,[[ 1, 1, 1, 1, 1],3579... [ 1, 5, 31, 121, 341],3580... [ 1, 31, 341, 1555, 4681],3581... [ 1,121, 1555, 7381, 22621],3582... [ 1,341, 4681, 22621, 69905]])3583sage: M.is_symmetric()3584True3585sage: L = M.cholesky()3586sage: L.round(6).zero_at(10^-10)3587[ 1.0 0.0 0.0 0.0 0.0]3588[ 1.0 2.0 0.0 0.0 0.0]3589[ 1.0 15.0 10.723805 0.0 0.0]3590[ 1.0 60.0 60.985814 7.792973 0.0]3591[ 1.0 170.0 198.623524 39.366567 1.7231]3592sage: (L*L.transpose()).round(6).zero_at(10^-10)3593[ 1.0 1.0 1.0 1.0 1.0]3594[ 1.0 5.0 31.0 121.0 341.0]3595[ 1.0 31.0 341.0 1555.0 4681.0]3596[ 1.0 121.0 1555.0 7381.0 22621.0]3597[ 1.0 341.0 4681.0 22621.0 69905.0]35983599A complex matrix that is Hermitian and positive definite. ::36003601sage: A = matrix(CDF, [[ 23, 17*I + 3, 24*I + 25, 21*I],3602... [ -17*I + 3, 38, -69*I + 89, 7*I + 15],3603... [-24*I + 25, 69*I + 89, 976, 24*I + 6],3604... [ -21*I, -7*I + 15, -24*I + 6, 28]])3605sage: A.is_hermitian()3606True3607sage: L = A.cholesky()3608sage: L.round(6).zero_at(10^-10)3609[ 4.795832 0.0 0.0 0.0]3610[ 0.625543 - 3.544745*I 5.004346 0.0 0.0]3611[ 5.21286 - 5.004346*I 13.588189 + 10.721116*I 24.984023 0.0]3612[ -4.378803*I -0.104257 - 0.851434*I -0.21486 + 0.371348*I 2.811799]3613sage: (L*L.conjugate_transpose()).round(6).zero_at(10^-10)3614[ 23.0 3.0 + 17.0*I 25.0 + 24.0*I 21.0*I]3615[ 3.0 - 17.0*I 38.0 89.0 - 69.0*I 15.0 + 7.0*I]3616[25.0 - 24.0*I 89.0 + 69.0*I 976.0 6.0 + 24.0*I]3617[ -21.0*I 15.0 - 7.0*I 6.0 - 24.0*I 28.0]36183619This routine will recognize when the input matrix is not3620positive definite. The negative eigenvalues are an3621equivalent indicator. (Eigenvalues of a Hermitian matrix3622must be real, so there is no loss in ignoring the imprecise3623imaginary parts). ::36243625sage: A = matrix(RDF, [[ 3, -6, 9, 6, -9],3626... [-6, 11, -16, -11, 17],3627... [ 9, -16, 28, 16, -40],3628... [ 6, -11, 16, 9, -19],3629... [-9, 17, -40, -19, 68]])3630sage: A.is_symmetric()3631True3632sage: A.eigenvalues()3633[108.07..., 13.02..., -0.02..., -0.70..., -1.37...]3634sage: A.cholesky()3635Traceback (most recent call last):3636...3637ValueError: matrix is not positive definite36383639sage: B = matrix(CDF, [[ 2, 4 - 2*I, 2 + 2*I],3640... [4 + 2*I, 8, 10*I],3641... [2 - 2*I, -10*I, -3]])3642sage: B.is_hermitian()3643True3644sage: [ev.real() for ev in B.eigenvalues()]3645[15.88..., 0.08..., -8.97...]3646sage: B.cholesky()3647Traceback (most recent call last):3648...3649ValueError: matrix is not positive definite36503651TESTS:36523653A trivial case. ::36543655sage: A = matrix(RDF, 0, [])3656sage: A.cholesky()3657[]36583659The Cholesky factorization is only defined for square matrices. ::36603661sage: A = matrix(RDF, 4, 5, range(20))3662sage: A.cholesky()3663Traceback (most recent call last):3664...3665ValueError: Cholesky decomposition requires a square matrix, not a 4 x 5 matrix3666"""3667from sage.rings.real_double import RDF3668from sage.rings.complex_double import CDF36693670cdef Matrix_double_dense L3671cache_cholesky = 'cholesky'3672cache_posdef = 'positive_definite'36733674if not self.is_square():3675msg = "Cholesky decomposition requires a square matrix, not a {0} x {1} matrix"3676self.cache(cache_posdef, False)3677raise ValueError(msg.format(self.nrows(), self.ncols()))3678if self._nrows == 0: # special case3679self.cache(cache_posdef, True)3680return self.__copy__()36813682L = self.fetch(cache_cholesky)3683if L is None:3684L = self._new()3685global scipy3686if scipy is None:3687import scipy3688import scipy.linalg3689from numpy.linalg import LinAlgError3690try:3691L._matrix_numpy = scipy.linalg.cholesky(self._matrix_numpy, lower=1)3692except LinAlgError:3693self.cache(cache_posdef, False)3694raise ValueError("matrix is not positive definite")3695L.set_immutable()3696self.cache(cache_cholesky, L)3697self.cache(cache_posdef, True)3698return L36993700def is_positive_definite(self):3701r"""3702Determines if a matrix is positive definite.37033704A matrix `A` is positive definite if it is square,3705is Hermitian (which reduces to symmetric in the real case),3706and for every nonzero vector `\vec{x}`,37073708.. math::37093710\vec{x}^\ast A \vec{x} > 037113712where `\vec{x}^\ast` is the conjugate-transpose in the3713complex case and just the transpose in the real case.3714Equivalently, a positive definite matrix has only positive3715eigenvalues and only positive determinants of leading3716principal submatrices.37173718INPUT:37193720Any matrix over ``RDF`` or ``CDF``.37213722OUTPUT:37233724``True`` if and only if the matrix is square, Hermitian,3725and meets the condition above on the quadratic form.3726The result is cached.37273728IMPLEMENTATION:37293730The existence of a Cholesky decomposition and the3731positive definite property are equivalent. So this3732method and the :meth:`cholesky` method compute and3733cache both the Cholesky decomposition and the3734positive-definiteness. So the :meth:`is_positive_definite`3735method or catching a ``ValueError`` from the :meth:`cholesky`3736method are equally expensive computationally and if the3737decomposition exists, it is cached as a side-effect of either3738routine.37393740EXAMPLES:37413742A matrix over ``RDF`` that is positive definite. ::37433744sage: M = matrix(RDF,[[ 1, 1, 1, 1, 1],3745... [ 1, 5, 31, 121, 341],3746... [ 1, 31, 341, 1555, 4681],3747... [ 1,121, 1555, 7381, 22621],3748... [ 1,341, 4681, 22621, 69905]])3749sage: M.is_symmetric()3750True3751sage: M.eigenvalues()3752[77547.66..., 82.44..., 2.41..., 0.46..., 0.011...]3753sage: [round(M[:i,:i].determinant()) for i in range(1, M.nrows()+1)]3754[1, 4, 460, 27936, 82944]3755sage: M.is_positive_definite()3756True37573758A matrix over ``CDF`` that is positive definite. ::37593760sage: C = matrix(CDF, [[ 23, 17*I + 3, 24*I + 25, 21*I],3761... [ -17*I + 3, 38, -69*I + 89, 7*I + 15],3762... [-24*I + 25, 69*I + 89, 976, 24*I + 6],3763... [ -21*I, -7*I + 15, -24*I + 6, 28]])3764sage: C.is_hermitian()3765True3766sage: [x.real() for x in C.eigenvalues()]3767[991.46..., 55.96..., 3.69..., 13.87...]3768sage: [round(C[:i,:i].determinant().real()) for i in range(1, C.nrows()+1)]3769[23, 576, 359540, 2842600]3770sage: C.is_positive_definite()3771True37723773A matrix over ``RDF`` that is not positive definite. ::37743775sage: A = matrix(RDF, [[ 3, -6, 9, 6, -9],3776... [-6, 11, -16, -11, 17],3777... [ 9, -16, 28, 16, -40],3778... [ 6, -11, 16, 9, -19],3779... [-9, 17, -40, -19, 68]])3780sage: A.is_symmetric()3781True3782sage: A.eigenvalues()3783[108.07..., 13.02..., -0.02..., -0.70..., -1.37...]3784sage: [round(A[:i,:i].determinant()) for i in range(1, A.nrows()+1)]3785[3, -3, -15, 30, -30]3786sage: A.is_positive_definite()3787False37883789A matrix over ``CDF`` that is not positive definite. ::37903791sage: B = matrix(CDF, [[ 2, 4 - 2*I, 2 + 2*I],3792... [4 + 2*I, 8, 10*I],3793... [2 - 2*I, -10*I, -3]])3794sage: B.is_hermitian()3795True3796sage: [ev.real() for ev in B.eigenvalues()]3797[15.88..., 0.08..., -8.97...]3798sage: [round(B[:i,:i].determinant().real()) for i in range(1, B.nrows()+1)]3799[2, -4, -12]3800sage: B.is_positive_definite()3801False38023803A large random matrix that is guaranteed by theory to be3804positive definite. ::38053806sage: R = random_matrix(CDF, 200)3807sage: H = R.conjugate_transpose()*R3808sage: H.is_positive_definite()3809True38103811TESTS:38123813A trivially small case. ::38143815sage: S = matrix(CDF, [])3816sage: S.nrows(), S.ncols()3817(0, 0)3818sage: S.is_positive_definite()3819True38203821A rectangular matrix will never be positive definite. ::38223823sage: R = matrix(RDF, 2, 3, range(6))3824sage: R.is_positive_definite()3825False38263827A non-Hermitian matrix will never be positive definite. ::38283829sage: T = matrix(CDF, 8, 8, range(64))3830sage: T.is_positive_definite()3831False38323833AUTHOR:38343835- Rob Beezer (2012-05-28)3836"""3837cache_str = 'positive_definite'3838posdef = self.fetch(cache_str)3839if posdef is None:3840try:3841self.cholesky()3842except ValueError:3843pass3844posdef = self.fetch(cache_str)3845return posdef38463847cdef Vector _vector_times_matrix_(self,Vector v):3848if self._nrows == 0 or self._ncols == 0:3849return self._row_ambient_module().zero_vector()3850global numpy3851if numpy is None:3852import numpy38533854v_numpy = numpy.array([self._python_dtype(i) for i in v])38553856M = self._row_ambient_module()3857ans = numpy.dot(v_numpy,self._matrix_numpy)3858return M(ans)38593860cdef Vector _matrix_times_vector_(self,Vector v):3861if self._nrows == 0 or self._ncols == 0:3862return self._column_ambient_module().zero_vector()38633864global numpy3865if numpy is None:3866import numpy38673868v_numpy = numpy.array([self._python_dtype(i) for i in v], dtype=self._numpy_dtype)38693870M = self._column_ambient_module()3871ans = numpy.dot(self._matrix_numpy, v_numpy)3872return M(ans)38733874def numpy(self, dtype=None):3875"""3876This method returns a copy of the matrix as a numpy array. It3877uses the numpy C/api so is very fast.38783879INPUT:38803881- ``dtype`` - The desired data-type for the array. If not given,3882then the type will be determined as the minimum type required3883to hold the objects in the sequence.38843885EXAMPLES::38863887sage: m = matrix(RDF,[[1,2],[3,4]])3888sage: n = m.numpy()3889sage: import numpy3890sage: numpy.linalg.eig(n)3891(array([-0.37228132, 5.37228132]), array([[-0.82456484, -0.41597356],3892[ 0.56576746, -0.90937671]]))3893sage: m = matrix(RDF, 2, range(6)); m3894[0.0 1.0 2.0]3895[3.0 4.0 5.0]3896sage: m.numpy()3897array([[ 0., 1., 2.],3898[ 3., 4., 5.]])38993900Alternatively, numpy automatically calls this function (via3901the magic :meth:`__array__` method) to convert Sage matrices3902to numpy arrays::39033904sage: import numpy3905sage: m = matrix(RDF, 2, range(6)); m3906[0.0 1.0 2.0]3907[3.0 4.0 5.0]3908sage: numpy.array(m)3909array([[ 0., 1., 2.],3910[ 3., 4., 5.]])3911sage: numpy.array(m).dtype3912dtype('float64')3913sage: m = matrix(CDF, 2, range(6)); m3914[0.0 1.0 2.0]3915[3.0 4.0 5.0]3916sage: numpy.array(m)3917array([[ 0.+0.j, 1.+0.j, 2.+0.j],3918[ 3.+0.j, 4.+0.j, 5.+0.j]])3919sage: numpy.array(m).dtype3920dtype('complex128')39213922TESTS::39233924sage: m = matrix(RDF,0,5,[]); m3925[]3926sage: m.numpy()3927array([], shape=(0, 5), dtype=float64)3928sage: m = matrix(RDF,5,0,[]); m3929[]3930sage: m.numpy()3931array([], shape=(5, 0), dtype=float64)3932"""3933import numpy as np3934if dtype is None or self._numpy_dtype == np.dtype(dtype):3935return self._matrix_numpy.copy()3936else:3937return matrix_dense.Matrix_dense.numpy(self, dtype=dtype)39383939def _replace_self_with_numpy(self,numpy_matrix):3940"""39413942EXAMPLES::39433944sage: import numpy3945sage: a = numpy.array([[1,2],[3,4]], 'float64')3946sage: m = matrix(RDF,2,2,0)3947sage: m._replace_self_with_numpy(a)3948sage: m3949[1.0 2.0]3950[3.0 4.0]3951"""3952if (<object>self._matrix_numpy).shape != (<object>numpy_matrix).shape:3953raise ValueError("matrix shapes are not the same")3954self._matrix_numpy = numpy_matrix.astype(self._numpy_dtype)395539563957def _replace_self_with_numpy32(self,numpy_matrix):3958"""39593960EXAMPLES::39613962sage: import numpy3963sage: a = numpy.array([[1,2],[3,4]], 'float32')3964sage: m = matrix(RDF,2,2,0)3965sage: m._replace_self_with_numpy32(a)3966sage: m3967[1.0 2.0]3968[3.0 4.0]3969"""3970#TODO find where this is used and change it3971self._replace_self_with_numpy(numpy_matrix)397239733974def _hadamard_row_bound(self):3975r"""3976Return an integer n such that the absolute value of the3977determinant of this matrix is at most $10^n$.39783979EXAMPLES::39803981sage: a = matrix(RDF, 3, [1,2,5,7,-3,4,2,1,123])3982sage: a._hadamard_row_bound()398343984sage: a.det()3985-2014.03986sage: 10^43987100003988"""3989cdef double d = 0, s3990cdef Py_ssize_t i, j3991for i from 0 <= i < self._nrows:3992s = 03993for j from 0 <= j < self._ncols:3994s += self.get_unsafe(i,j)**23995d += math.log(s)3996d /= 23997return int(math.ceil(d / math.log(10)))39983999@rename_keyword(deprecation=6094, method="algorithm")4000def exp(self, algorithm='pade', order=None):4001r"""4002Calculate the exponential of this matrix X, which is the matrix40034004.. math::40054006e^X = \sum_{k=0}^{\infty} \frac{X^k}{k!}.40074008INPUT:40094010- algorithm -- 'pade', 'eig', or 'taylor'; the algorithm used to4011compute the exponential.40124013- order -- for the Taylor series algorithm, this specifies the4014order of the Taylor series used. This is ignored for the4015other algorithms. The current default (from scipy) is 20.40164017EXAMPLES::40184019sage: A=matrix(RDF, 2, [1,2,3,4]); A4020[1.0 2.0]4021[3.0 4.0]4022sage: A.exp()4023[51.9689561987 74.736564567]4024[112.104846851 164.073803049]4025sage: A.exp(algorithm='eig')4026[51.9689561987 74.736564567]4027[112.104846851 164.073803049]4028sage: A.exp(algorithm='taylor', order=5)4029[19.9583333333 28.0833333333]4030[ 42.125 62.0833333333]4031sage: A.exp(algorithm='taylor')4032[51.9689035511 74.7364878369]4033[112.104731755 164.073635306]40344035sage: A=matrix(CDF, 2, [1,2+I,3*I,4]); A4036[ 1.0 2.0 + 1.0*I]4037[ 3.0*I 4.0]4038sage: A.exp()4039[-19.6146029538 + 12.5177438468*I 3.79496364496 + 28.8837993066*I]4040[-32.3835809809 + 21.8842359579*I 2.26963300409 + 44.9013248277*I]4041sage: A.exp(algorithm='eig')4042[-19.6146029538 + 12.5177438468*I 3.79496364496 + 28.8837993066*I]4043[-32.3835809809 + 21.8842359579*I 2.26963300409 + 44.9013248277*I]4044sage: A.exp(algorithm='taylor', order=5)4045[ -6.29166666667 + 14.25*I 14.0833333333 + 15.7916666667*I]4046[ -10.5 + 26.375*I 20.0833333333 + 24.75*I]4047sage: A.exp(algorithm='taylor')4048[-19.6146006163 + 12.5177432169*I 3.79496442472 + 28.8837964828*I]4049[-32.3835771246 + 21.8842351994*I 2.26963458304 + 44.9013203415*I]4050"""4051if algorithm not in ('pade', 'eig', 'taylor'):4052raise ValueError("algorithm must be 'pade', 'eig', or 'taylor'")40534054global scipy4055if scipy is None:4056import scipy4057import scipy.linalg40584059cdef Matrix_double_dense M4060M = self._new()40614062if algorithm=='pade':4063M._matrix_numpy = scipy.linalg.expm(self._matrix_numpy)4064elif algorithm=='eig':4065M._matrix_numpy = scipy.linalg.expm2(self._matrix_numpy)4066elif algorithm=='taylor':4067if order is None:4068M._matrix_numpy = scipy.linalg.expm3(self._matrix_numpy)4069else:4070M._matrix_numpy = scipy.linalg.expm3(self._matrix_numpy, q=order)40714072return M40734074def zero_at(self, eps):4075"""4076Returns a copy of the matrix where elements smaller than or4077equal to ``eps`` are replaced with zeroes. For complex matrices,4078the real and imaginary parts are considered individually.40794080This is useful for modifying output from algorithms which have large4081relative errors when producing zero elements, e.g. to create reliable4082doctests.40834084INPUT:40854086- ``eps`` - Cutoff value40874088OUTPUT:40894090A modified copy of the matrix.40914092EXAMPLES::40934094sage: a=matrix([[1, 1e-4r, 1+1e-100jr], [1e-8+3j, 0, 1e-58r]])4095sage: a4096[ 1.0 0.0001 1.0 + 1e-100*I]4097[ 1e-08 + 3.0*I 0.0 1e-58]4098sage: a.zero_at(1e-50)4099[ 1.0 0.0001 1.0]4100[1e-08 + 3.0*I 0.0 0.0]4101sage: a.zero_at(1e-4)4102[ 1.0 0.0 1.0]4103[3.0*I 0.0 0.0]4104410541064107"""4108global numpy4109cdef Matrix_double_dense M4110if numpy is None:4111import numpy4112eps = float(eps)4113out = self._matrix_numpy.copy()4114if self._sage_dtype is sage.rings.complex_double.CDF:4115out.real[numpy.abs(out.real) <= eps] = 04116out.imag[numpy.abs(out.imag) <= eps] = 04117else:4118out[numpy.abs(out) <= eps] = 04119M = self._new()4120M._matrix_numpy = out4121return M41224123def round(self, ndigits=0):4124"""4125Returns a copy of the matrix where all entries have been rounded4126to a given precision in decimal digits (default 0 digits).41274128INPUT:41294130- ``ndigits`` - The precision in number of decimal digits41314132OUTPUT:41334134A modified copy of the matrix41354136EXAMPLES::41374138sage: M=matrix(CDF, [[10.234r + 34.2343jr, 34e10r]])4139sage: M4140[10.234 + 34.2343*I 3.4e+11]4141sage: M.round(2)4142[10.23 + 34.23*I 3.4e+11]4143sage: M.round()4144[10.0 + 34.0*I 3.4e+11]4145"""4146global numpy4147cdef Matrix_double_dense M4148if numpy is None:4149import numpy4150ndigits = int(ndigits)4151M = self._new()4152M._matrix_numpy = numpy.round(self._matrix_numpy, ndigits)4153return M41544155def _normalize_columns(self):4156"""4157Returns a copy of the matrix where each column has been4158multiplied by plus or minus 1, to guarantee that the real4159part of the leading entry of each nonzero column is positive.41604161This is useful for modifying output from algorithms which4162produce matrices which are only well-defined up to signs of4163the columns, for example an algorithm which should produce an4164orthogonal matrix.41654166OUTPUT:41674168A modified copy of the matrix.41694170EXAMPLES::41714172sage: a=matrix(CDF, [[1, -2+I, 0, -3*I], [2, 2, -2, 2], [-3, -3, -3, -2]])4173sage: a4174[ 1.0 -2.0 + 1.0*I 0.0 -3.0*I]4175[ 2.0 2.0 -2.0 2.0]4176[ -3.0 -3.0 -3.0 -2.0]4177sage: a._normalize_columns()4178[ 1.0 2.0 - 1.0*I -0.0 -3.0*I]4179[ 2.0 -2.0 2.0 2.0]4180[ -3.0 3.0 3.0 -2.0]4181"""4182M = self.__copy__()4183cdef Py_ssize_t i, j4184for j from 0 <= j < M.ncols():4185for i from 0 <= i < M.column(j).degree():4186a = M.column(j)[i].real()4187if a != 0:4188if a < 0:4189M.rescale_col(j, -1)4190break4191return M41924193def _normalize_rows(self):4194"""4195Returns a copy of the matrix where each row has been4196multiplied by plus or minus 1, to guarantee that the real4197part of the leading entry of each nonzero row is positive.41984199This is useful for modifying output from algorithms which4200produce matrices which are only well-defined up to signs of4201the rows, for example an algorithm which should produce an4202upper triangular matrix.42034204OUTPUT:42054206A modified copy of the matrix.42074208EXAMPLES::42094210sage: a=matrix(CDF, [[1, 2, -3], [-2+I, 2, -3], [0, -2, -3], [-3*I, 2, -2]])4211sage: a4212[ 1.0 2.0 -3.0]4213[-2.0 + 1.0*I 2.0 -3.0]4214[ 0.0 -2.0 -3.0]4215[ -3.0*I 2.0 -2.0]4216sage: a._normalize_rows()4217[ 1.0 2.0 -3.0]4218[2.0 - 1.0*I -2.0 3.0]4219[ -0.0 2.0 3.0]4220[ -3.0*I 2.0 -2.0]4221"""4222return self.transpose()._normalize_columns().transpose()422342244225