Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/matrix/matrix_double_dense.pyx
8815 views
1
"""
2
Dense matrices using a NumPy backend.
3
4
5
This serves as a base class for dense matrices over
6
Real Double Field and Complex Double Field.
7
8
AUTHORS:
9
10
- Jason Grout, Sep 2008: switch to NumPy backend, factored out the Matrix_double_dense class
11
12
- Josh Kantor
13
14
- William Stein: many bug fixes and touch ups.
15
16
17
EXAMPLES::
18
19
sage: b=Mat(RDF,2,3).basis()
20
sage: b[0]
21
[1.0 0.0 0.0]
22
[0.0 0.0 0.0]
23
24
25
We deal with the case of zero rows or zero columns::
26
27
sage: m = MatrixSpace(RDF,0,3)
28
sage: m.zero_matrix()
29
[]
30
31
TESTS::
32
33
sage: a = matrix(RDF,2,range(4), sparse=False)
34
sage: TestSuite(a).run()
35
sage: a = matrix(CDF,2,range(4), sparse=False)
36
sage: TestSuite(a).run()
37
"""
38
39
#*****************************************************************************
40
# Copyright (C) 2004,2005,2006 Joshua Kantor <[email protected]>
41
#
42
# Distributed under the terms of the GNU General Public License (GPL)
43
# as published by the Free Software Foundation; either version 2 of
44
# the License, or (at your option) any later version.
45
# http://www.gnu.org/licenses/
46
#*****************************************************************************
47
48
import math
49
50
import sage.rings.real_double
51
import sage.rings.complex_double
52
53
from matrix cimport Matrix
54
from sage.structure.element cimport ModuleElement,Vector
55
from constructor import matrix
56
from sage.modules.free_module_element import vector
57
cimport sage.structure.element
58
from matrix_space import MatrixSpace
59
from sage.misc.decorators import rename_keyword
60
61
cimport numpy as cnumpy
62
63
numpy=None
64
scipy=None
65
66
# This is for the Numpy C API to work
67
cnumpy.import_array()
68
69
70
cdef class Matrix_double_dense(matrix_dense.Matrix_dense):
71
"""
72
Base class for matrices over the Real Double Field and the Complex
73
Double Field. These are supposed to be fast matrix operations
74
using C doubles. Most operations are implemented using numpy which
75
will call the underlying BLAS on the system.
76
77
This class cannot be instantiated on its own. The numpy matrix
78
creation depends on several variables that are set in the
79
subclasses.
80
81
EXAMPLES::
82
83
sage: m = Matrix(RDF, [[1,2],[3,4]])
84
sage: m**2
85
[ 7.0 10.0]
86
[15.0 22.0]
87
sage: n= m^(-1); n
88
[-2.0 1.0]
89
[ 1.5 -0.5]
90
"""
91
92
93
########################################################################
94
# LEVEL 1 functionality
95
# * __cinit__
96
# * __dealloc__
97
# * __init__
98
# * set_unsafe
99
# * get_unsafe
100
# * __richcmp__ -- always the same
101
# * __hash__ -- always simple
102
########################################################################
103
def __cinit__(self, parent, entries, copy, coerce):
104
"""
105
Set up a new matrix
106
"""
107
matrix_dense.Matrix_dense.__init__(self,parent)
108
return
109
110
def __create_matrix__(self):
111
"""
112
Create a new uninitialized numpy matrix to hold the data for the class.
113
114
This function assumes that self._numpy_dtypeint and
115
self._nrows and self._ncols have already been initialized.
116
117
EXAMPLE:
118
In this example, we throw away the current matrix and make a
119
new uninitialized matrix representing the data for the class.::
120
121
sage: a=matrix(RDF, 3, range(9))
122
sage: a.__create_matrix__()
123
"""
124
cdef cnumpy.npy_intp dims[2]
125
dims[0] = self._nrows
126
dims[1] = self._ncols
127
self._matrix_numpy = cnumpy.PyArray_SimpleNew(2, dims, self._numpy_dtypeint)
128
return
129
130
def __dealloc__(self):
131
""" Deallocate any memory that was initialized."""
132
return
133
134
def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.
135
return self._richcmp(right, op)
136
137
def __hash__(self):
138
"""
139
Hash this matrix if it's immutable.
140
141
EXAMPLES::
142
143
sage: A = matrix(RDF,3,range(1,10))
144
sage: hash(A)
145
Traceback (most recent call last):
146
...
147
TypeError: mutable matrices are unhashable
148
sage: A.set_immutable()
149
sage: hash(A)
150
88
151
sage: A = matrix(CDF,3,range(1,10))
152
sage: hash(A)
153
Traceback (most recent call last):
154
...
155
TypeError: mutable matrices are unhashable
156
sage: A.set_immutable()
157
sage: hash(A)
158
88
159
"""
160
return self._hash()
161
162
def LU_valid(self):
163
r"""
164
Returns ``True`` if the LU form of this matrix has
165
already been computed.
166
167
EXAMPLES::
168
169
sage: A = random_matrix(RDF,3) ; A.LU_valid()
170
False
171
sage: P, L, U = A.LU()
172
sage: A.LU_valid()
173
True
174
"""
175
return self.fetch('PLU_factors') is not None
176
177
def __init__(self, parent, entries, copy, coerce):
178
"""
179
Fill the matrix with entries.
180
181
The numpy matrix must have already been allocated.
182
183
EXAMPLES::
184
185
sage: matrix(RDF,3,range(9))
186
[0.0 1.0 2.0]
187
[3.0 4.0 5.0]
188
[6.0 7.0 8.0]
189
sage: matrix(CDF,3,3,2)
190
[2.0 0.0 0.0]
191
[0.0 2.0 0.0]
192
[0.0 0.0 2.0]
193
194
TESTS::
195
196
sage: matrix(RDF,3,0)
197
[]
198
sage: matrix(RDF,3,3,0)
199
[0.0 0.0 0.0]
200
[0.0 0.0 0.0]
201
[0.0 0.0 0.0]
202
sage: matrix(RDF,3,3,1)
203
[1.0 0.0 0.0]
204
[0.0 1.0 0.0]
205
[0.0 0.0 1.0]
206
sage: matrix(RDF,3,3,2)
207
[2.0 0.0 0.0]
208
[0.0 2.0 0.0]
209
[0.0 0.0 2.0]
210
sage: matrix(CDF,3,0)
211
[]
212
sage: matrix(CDF,3,3,0)
213
[0.0 0.0 0.0]
214
[0.0 0.0 0.0]
215
[0.0 0.0 0.0]
216
sage: matrix(CDF,3,3,1)
217
[1.0 0.0 0.0]
218
[0.0 1.0 0.0]
219
[0.0 0.0 1.0]
220
sage: matrix(CDF,3,3,range(9))
221
[0.0 1.0 2.0]
222
[3.0 4.0 5.0]
223
[6.0 7.0 8.0]
224
sage: matrix(CDF,2,2,[CDF(1+I)*j for j in range(4)])
225
[ 0.0 1.0 + 1.0*I]
226
[2.0 + 2.0*I 3.0 + 3.0*I]
227
"""
228
cdef Py_ssize_t i,j
229
cdef cnumpy.npy_intp dims[2]
230
dims[0] = self._nrows
231
dims[1] = self._ncols
232
if isinstance(entries,(tuple, list)):
233
if len(entries)!=self._nrows*self._ncols:
234
raise TypeError("entries has wrong length")
235
236
if coerce:
237
for i from 0<=i<self._nrows:
238
for j from 0<=j<self._ncols:
239
self.set_unsafe(i,j,self._python_dtype(entries[i*self._ncols+j]))
240
else:
241
for i from 0<=i<self._nrows:
242
for j from 0<=j<self._ncols:
243
self.set_unsafe(i,j,entries[i*self._ncols+j])
244
245
else:
246
cnumpy.PyArray_FILLWBYTE(self._matrix_numpy, 0)
247
248
if entries is None:
249
z = self._python_dtype(0.0)
250
else:
251
try:
252
z = self._python_dtype(entries)
253
except TypeError:
254
raise TypeError("entries must be coercible to a list or float")
255
if z != 0:
256
if self._nrows != self._ncols:
257
raise TypeError("scalar matrix must be square")
258
for i from 0<=i<self._ncols:
259
self.set_unsafe(i,i,z)
260
261
262
263
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object value):
264
"""
265
Set the (i,j) entry to value without any bounds checking,
266
mutability checking, etc.
267
"""
268
# We assume that Py_ssize_t is the same as cnumpy.npy_intp
269
270
# We must patch the ndarrayobject.h file so that the SETITEM
271
# macro does not have a semicolon at the end for this to work.
272
# Cython wraps the macro in a function that converts the
273
# returned int to a python object, which leads to compilation
274
# errors because after preprocessing you get something that
275
# looks like "););". This is bug
276
# http://scipy.org/scipy/numpy/ticket/918
277
278
# We call the self._python_dtype function on the value since
279
# numpy does not know how to deal with complex numbers other
280
# than the built-in complex number type.
281
cdef int status
282
status = cnumpy.PyArray_SETITEM(self._matrix_numpy,
283
cnumpy.PyArray_GETPTR2(self._matrix_numpy, i, j),
284
self._python_dtype(value))
285
#TODO: Throw an error if status == -1
286
287
288
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
289
"""
290
Get the (i,j) entry without any bounds checking, etc.
291
"""
292
# We assume that Py_ssize_t is the same as cnumpy.npy_intp
293
return self._sage_dtype(cnumpy.PyArray_GETITEM(self._matrix_numpy,
294
cnumpy.PyArray_GETPTR2(self._matrix_numpy, i, j)))
295
296
cdef Matrix_double_dense _new(self, int nrows=-1, int ncols=-1):
297
"""
298
Return a new uninitialized matrix with same parent as self.
299
300
INPUT:
301
302
nrows -- (default self._nrows) number of rows in returned matrix
303
ncols -- (default self._ncols) number of columns in returned matrix
304
305
"""
306
cdef Matrix_double_dense m
307
if nrows == -1:
308
nrows = self._nrows
309
if ncols == -1:
310
ncols = self._ncols
311
parent = self.matrix_space(nrows, ncols)
312
m = self.__class__.__new__(self.__class__,parent,None,None,None)
313
return m
314
315
316
317
318
########################################################################
319
# LEVEL 2 functionality
320
# * def _pickle
321
# * def _unpickle
322
cpdef ModuleElement _add_(self, ModuleElement right):
323
"""
324
Add two matrices together.
325
326
EXAMPLES::
327
328
sage: A = matrix(RDF,3,range(1,10))
329
sage: A+A
330
[ 2.0 4.0 6.0]
331
[ 8.0 10.0 12.0]
332
[14.0 16.0 18.0]
333
"""
334
if self._nrows == 0 or self._ncols == 0:
335
return self.__copy__()
336
337
cdef Matrix_double_dense M, _right, _left
338
_right = right
339
_left = self
340
341
M = self._new()
342
M._matrix_numpy = _left._matrix_numpy + _right._matrix_numpy
343
return M
344
345
cpdef ModuleElement _sub_(self, ModuleElement right):
346
"""
347
Return self - right
348
349
350
EXAMPLES::
351
352
sage: A = matrix(RDF,3,range(1,10))
353
sage: (A-A).is_zero()
354
True
355
"""
356
if self._nrows == 0 or self._ncols == 0:
357
return self.__copy__()
358
359
cdef Matrix_double_dense M,_right,_left
360
_right = right
361
_left = self
362
363
M = self._new()
364
M._matrix_numpy = _left._matrix_numpy - _right._matrix_numpy
365
return M
366
367
def __neg__(self):
368
"""
369
Negate this matrix
370
371
EXAMPLES::
372
373
sage: A = matrix(RDF,3,range(1,10))
374
sage: -A
375
[-1.0 -2.0 -3.0]
376
[-4.0 -5.0 -6.0]
377
[-7.0 -8.0 -9.0]
378
sage: B = -A ; (A+B).is_zero()
379
True
380
"""
381
if self._nrows == 0 or self._ncols == 0:
382
return self.__copy__()
383
384
cdef Matrix_double_dense M
385
M = self._new()
386
M._matrix_numpy = -self._matrix_numpy
387
return M
388
389
390
# * cdef _cmp_c_impl
391
# x * __copy__
392
# * _list -- list of underlying elements (need not be a copy)
393
# * _dict -- sparse dictionary of underlying elements (need not be a copy)
394
########################################################################
395
# def _pickle(self): #unsure how to implement
396
# def _unpickle(self, data, int version): # use version >= 0 #unsure how to implement
397
######################################################################
398
cdef sage.structure.element.Matrix _matrix_times_matrix_(self, sage.structure.element.Matrix right):
399
"""
400
Multiply self*right as matrices.
401
402
EXAMPLES::
403
404
sage: A = matrix(RDF,3,range(1,10))
405
sage: B = matrix(RDF,3,range(1,13))
406
sage: A*B
407
[ 38.0 44.0 50.0 56.0]
408
[ 83.0 98.0 113.0 128.0]
409
[128.0 152.0 176.0 200.0]
410
"""
411
if self._ncols!=right._nrows:
412
raise IndexError("Number of columns of self must equal number of rows of right")
413
414
if self._nrows == 0 or self._ncols == 0 or right._nrows == 0 or right._ncols == 0:
415
return self.matrix_space(self._nrows, right._ncols).zero_matrix()
416
417
cdef Matrix_double_dense M,_right,_left
418
M = self._new(self._nrows, right._ncols)
419
_right = right
420
_left = self
421
global numpy
422
if numpy is None:
423
import numpy
424
425
M._matrix_numpy = numpy.dot(_left._matrix_numpy, _right._matrix_numpy)
426
return M
427
428
# cdef int _cmp_c_impl(self, Matrix right) except -2:
429
def __invert__(self):
430
"""
431
Invert this matrix.
432
433
EXAMPLES::
434
435
sage: A = Matrix(RDF, [[10, 0], [0, 100]])
436
sage: (~A).det()
437
0.001
438
439
sage: A = matrix(RDF,3,[2,3,5,7,8,9,11,13,17]);A
440
[ 2.0 3.0 5.0]
441
[ 7.0 8.0 9.0]
442
[11.0 13.0 17.0]
443
sage: ~A
444
[ -2.71428571429 -2.0 1.85714285714]
445
[ 2.85714285714 3.0 -2.42857142857]
446
[-0.428571428571 -1.0 0.714285714286]
447
448
Note that if this matrix is (nearly) singular, finding
449
its inverse will not help much and will give slightly different
450
answers on similar platforms depending on the hardware
451
and tuning options given to ATLAS::
452
453
sage: A = matrix(RDF,3,range(1,10));A
454
[1.0 2.0 3.0]
455
[4.0 5.0 6.0]
456
[7.0 8.0 9.0]
457
458
sage: A.determinant() < 10e-12
459
True
460
461
462
TESTS::
463
464
sage: ~Matrix(RDF, 0,0)
465
[]
466
sage: ~Matrix(RDF, 0,3)
467
Traceback (most recent call last):
468
...
469
ArithmeticError: self must be a square matrix
470
"""
471
# see trac ticket 4502 --- there is an issue with the "#random" pragma that needs to be fixed
472
# as for the mathematical side, scipy v0.7 is expected to fix the invertibility failures
473
#
474
# sage: A = Matrix(RDF, [[1, 0], [0, 0]])
475
# sage: A.inverse().det() # random - on some computers, this will be invertible due to numerical error.
476
# Traceback (most recent call last):
477
# ...
478
# LinAlgError: singular matrix
479
# sage: A = matrix(RDF,3,range(1,10));A
480
# [1.0 2.0 3.0]
481
# [4.0 5.0 6.0]
482
# [7.0 8.0 9.0]
483
#
484
# sage: A.determinant() < 10e-12
485
# True
486
# sage: ~A # random - on some computers, this will be invertible due to numerical error.
487
# Traceback (most recent call last):
488
# ...
489
# ZeroDivisionError: singular matrix
490
#
491
if self._nrows != self._ncols:
492
raise ArithmeticError("self must be a square matrix")
493
if self._nrows == 0 and self._ncols == 0:
494
return self.__copy__()
495
496
# Maybe we should cache the (P)LU decomposition and use scipy.lu_solve?
497
cdef Matrix_double_dense M
498
M = self._new()
499
global scipy
500
if scipy is None:
501
import scipy
502
import scipy.linalg
503
from numpy.linalg import LinAlgError
504
try: ## Standard error reporting for Sage.
505
M._matrix_numpy = scipy.linalg.inv(self._matrix_numpy)
506
except LinAlgError:
507
raise ZeroDivisionError("input matrix must be nonsingular")
508
return M
509
510
def __copy__(self):
511
r"""
512
Returns a new copy of this matrix.
513
514
EXAMPLES::
515
516
sage: a = matrix(RDF,1,3, [1,2,-3])
517
sage: a
518
[ 1.0 2.0 -3.0]
519
sage: b = a.__copy__()
520
sage: b
521
[ 1.0 2.0 -3.0]
522
sage: b is a
523
False
524
sage: b == a
525
True
526
sage: b[0,0] = 3
527
sage: a[0,0] # note that a hasn't changed
528
1.0
529
530
::
531
532
sage: copy(MatrixSpace(RDF,0,0,sparse=False).zero_matrix())
533
[]
534
"""
535
if self._nrows == 0 or self._ncols == 0:
536
# Create a brand new empy matrix. This is needed to prevent a
537
# recursive loop: a copy of zero_matrix is asked otherwise.
538
return self.__class__(self.parent(), [], self._nrows, self._ncols)
539
540
cdef Matrix_double_dense A
541
A = self._new(self._nrows, self._ncols)
542
A._matrix_numpy = self._matrix_numpy.copy()
543
if self._subdivisions is not None:
544
A.subdivide(*self.subdivisions())
545
return A
546
547
548
# def _list(self):
549
# def _dict(self):
550
551
552
########################################################################
553
# LEVEL 3 functionality (Optional)
554
# * cdef _sub_
555
# * __deepcopy__
556
# * __invert__
557
# * Matrix windows -- only if you need strassen for that base
558
# * Other functions (list them here):
559
#
560
# compute_LU(self)
561
#
562
########################################################################
563
564
565
def condition(self, p='frob'):
566
r"""
567
Returns the condition number of a square nonsingular matrix.
568
569
Roughly speaking, this is a measure of how sensitive
570
the matrix is to round-off errors in numerical computations.
571
The minimum possible value is 1.0, and larger numbers indicate
572
greater sensitivity.
573
574
INPUT:
575
576
- ``p`` - default: 'frob' - controls which norm is used
577
to compute the condition number, allowable values are
578
'frob' (for the Frobenius norm), integers -2, -1, 1, 2,
579
positive and negative infinity. See output discussion
580
for specifics.
581
582
OUTPUT:
583
584
The condition number of a matrix is the product of a norm
585
of the matrix times the norm of the inverse of the matrix.
586
This requires that the matrix be square and invertible
587
(nonsingular, full rank).
588
589
Returned value is a double precision floating point value
590
in ``RDF``, or ``Infinity``. Row and column sums described below are
591
sums of the absolute values of the entries, where the
592
absolute value of the complex number `a+bi` is `\sqrt{a^2+b^2}`.
593
Singular values are the "diagonal" entries of the "S" matrix in
594
the singular value decomposition.
595
596
- ``p = 'frob'``: the default norm employed in computing
597
the condition number, the Frobenius norm, which for a
598
matrix `A=(a_{ij})` computes
599
600
.. math::
601
602
\left(\sum_{i,j}\left\lvert{a_{i,j}}\right\rvert^2\right)^{1/2}
603
604
- ``p = 'sv'``: the quotient of the maximal and minimal singular value.
605
- ``p = Infinity`` or ``p = oo``: the maximum row sum.
606
- ``p = -Infinity`` or ``p = -oo``: the minimum column sum.
607
- ``p = 1``: the maximum column sum.
608
- ``p = -1``: the minimum column sum.
609
- ``p = 2``: the 2-norm, equal to the maximum singular value.
610
- ``p = -2``: the minimum singular value.
611
612
ALGORITHM:
613
614
Computation is performed by the ``cond()`` function of
615
the SciPy/NumPy library.
616
617
EXAMPLES:
618
619
First over the reals. ::
620
621
sage: A = matrix(RDF, 4, [(1/4)*x^3 for x in range(16)]); A
622
[ 0.0 0.25 2.0 6.75]
623
[ 16.0 31.25 54.0 85.75]
624
[ 128.0 182.25 250.0 332.75]
625
[ 432.0 549.25 686.0 843.75]
626
sage: A.condition()
627
9923.88955...
628
sage: A.condition(p='frob')
629
9923.88955...
630
sage: A.condition(p=Infinity)
631
22738.5
632
sage: A.condition(p=-Infinity)
633
17.5
634
sage: A.condition(p=1)
635
12139.21...
636
sage: A.condition(p=-1)
637
550.0
638
sage: A.condition(p=2)
639
9897.8088...
640
sage: A.condition(p=-2)
641
0.000101032462...
642
643
And over the complex numbers. ::
644
645
sage: B = matrix(CDF, 3, [x + x^2*I for x in range(9)]); B
646
[ 0.0 1.0 + 1.0*I 2.0 + 4.0*I]
647
[ 3.0 + 9.0*I 4.0 + 16.0*I 5.0 + 25.0*I]
648
[6.0 + 36.0*I 7.0 + 49.0*I 8.0 + 64.0*I]
649
sage: B.condition()
650
203.851798...
651
sage: B.condition(p='frob')
652
203.851798...
653
sage: B.condition(p=Infinity)
654
369.55630...
655
sage: B.condition(p=-Infinity)
656
5.46112969...
657
sage: B.condition(p=1)
658
289.251481...
659
sage: B.condition(p=-1)
660
20.4566639...
661
sage: B.condition(p=2)
662
202.653543...
663
sage: B.condition(p=-2)
664
0.00493453005...
665
666
Hilbert matrices are famously ill-conditioned, while
667
an identity matrix can hit the minimum with the right norm. ::
668
669
sage: A = matrix(RDF, 10, [1/(i+j+1) for i in range(10) for j in range(10)])
670
sage: A.condition()
671
1.633...e+13
672
sage: id = identity_matrix(CDF, 10)
673
sage: id.condition(p=1)
674
1.0
675
676
Return values are in `RDF`. ::
677
678
sage: A = matrix(CDF, 2, range(1,5))
679
sage: A.condition() in RDF
680
True
681
682
Rectangular and singular matrices raise errors if p is not 'sv'. ::
683
684
sage: A = matrix(RDF, 2, 3, range(6))
685
sage: A.condition()
686
Traceback (most recent call last):
687
...
688
TypeError: matrix must be square if p is not 'sv', not 2 x 3
689
690
sage: A.condition('sv')
691
7.34...
692
693
sage: A = matrix(QQ, 5, range(25))
694
sage: A.is_singular()
695
True
696
sage: B = A.change_ring(CDF)
697
sage: B.condition()
698
Traceback (most recent call last):
699
...
700
LinAlgError: Singular matrix
701
702
Improper values of ``p`` are caught. ::
703
704
sage: A = matrix(CDF, 2, range(1,5))
705
sage: A.condition(p='bogus')
706
Traceback (most recent call last):
707
...
708
ValueError: condition number 'p' must be +/- infinity, 'frob', 'sv' or an integer, not bogus
709
sage: A.condition(p=632)
710
Traceback (most recent call last):
711
...
712
ValueError: condition number integer values of 'p' must be -2, -1, 1 or 2, not 632
713
714
TESTS:
715
716
Some condition numbers, first by the definition which also exercises
717
:meth:`norm`, then by this method. ::
718
719
sage: A = matrix(CDF, [[1,2,4],[5,3,9],[7,8,6]])
720
sage: c = A.norm(2)*A.inverse().norm(2)
721
sage: d = A.condition(2)
722
sage: abs(c-d) < 1.0e-12
723
True
724
sage: c = A.norm(1)*A.inverse().norm(1)
725
sage: d = A.condition(1)
726
sage: abs(c-d) < 1.0e-12
727
True
728
"""
729
if not self.is_square() and p != 'sv':
730
raise TypeError("matrix must be square if p is not 'sv', not %s x %s" % (self.nrows(), self.ncols()))
731
global numpy
732
if numpy is None:
733
import numpy
734
import sage.rings.infinity
735
import sage.rings.integer
736
import sage.rings.real_double
737
if p == sage.rings.infinity.Infinity:
738
p = numpy.inf
739
elif p == -sage.rings.infinity.Infinity:
740
p = -numpy.inf
741
elif p == 'frob':
742
p = 'fro'
743
elif p == 'sv' :
744
p = None
745
else:
746
try:
747
p = sage.rings.integer.Integer(p)
748
except TypeError:
749
raise ValueError("condition number 'p' must be +/- infinity, 'frob', 'sv' or an integer, not %s" % p)
750
if p not in [-2,-1,1,2]:
751
raise ValueError("condition number integer values of 'p' must be -2, -1, 1 or 2, not %s" % p)
752
# may raise a LinAlgError if matrix is singular
753
c = numpy.linalg.cond(self._matrix_numpy, p=p)
754
if c == numpy.inf:
755
return sage.rings.infinity.Infinity
756
else:
757
return sage.rings.real_double.RDF(c)
758
759
def norm(self, p=None):
760
r"""
761
Returns the norm of the matrix.
762
763
INPUT:
764
765
- ``p`` - default: 'frob' - controls which norm is computed,
766
allowable values are 'frob' (for the Frobenius norm),
767
integers -2, -1, 1, 2, positive and negative infinity. See
768
output discussion for specifics. The default (``p='frob'``)
769
is deprecated and will change to a default of ``p=2`` soon.
770
771
OUTPUT:
772
773
Returned value is a double precision floating point value
774
in ``RDF``. Row and column sums described below are
775
sums of the absolute values of the entries, where the
776
absolute value of the complex number `a+bi` is `\sqrt{a^2+b^2}`.
777
Singular values are the "diagonal" entries of the "S" matrix in
778
the singular value decomposition.
779
780
- ``p = 'frob'``: the Frobenius norm, which for
781
a matrix `A=(a_{ij})` computes
782
783
.. math::
784
785
\left(\sum_{i,j}\left\lvert{a_{i,j}}\right\rvert^2\right)^{1/2}
786
787
- ``p = Infinity`` or ``p = oo``: the maximum row sum.
788
- ``p = -Infinity`` or ``p = -oo``: the minimum column sum.
789
- ``p = 1``: the maximum column sum.
790
- ``p = -1``: the minimum column sum.
791
- ``p = 2``: the induced 2-norm, equal to the maximum singular value.
792
- ``p = -2``: the minimum singular value.
793
794
ALGORITHM:
795
796
Computation is performed by the ``norm()`` function of
797
the SciPy/NumPy library.
798
799
EXAMPLES:
800
801
First over the reals. ::
802
803
sage: A = matrix(RDF, 3, range(-3, 6)); A
804
[-3.0 -2.0 -1.0]
805
[ 0.0 1.0 2.0]
806
[ 3.0 4.0 5.0]
807
sage: A.norm()
808
doctest:...: DeprecationWarning: The default norm will be changing from p='frob' to p=2. Use p='frob' explicitly to continue calculating the Frobenius norm.
809
See http://trac.sagemath.org/13643 for details.
810
8.30662386...
811
sage: A.norm(p='frob')
812
8.30662386...
813
sage: A.norm(p=Infinity)
814
12.0
815
sage: A.norm(p=-Infinity)
816
3.0
817
sage: A.norm(p=1)
818
8.0
819
sage: A.norm(p=-1)
820
6.0
821
sage: A.norm(p=2)
822
7.99575670...
823
sage: A.norm(p=-2) < 10^-15
824
True
825
826
And over the complex numbers. ::
827
828
sage: B = matrix(CDF, 2, [[1+I, 2+3*I],[3+4*I,3*I]]); B
829
[1.0 + 1.0*I 2.0 + 3.0*I]
830
[3.0 + 4.0*I 3.0*I]
831
sage: B.norm()
832
7.0
833
sage: B.norm(p='frob')
834
7.0
835
sage: B.norm(p=Infinity)
836
8.0
837
sage: B.norm(p=-Infinity)
838
5.01976483...
839
sage: B.norm(p=1)
840
6.60555127...
841
sage: B.norm(p=-1)
842
6.41421356...
843
sage: B.norm(p=2)
844
6.66189877...
845
sage: B.norm(p=-2)
846
2.14921023...
847
848
Since it is invariant under unitary multiplication, the
849
Frobenius norm is equal to the square root of the sum of
850
squares of the singular values. ::
851
852
sage: A = matrix(RDF, 5, range(1,26))
853
sage: f = A.norm(p='frob')
854
sage: U, S, V = A.SVD()
855
sage: s = sqrt(sum([S[i,i]^2 for i in range(5)]))
856
sage: abs(f-s) < 1.0e-12
857
True
858
859
Return values are in `RDF`. ::
860
861
sage: A = matrix(CDF, 2, range(4))
862
sage: A.norm() in RDF
863
True
864
865
Improper values of ``p`` are caught. ::
866
867
sage: A.norm(p='bogus')
868
Traceback (most recent call last):
869
...
870
ValueError: matrix norm 'p' must be +/- infinity, 'frob' or an integer, not bogus
871
sage: A.norm(p=632)
872
Traceback (most recent call last):
873
...
874
ValueError: matrix norm integer values of 'p' must be -2, -1, 1 or 2, not 632
875
"""
876
global numpy
877
if numpy is None:
878
import numpy
879
880
if p is None:
881
from sage.misc.superseded import deprecation
882
msg = "The default norm will be changing from p='frob' to p=2. Use p='frob' explicitly to continue calculating the Frobenius norm."
883
deprecation(13643, msg)
884
# change to default of p=2 when deprecation period has elapsed
885
p='frob'
886
887
import sage.rings.infinity
888
import sage.rings.integer
889
import sage.rings.real_double
890
if p == sage.rings.infinity.Infinity:
891
p = numpy.inf
892
elif p == -sage.rings.infinity.Infinity:
893
p = -numpy.inf
894
elif p == 'frob':
895
p = 'fro'
896
else:
897
try:
898
p = sage.rings.integer.Integer(p)
899
except TypeError:
900
raise ValueError("matrix norm 'p' must be +/- infinity, 'frob' or an integer, not %s" % p)
901
if not p in [-2,-1,1,2]:
902
raise ValueError("matrix norm integer values of 'p' must be -2, -1, 1 or 2, not %s" % p)
903
return sage.rings.real_double.RDF(numpy.linalg.norm(self._matrix_numpy, ord=p))
904
905
def singular_values(self, eps=None):
906
r"""
907
Returns a sorted list of the singular values of the matrix.
908
909
INPUT:
910
911
- ``eps`` - default: ``None`` - the largest number which
912
will be considered to be zero. May also be set to the
913
string 'auto'. See the discussion below.
914
915
OUTPUT:
916
917
A sorted list of the singular values of the matrix, which are the
918
diagonal entries of the "S" matrix in the SVD decomposition. As such,
919
the values are real and are returned as elements of ``RDF``. The
920
list is sorted with larger values first, and since theory predicts
921
these values are always positive, for a rank-deficient matrix the
922
list should end in zeros (but in practice may not). The length of
923
the list is the minimum of the row count and column count for the
924
matrix.
925
926
The number of non-zero singular values will be the rank of the
927
matrix. However, as a numerical matrix, it is impossible to
928
control the difference between zero entries and very small
929
non-zero entries. As an informed consumer it is up to you
930
to use the output responsibly. We will do our best, and give
931
you the tools to work with the output, but we cannot
932
give you a guarantee.
933
934
With ``eps`` set to ``None`` you will get the raw singular
935
values and can manage them as you see fit. You may also set
936
``eps`` to any positive floating point value you wish. If you
937
set ``eps`` to 'auto' this routine will compute a reasonable
938
cutoff value, based on the size of the matrix, the largest
939
singular value and the smallest nonzero value representable
940
by the 53-bit precision values used. See the discussion
941
at page 268 of [WATKINS]_.
942
943
See the examples for a way to use the "verbose" facility
944
to easily watch the zero cutoffs in action.
945
946
ALGORITHM:
947
948
The singular values come from the SVD decomposition
949
computed by SciPy/NumPy.
950
951
EXAMPLES:
952
953
Singular values close to zero have trailing digits that may vary
954
on different hardware. For exact matrices, the number of non-zero
955
singular values will equal the rank of the matrix. So for some of
956
the doctests we round the small singular values that ideally would
957
be zero, to control the variability across hardware.
958
959
This matrix has a determinant of one. A chain of two or
960
three theorems implies the product of the singular values
961
must also be one. ::
962
963
sage: A = matrix(QQ, [[ 1, 0, 0, 0, 0, 1, 3],
964
... [-2, 1, 1, -2, 0, -4, 0],
965
... [ 1, 0, 1, -4, -6, -3, 7],
966
... [-2, 2, 1, 1, 7, 1, -1],
967
... [-1, 0, -1, 5, 8, 4, -6],
968
... [ 4, -2, -2, 1, -3, 0, 8],
969
... [-2, 1, 0, 2, 7, 3, -4]])
970
sage: A.determinant()
971
1
972
sage: B = A.change_ring(RDF)
973
sage: sv = B.singular_values(); sv
974
[20.5239806589, 8.48683702854, 5.86168134845, 2.44291658993,
975
0.583197014472, 0.269332872866, 0.00255244880761]
976
sage: prod(sv)
977
1.0
978
979
An exact matrix that is obviously not of full rank, and then
980
a computation of the singular values after conversion
981
to an approximate matrix. ::
982
983
sage: A = matrix(QQ, [[1/3, 2/3, 11/3],
984
... [2/3, 1/3, 7/3],
985
... [2/3, 5/3, 27/3]])
986
sage: A.rank()
987
2
988
sage: B = A.change_ring(CDF)
989
sage: sv = B.singular_values()
990
sage: sv[0:2]
991
[10.1973039..., 0.487045871...]
992
sage: sv[2] < 1e-14
993
True
994
995
A matrix of rank 3 over the complex numbers. ::
996
997
sage: A = matrix(CDF, [[46*I - 28, -47*I - 50, 21*I + 51, -62*I - 782, 13*I + 22],
998
... [35*I - 20, -32*I - 46, 18*I + 43, -57*I - 670, 7*I + 3],
999
... [22*I - 13, -23*I - 23, 9*I + 24, -26*I - 347, 7*I + 13],
1000
... [-44*I + 23, 41*I + 57, -19*I - 54, 60*I + 757, -11*I - 9],
1001
... [30*I - 18, -30*I - 34, 14*I + 34, -42*I - 522, 8*I + 12]])
1002
sage: sv = A.singular_values()
1003
sage: sv[0:3]
1004
[1440.733666, 18.4044034134, 6.83970779714]
1005
sage: (10^-15 < sv[3]) and (sv[3] < 10^-13)
1006
True
1007
sage: (10^-16 < sv[4]) and (sv[4] < 10^-14)
1008
True
1009
1010
A full-rank matrix that is ill-conditioned. We use this to
1011
illustrate ways of using the various possibilities for ``eps``,
1012
including one that is ill-advised. Notice that the automatically
1013
computed cutoff gets this (difficult) example slightly wrong.
1014
This illustrates the impossibility of any automated process always
1015
getting this right. Use with caution and judgement. ::
1016
1017
sage: entries = [1/(i+j+1) for i in range(12) for j in range(12)]
1018
sage: B = matrix(QQ, 12, 12, entries)
1019
sage: B.rank()
1020
12
1021
sage: A = B.change_ring(RDF)
1022
sage: A.condition() > 1.6e16 or A.condition()
1023
True
1024
1025
sage: A.singular_values(eps=None) # abs tol 1e-16
1026
[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]
1027
1028
sage: A.singular_values(eps='auto') # abs tol 1e-16
1029
[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]
1030
1031
sage: A.singular_values(eps=1e-4)
1032
[1.79537205956, 0.380275245955, 0.0447385487522, 0.00372231223789, 0.000233089089022, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
1033
1034
With Sage's "verbose" facility, you can compactly see the cutoff
1035
at work. In any application of this routine, or those that build upon
1036
it, it would be a good idea to conduct this exercise on samples.
1037
We also test here that all the values are returned in `RDF` since
1038
singular values are always real. ::
1039
1040
sage: A = matrix(CDF, 4, range(16))
1041
sage: set_verbose(1)
1042
sage: sv = A.singular_values(eps='auto'); sv
1043
verbose 1 (<module>) singular values,
1044
smallest-non-zero:cutoff:largest-zero,
1045
2.2766...:6.2421...e-14:...
1046
[35.139963659, 2.27661020871, 0.0, 0.0]
1047
sage: set_verbose(0)
1048
1049
sage: all([s in RDF for s in sv])
1050
True
1051
1052
TESTS:
1053
1054
Bogus values of the ``eps`` keyword will be caught. ::
1055
1056
sage: A.singular_values(eps='junk')
1057
Traceback (most recent call last):
1058
...
1059
ValueError: could not convert string to float: junk
1060
1061
REFERENCES:
1062
1063
.. [WATKINS] Watkins, David S. Fundamentals of Matrix Computations,
1064
Third Edition. Wiley, Hoboken, New Jersey, 2010.
1065
1066
AUTHOR:
1067
1068
- Rob Beezer - (2011-02-18)
1069
"""
1070
from sage.misc.misc import verbose
1071
from sage.rings.real_double import RDF
1072
global scipy
1073
# get SVD decomposition, which is a cached quantity
1074
_, S, _ = self.SVD()
1075
diag = min(self._nrows, self._ncols)
1076
sv = [RDF(S[i,i]) for i in range(diag)]
1077
# no cutoff, send raw data back
1078
if eps is None:
1079
verbose("singular values, no zero cutoff specified", level=1)
1080
return sv
1081
# set cutoff as RDF element
1082
if eps == 'auto':
1083
if scipy is None: import scipy
1084
eps = 2*max(self._nrows, self._ncols)*scipy.finfo(float).eps*sv[0]
1085
eps = RDF(eps)
1086
# locate non-zero entries
1087
rank = 0
1088
while rank < diag and sv[rank] > eps:
1089
rank = rank + 1
1090
# capture info for watching zero cutoff behavior at verbose level 1
1091
if rank == 0:
1092
small_nonzero = None
1093
else:
1094
small_nonzero = sv[rank-1]
1095
if rank < diag:
1096
large_zero = sv[rank]
1097
else:
1098
large_zero = None
1099
# convert small values to zero, then done
1100
for i in range(rank, diag):
1101
sv[i] = RDF(0)
1102
verbose("singular values, smallest-non-zero:cutoff:largest-zero, %s:%s:%s" % (small_nonzero, eps, large_zero), level=1)
1103
return sv
1104
1105
def LU(self):
1106
r"""
1107
Returns a decomposition of the (row-permuted) matrix as a product of
1108
a lower-triangular matrix ("L") and an upper-triangular matrix ("U").
1109
1110
OUTPUT:
1111
1112
For an `m\times n` matrix ``A`` this method returns a triple of
1113
immutable matrices ``P, L, U`` such that
1114
1115
- ``P*A = L*U``
1116
- ``P`` is a square permutation matrix, of size `m\times m`,
1117
so is all zeroes, but with exactly a single one in each
1118
row and each column.
1119
- ``L`` is lower-triangular, square of size `m\times m`,
1120
with every diagonal entry equal to one.
1121
- ``U`` is upper-triangular with size `m\times n`, i.e.
1122
entries below the "diagonal" are all zero.
1123
1124
The computed decomposition is cached and returned on
1125
subsequent calls, thus requiring the results to be immutable.
1126
1127
Effectively, ``P`` permutes the rows of ``A``. Then ``L``
1128
can be viewed as a sequence of row operations on this matrix,
1129
where each operation is adding a multiple of a row to a
1130
subsequent row. There is no scaling (thus 1's on the diagonal
1131
of ``L``) and no row-swapping (``P`` does that). As a result
1132
``U`` is close to being the result of Gaussian-elimination.
1133
However, round-off errors can make it hard to determine
1134
the zero entries of ``U``.
1135
1136
.. NOTE::
1137
1138
Sometimes this decomposition is written as ``A=P*L*U``,
1139
where ``P`` represents the inverse permutation and is
1140
the matrix inverse of the ``P`` returned by this method.
1141
The computation of this matrix inverse can be accomplished
1142
quickly with just a transpose as the matrix is orthogonal/unitary.
1143
1144
EXAMPLES::
1145
1146
sage: m = matrix(RDF,4,range(16))
1147
sage: P,L,U = m.LU()
1148
sage: P*m
1149
[12.0 13.0 14.0 15.0]
1150
[ 0.0 1.0 2.0 3.0]
1151
[ 8.0 9.0 10.0 11.0]
1152
[ 4.0 5.0 6.0 7.0]
1153
sage: L*U
1154
[12.0 13.0 14.0 15.0]
1155
[ 0.0 1.0 2.0 3.0]
1156
[ 8.0 9.0 10.0 11.0]
1157
[ 4.0 5.0 6.0 7.0]
1158
1159
Trac 10839 made this routine available for rectangular matrices. ::
1160
1161
sage: A = matrix(RDF, 5, 6, range(30)); A
1162
[ 0.0 1.0 2.0 3.0 4.0 5.0]
1163
[ 6.0 7.0 8.0 9.0 10.0 11.0]
1164
[12.0 13.0 14.0 15.0 16.0 17.0]
1165
[18.0 19.0 20.0 21.0 22.0 23.0]
1166
[24.0 25.0 26.0 27.0 28.0 29.0]
1167
sage: P, L, U = A.LU()
1168
sage: P
1169
[0.0 0.0 0.0 0.0 1.0]
1170
[1.0 0.0 0.0 0.0 0.0]
1171
[0.0 0.0 1.0 0.0 0.0]
1172
[0.0 0.0 0.0 1.0 0.0]
1173
[0.0 1.0 0.0 0.0 0.0]
1174
sage: L.zero_at(0) # Use zero_at(0) to get rid of signed zeros
1175
[ 1.0 0.0 0.0 0.0 0.0]
1176
[ 0.0 1.0 0.0 0.0 0.0]
1177
[ 0.5 0.5 1.0 0.0 0.0]
1178
[0.75 0.25 0.0 1.0 0.0]
1179
[0.25 0.75 0.0 0.0 1.0]
1180
sage: U.zero_at(0) # Use zero_at(0) to get rid of signed zeros
1181
[24.0 25.0 26.0 27.0 28.0 29.0]
1182
[ 0.0 1.0 2.0 3.0 4.0 5.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]
1185
[ 0.0 0.0 0.0 0.0 0.0 0.0]
1186
sage: P*A-L*U
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]
1191
[0.0 0.0 0.0 0.0 0.0 0.0]
1192
sage: P.transpose()*L*U
1193
[ 0.0 1.0 2.0 3.0 4.0 5.0]
1194
[ 6.0 7.0 8.0 9.0 10.0 11.0]
1195
[12.0 13.0 14.0 15.0 16.0 17.0]
1196
[18.0 19.0 20.0 21.0 22.0 23.0]
1197
[24.0 25.0 26.0 27.0 28.0 29.0]
1198
1199
Trivial cases return matrices of the right size and
1200
characteristics. ::
1201
1202
sage: A = matrix(RDF, 5, 0, entries=0)
1203
sage: P, L, U = A.LU()
1204
sage: P.parent()
1205
Full MatrixSpace of 5 by 5 dense matrices over Real Double Field
1206
sage: L.parent()
1207
Full MatrixSpace of 5 by 5 dense matrices over Real Double Field
1208
sage: U.parent()
1209
Full MatrixSpace of 5 by 0 dense matrices over Real Double Field
1210
sage: P*A-L*U
1211
[]
1212
1213
The results are immutable since they are cached. ::
1214
1215
sage: P, L, U = matrix(RDF, 2, 2, range(4)).LU()
1216
sage: L[0,0] = 0
1217
Traceback (most recent call last):
1218
...
1219
ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
1220
sage: P[0,0] = 0
1221
Traceback (most recent call last):
1222
...
1223
ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
1224
sage: U[0,0] = 0
1225
Traceback (most recent call last):
1226
...
1227
ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
1228
"""
1229
global scipy, numpy
1230
cdef Matrix_double_dense P, L, U
1231
m = self._nrows
1232
n = self._ncols
1233
1234
# scipy fails on trivial cases
1235
if m == 0 or n == 0:
1236
P = self._new(m, m)
1237
for i in range(m):
1238
P[i,i]=1
1239
P.set_immutable()
1240
L = P
1241
U = self._new(m,n)
1242
U.set_immutable()
1243
return P, L, U
1244
1245
PLU = self.fetch('PLU_factors')
1246
if not PLU is None:
1247
return PLU
1248
if scipy is None:
1249
import scipy
1250
import scipy.linalg
1251
if numpy is None:
1252
import numpy
1253
PM, LM, UM = scipy.linalg.lu(self._matrix_numpy)
1254
# Numpy has a different convention than we had with GSL
1255
# So we invert (transpose) the P to match our prior behavior
1256
# TODO: It's an awful waste to store a huge matrix for P, which
1257
# is just a simple permutation, really.
1258
P = self._new(m, m)
1259
L = self._new(m, m)
1260
U = self._new(m, n)
1261
P._matrix_numpy = PM.T.copy()
1262
L._matrix_numpy = numpy.ascontiguousarray(LM)
1263
U._matrix_numpy = numpy.ascontiguousarray(UM)
1264
PLU = (P, L, U)
1265
for M in PLU:
1266
M.set_immutable()
1267
self.cache('PLU_factors', PLU)
1268
return PLU
1269
1270
def eigenspaces_left(self, var='a', algebraic_multiplicity=False):
1271
r"""
1272
Computes the left eigenspaces of a matrix of double precision
1273
real or complex numbers (i.e. RDF or CDF).
1274
1275
.. warning::
1276
1277
This method returns eigenspaces that are all of
1278
dimension one, since it is impossible to ascertain
1279
if the numerical results belong to the same eigenspace.
1280
So this is deprecated in favor of the eigenmatrix routines,
1281
such as :meth:`sage.matrix.matrix2.Matrix.eigenmatrix_right`.
1282
1283
INPUT:
1284
1285
- ``var`` - ignored for numerical matrices
1286
- ``algebraic_multiplicity`` - must be set to ``False``
1287
for numerical matrices, and will raise an error otherwise.
1288
1289
OUTPUT:
1290
1291
Return a list of pairs ``(e, V)`` where ``e`` is a (complex)
1292
eigenvalue and ``V`` is the associated left eigenspace as a
1293
vector space.
1294
1295
No attempt is made to determine if an eigenvalue has multiplicity
1296
greater than one, so all the eigenspaces returned have dimension one.
1297
1298
The SciPy routines used for these computations produce eigenvectors
1299
normalized to have length 1, but on different hardware they may vary
1300
by a sign. So for doctests we have normalized output by creating an
1301
eigenspace with a canonical basis.
1302
1303
EXAMPLES:
1304
1305
This first test simply raises the deprecation warning. ::
1306
1307
sage: A = identity_matrix(RDF, 2)
1308
sage: es = A.eigenspaces_left()
1309
doctest:...: DeprecationWarning: Eigenspaces of RDF/CDF matrices are
1310
deprecated as of Sage version 5.0,
1311
please use "eigenmatrix_left" instead
1312
See http://trac.sagemath.org/11603 for details.
1313
1314
::
1315
1316
sage: m = matrix(RDF, [[-5, 3, 2, 8],[10, 2, 4, -2],[-1, -10, -10, -17],[-2, 7, 6, 13]])
1317
sage: spectrum = m.eigenspaces_left()
1318
sage: spectrum[0][0]
1319
2.0
1320
sage: (RDF^4).subspace(spectrum[0][1].basis())
1321
Vector space of degree 4 and dimension 1 over Real Double Field
1322
Basis matrix:
1323
[1.0 1.0 1.0 1.0]
1324
1325
sage: e, V = spectrum[2]
1326
sage: v = V.basis()[0]
1327
sage: diff = (v*m).change_ring(CDF) - e*v
1328
sage: abs(abs(diff)) < 1e-14
1329
True
1330
1331
TESTS::
1332
1333
sage: m.eigenspaces_left(algebraic_multiplicity=True)
1334
Traceback (most recent call last):
1335
...
1336
ValueError: algebraic_multiplicity must be set to False for double precision matrices
1337
"""
1338
from sage.misc.superseded import deprecation
1339
msg = ('Eigenspaces of RDF/CDF matrices are deprecated as of ',
1340
'Sage version 5.0',
1341
', please use "eigenmatrix_left" instead')
1342
deprecation(11603, ''.join(msg))
1343
# For numerical values we leave decisions about
1344
# multiplicity to the calling routine
1345
if algebraic_multiplicity:
1346
raise ValueError("algebraic_multiplicity must be set to False for double precision matrices")
1347
spectrum = self.left_eigenvectors()
1348
pairs = []
1349
for evalue,evectors,_ in spectrum:
1350
evector = evectors[0]
1351
espace = evector.parent().span_of_basis([evector],check=False)
1352
pairs.append((evalue, espace))
1353
return pairs
1354
1355
left_eigenspaces = eigenspaces_left
1356
1357
def eigenspaces_right(self, var='a', algebraic_multiplicity=False):
1358
r"""
1359
Computes the right eigenspaces of a matrix of double precision
1360
real or complex numbers (i.e. RDF or CDF).
1361
1362
.. warning::
1363
1364
This method returns eigenspaces that are all of
1365
dimension one, since it is impossible to ascertain
1366
if the numerical results belong to the same eigenspace.
1367
So this is deprecated in favor of the eigenmatrix routines,
1368
such as :meth:`sage.matrix.matrix2.Matrix.eigenmatrix_right`.
1369
1370
INPUT:
1371
1372
- ``var`` - ignored for numerical matrices
1373
- ``algebraic_multiplicity`` - must be set to ``False``
1374
for numerical matrices, and will raise an error otherwise.
1375
1376
OUTPUT:
1377
1378
Return a list of pairs ``(e, V)`` where ``e`` is a (complex)
1379
eigenvalue and ``V`` is the associated right eigenspace as a
1380
vector space.
1381
1382
No attempt is made to determine if an eigenvalue has multiplicity
1383
greater than one, so all the eigenspaces returned have dimension one.
1384
1385
The SciPy routines used for these computations produce eigenvectors
1386
normalized to have length 1, but on different hardware they may vary
1387
by a sign. So for doctests we have normalized output by creating an
1388
eigenspace with a canonical basis.
1389
1390
1391
EXAMPLES:
1392
1393
This first test simply raises the deprecation warning. ::
1394
1395
sage: A = identity_matrix(RDF, 2)
1396
sage: es = A.eigenspaces_right()
1397
doctest:...: DeprecationWarning: Eigenspaces of RDF/CDF matrices are
1398
deprecated as of Sage version 5.0,
1399
please use "eigenmatrix_right" instead
1400
See http://trac.sagemath.org/11603 for details.
1401
1402
::
1403
1404
sage: m = matrix(RDF, [[-9, -14, 19, -74],[-1, 2, 4, -11],[-4, -12, 6, -32],[0, -2, -1, 1]])
1405
sage: m
1406
[ -9.0 -14.0 19.0 -74.0]
1407
[ -1.0 2.0 4.0 -11.0]
1408
[ -4.0 -12.0 6.0 -32.0]
1409
[ 0.0 -2.0 -1.0 1.0]
1410
sage: spectrum = m.eigenspaces_right()
1411
sage: spectrum[0][0]
1412
2.0
1413
sage: (RDF^4).subspace(spectrum[0][1].basis())
1414
Vector space of degree 4 and dimension 1 over Real Double Field
1415
Basis matrix:
1416
[ 1.0 -2.0 3.0 1.0]
1417
1418
sage: e, V = spectrum[2]
1419
sage: v = V.basis()[0]
1420
sage: diff = (m*v).change_ring(CDF) - e*v
1421
sage: abs(abs(diff)) < 3e-14
1422
True
1423
1424
TESTS::
1425
1426
sage: m.eigenspaces_right(algebraic_multiplicity=True)
1427
Traceback (most recent call last):
1428
...
1429
ValueError: algebraic_multiplicity must be set to False for double precision matrices
1430
"""
1431
from sage.misc.superseded import deprecation
1432
msg = ('Eigenspaces of RDF/CDF matrices are deprecated as of ',
1433
'Sage version 5.0',
1434
', please use "eigenmatrix_right" instead')
1435
deprecation(11603, ''.join(msg))
1436
# For numerical values we leave decisions about
1437
# multiplicity to the calling routine
1438
if algebraic_multiplicity:
1439
raise ValueError("algebraic_multiplicity must be set to False for double precision matrices")
1440
spectrum = self.right_eigenvectors()
1441
pairs = []
1442
for evalue,evectors,_ in spectrum:
1443
evector = evectors[0]
1444
espace = evector.parent().span_of_basis([evector],check=False)
1445
pairs.append((evalue, espace))
1446
return pairs
1447
1448
right_eigenspaces = eigenspaces_right
1449
1450
def eigenvalues(self, algorithm='default', tol=None):
1451
r"""
1452
Returns a list of eigenvalues.
1453
1454
1455
INPUT:
1456
1457
- ``self`` - a square matrix
1458
1459
- ``algorithm`` - default: ``'default'``
1460
1461
- ``'default'`` - applicable to any matrix
1462
with double-precision floating point entries.
1463
Uses the :meth:`~scipy.linalg.eigvals` method from SciPy.
1464
1465
- ``'symmetric'`` - converts the matrix into a real matrix
1466
(i.e. with entries from :class:`~sage.rings.real_double.RDF`),
1467
then applies the algorithm for Hermitian matrices. This
1468
algorithm can be significantly faster than the
1469
``'default'`` algorithm.
1470
1471
- ``'hermitian'`` - uses the :meth:`~scipy.linalg.eigh` method
1472
from SciPy, which applies only to real symmetric or complex
1473
Hermitian matrices. Since Hermitian is defined as a matrix
1474
equaling its conjugate-transpose, for a matrix with real
1475
entries this property is equivalent to being symmetric.
1476
This algorithm can be significantly faster than the
1477
``'default'`` algorithm.
1478
1479
- ``'tol'`` - default: ``None`` - if set to a value other than
1480
``None`` this is interpreted as a small real number used to aid in
1481
grouping eigenvalues that are numerically similar. See the output
1482
description for more information.
1483
1484
.. WARNING::
1485
1486
When using the ``'symmetric'`` or ``'hermitian'`` algorithms,
1487
no check is made on the input matrix, and only the entries below,
1488
and on, the main diagonal are employed in the computation.
1489
1490
Methods such as :meth:`is_symmetric` and :meth:`is_hermitian`
1491
could be used to verify this beforehand.
1492
1493
OUTPUT:
1494
1495
Default output for a square matrix of size $n$ is a list of $n$
1496
eigenvalues from the complex double field,
1497
:class:`~sage.rings.complex_double.CDF`. If the ``'symmetric'``
1498
or ``'hermitian'`` algorithms are chosen, the returned eigenvalues
1499
are from the real double field,
1500
:class:`~sage.rings.real_double.RDF`.
1501
1502
If a tolerance is specified, an attempt is made to group eigenvalues
1503
that are numerically similar. The return is then a list of pairs,
1504
where each pair is an eigenvalue followed by its multiplicity.
1505
The eigenvalue reported is the mean of the eigenvalues computed,
1506
and these eigenvalues are contained in an interval (or disk) whose
1507
radius is less than ``5*tol`` for $n < 10,000$ in the worst case.
1508
1509
More precisely, for an $n\times n$ matrix, the diameter of the
1510
interval containing similar eigenvalues could be as large as sum
1511
of the reciprocals of the first $n$ integers times ``tol``.
1512
1513
.. WARNING::
1514
1515
Use caution when using the ``tol`` parameter to group
1516
eigenvalues. See the examples below to see how this can go wrong.
1517
1518
EXAMPLES::
1519
1520
sage: m = matrix(RDF, 2, 2, [1,2,3,4])
1521
sage: ev = m.eigenvalues(); ev
1522
[-0.372281323..., 5.37228132...]
1523
sage: ev[0].parent()
1524
Complex Double Field
1525
1526
sage: m = matrix(RDF, 2, 2, [0,1,-1,0])
1527
sage: m.eigenvalues(algorithm='default')
1528
[1.0*I, -1.0*I]
1529
1530
sage: m = matrix(CDF, 2, 2, [I,1,-I,0])
1531
sage: m.eigenvalues()
1532
[-0.624810533... + 1.30024259...*I, 0.624810533... - 0.30024259...*I]
1533
1534
The adjacency matrix of a graph will be symmetric, and the
1535
eigenvalues will be real. ::
1536
1537
sage: A = graphs.PetersenGraph().adjacency_matrix()
1538
sage: A = A.change_ring(RDF)
1539
sage: ev = A.eigenvalues(algorithm='symmetric'); ev
1540
[-2.0, -2.0, -2.0, -2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0]
1541
sage: ev[0].parent()
1542
Real Double Field
1543
1544
The matrix ``A`` is "random", but the construction of ``B``
1545
provides a positive-definite Hermitian matrix. Note that
1546
the eigenvalues of a Hermitian matrix are real, and the
1547
eigenvalues of a positive-definite matrix will be positive. ::
1548
1549
sage: A = matrix([[ 4*I + 5, 8*I + 1, 7*I + 5, 3*I + 5],
1550
... [ 7*I - 2, -4*I + 7, -2*I + 4, 8*I + 8],
1551
... [-2*I + 1, 6*I + 6, 5*I + 5, -I - 4],
1552
... [ 5*I + 1, 6*I + 2, I - 4, -I + 3]])
1553
sage: B = (A*A.conjugate_transpose()).change_ring(CDF)
1554
sage: ev = B.eigenvalues(algorithm='hermitian'); ev
1555
[2.68144025..., 49.5167998..., 274.086188..., 390.71557...]
1556
sage: ev[0].parent()
1557
Real Double Field
1558
1559
A tolerance can be given to aid in grouping eigenvalues that
1560
are similar numerically. However, if the parameter is too small
1561
it might split too finely. Too large, and it can go wrong very
1562
badly. Use with care. ::
1563
1564
sage: G = graphs.PetersenGraph()
1565
sage: G.spectrum()
1566
[3, 1, 1, 1, 1, 1, -2, -2, -2, -2]
1567
1568
sage: A = G.adjacency_matrix().change_ring(RDF)
1569
sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5)
1570
[(-2.0, 4), (1.0, 5), (3.0, 1)]
1571
1572
sage: A.eigenvalues(algorithm='symmetric', tol=2.5)
1573
[(-2.0, 4), (1.33333333333, 6)]
1574
1575
An (extreme) example of properly grouping similar eigenvalues. ::
1576
1577
sage: G = graphs.HigmanSimsGraph()
1578
sage: A = G.adjacency_matrix().change_ring(RDF)
1579
sage: A.eigenvalues(algorithm='symmetric', tol=1.0e-5)
1580
[(-8.0, 22), (2.0, 77), (22.0, 1)]
1581
1582
TESTS:
1583
1584
Testing bad input. ::
1585
1586
sage: A = matrix(CDF, 2, range(4))
1587
sage: A.eigenvalues(algorithm='junk')
1588
Traceback (most recent call last):
1589
...
1590
ValueError: algorithm must be 'default', 'symmetric', or 'hermitian', not junk
1591
1592
sage: A = matrix(CDF, 2, 3, range(6))
1593
sage: A.eigenvalues()
1594
Traceback (most recent call last):
1595
...
1596
ValueError: matrix must be square, not 2 x 3
1597
1598
sage: A = matrix(CDF, 2, [1, 2, 3, 4*I])
1599
sage: A.eigenvalues(algorithm='symmetric')
1600
Traceback (most recent call last):
1601
...
1602
TypeError: cannot apply symmetric algorithm to matrix with complex entries
1603
1604
sage: A = matrix(CDF, 2, 2, range(4))
1605
sage: A.eigenvalues(tol='junk')
1606
Traceback (most recent call last):
1607
...
1608
TypeError: tolerance parameter must be a real number, not junk
1609
1610
sage: A = matrix(CDF, 2, 2, range(4))
1611
sage: A.eigenvalues(tol=-0.01)
1612
Traceback (most recent call last):
1613
...
1614
ValueError: tolerance parameter must be positive, not -0.01
1615
1616
A very small matrix. ::
1617
1618
sage: matrix(CDF,0,0).eigenvalues()
1619
[]
1620
"""
1621
import sage.rings.real_double
1622
import sage.rings.complex_double
1623
import numpy
1624
if not algorithm in ['default', 'symmetric', 'hermitian']:
1625
msg = "algorithm must be 'default', 'symmetric', or 'hermitian', not {0}"
1626
raise ValueError(msg.format(algorithm))
1627
if not self.is_square():
1628
msg = 'matrix must be square, not {0} x {1}'
1629
raise ValueError(msg.format(self.nrows(), self.ncols()))
1630
if algorithm == 'symmetric' and self.base_ring() == sage.rings.complex_double.CDF:
1631
try:
1632
self = self.change_ring(sage.rings.real_double.RDF) # check side effect
1633
except TypeError:
1634
raise TypeError('cannot apply symmetric algorithm to matrix with complex entries')
1635
if algorithm == 'symmetric':
1636
algorithm = 'hermitian'
1637
multiplicity = not tol is None
1638
if multiplicity:
1639
try:
1640
tol = float(tol)
1641
except (ValueError, TypeError):
1642
msg = 'tolerance parameter must be a real number, not {0}'
1643
raise TypeError(msg.format(tol))
1644
if tol < 0:
1645
msg = 'tolerance parameter must be positive, not {0}'
1646
raise ValueError(msg.format(tol))
1647
1648
if self._nrows == 0:
1649
return []
1650
global scipy
1651
if scipy is None:
1652
import scipy
1653
import scipy.linalg
1654
if self._nrows == 0:
1655
return []
1656
global scipy
1657
if scipy is None:
1658
import scipy
1659
import scipy.linalg
1660
global numpy
1661
if numpy is None:
1662
import numpy
1663
# generic eigenvalues, or real eigenvalues for Hermitian
1664
if algorithm == 'default':
1665
return_class = sage.rings.complex_double.CDF
1666
evalues = scipy.linalg.eigvals(self._matrix_numpy)
1667
elif algorithm=='hermitian':
1668
return_class = sage.rings.real_double.RDF
1669
evalues = scipy.linalg.eigh(self._matrix_numpy, eigvals_only=True)
1670
if not multiplicity:
1671
return [return_class(e) for e in evalues]
1672
else:
1673
# pairs in ev_group are
1674
# slot 0: the sum of "equal" eigenvalues, "s"
1675
# slot 1: number of eigenvalues in this sum, "m"
1676
# slot 2: average of these eigenvalues, "avg"
1677
# we test if "new" eigenvalues are close to the group average
1678
ev_group = []
1679
for e in evalues:
1680
location = None
1681
best_fit = tol
1682
for i in range(len(ev_group)):
1683
s, m, avg = ev_group[i]
1684
d = numpy.abs(avg - e)
1685
if d < best_fit:
1686
best_fit = d
1687
location = i
1688
if location is None:
1689
ev_group.append([e, 1, e])
1690
else:
1691
ev_group[location][0] += e
1692
ev_group[location][1] += 1
1693
ev_group[location][2] = ev_group[location][0]/ev_group[location][1]
1694
return [(return_class(avg), m) for _, m, avg in ev_group]
1695
1696
def left_eigenvectors(self):
1697
r"""
1698
Compute the left eigenvectors of a matrix of double precision
1699
real or complex numbers (i.e. RDF or CDF).
1700
1701
OUTPUT:
1702
Returns a list of triples, each of the form ``(e,[v],1)``,
1703
where ``e`` is the eigenvalue, and ``v`` is an associated
1704
left eigenvector. If the matrix is of size `n`, then there are
1705
`n` triples. Values are computed with the SciPy library.
1706
1707
The format of this output is designed to match the format
1708
for exact results. However, since matrices here have numerical
1709
entries, the resulting eigenvalues will also be numerical. No
1710
attempt is made to determine if two eigenvalues are equal, or if
1711
eigenvalues might actually be zero. So the algebraic multiplicity
1712
of each eigenvalue is reported as 1. Decisions about equal
1713
eigenvalues or zero eigenvalues should be addressed in the
1714
calling routine.
1715
1716
The SciPy routines used for these computations produce eigenvectors
1717
normalized to have length 1, but on different hardware they may vary
1718
by a sign. So for doctests we have normalized output by forcing their
1719
eigenvectors to have their first non-zero entry equal to one.
1720
1721
EXAMPLES::
1722
1723
sage: m = matrix(RDF, [[-5, 3, 2, 8],[10, 2, 4, -2],[-1, -10, -10, -17],[-2, 7, 6, 13]])
1724
sage: m
1725
[ -5.0 3.0 2.0 8.0]
1726
[ 10.0 2.0 4.0 -2.0]
1727
[ -1.0 -10.0 -10.0 -17.0]
1728
[ -2.0 7.0 6.0 13.0]
1729
sage: spectrum = m.left_eigenvectors()
1730
sage: for i in range(len(spectrum)):
1731
... spectrum[i][1][0]=matrix(RDF, spectrum[i][1]).echelon_form()[0]
1732
sage: spectrum[0]
1733
(2.0, [(1.0, 1.0, 1.0, 1.0)], 1)
1734
sage: spectrum[1]
1735
(1.0, [(1.0, 0.8, 0.8, 0.6)], 1)
1736
sage: spectrum[2]
1737
(-2.0, [(1.0, 0.4, 0.6, 0.2)], 1)
1738
sage: spectrum[3]
1739
(-1.0, [(1.0, 1.0, 2.0, 2.0)], 1)
1740
"""
1741
if not self.is_square():
1742
raise ArithmeticError("self must be a square matrix")
1743
if self._nrows == 0:
1744
return [], self.__copy__()
1745
global scipy
1746
if scipy is None:
1747
import scipy
1748
import scipy.linalg
1749
v,eig = scipy.linalg.eig(self._matrix_numpy, right=False, left=True)
1750
# scipy puts eigenvectors in columns, we will extract from rows
1751
eig = matrix(eig.T)
1752
return [(sage.rings.complex_double.CDF(v[i]), [eig[i]], 1) for i in range(len(v))]
1753
1754
eigenvectors_left = left_eigenvectors
1755
1756
def right_eigenvectors(self):
1757
r"""
1758
Compute the right eigenvectors of a matrix of double precision
1759
real or complex numbers (i.e. RDF or CDF).
1760
1761
OUTPUT:
1762
1763
Returns a list of triples, each of the form ``(e,[v],1)``,
1764
where ``e`` is the eigenvalue, and ``v`` is an associated
1765
right eigenvector. If the matrix is of size `n`, then there
1766
are `n` triples. Values are computed with the SciPy library.
1767
1768
The format of this output is designed to match the format
1769
for exact results. However, since matrices here have numerical
1770
entries, the resulting eigenvalues will also be numerical. No
1771
attempt is made to determine if two eigenvalues are equal, or if
1772
eigenvalues might actually be zero. So the algebraic multiplicity
1773
of each eigenvalue is reported as 1. Decisions about equal
1774
eigenvalues or zero eigenvalues should be addressed in the
1775
calling routine.
1776
1777
The SciPy routines used for these computations produce eigenvectors
1778
normalized to have length 1, but on different hardware they may vary
1779
by a sign. So for doctests we have normalized output by forcing their
1780
eigenvectors to have their first non-zero entry equal to one.
1781
1782
EXAMPLES::
1783
1784
sage: m = matrix(RDF, [[-9, -14, 19, -74],[-1, 2, 4, -11],[-4, -12, 6, -32],[0, -2, -1, 1]])
1785
sage: m
1786
[ -9.0 -14.0 19.0 -74.0]
1787
[ -1.0 2.0 4.0 -11.0]
1788
[ -4.0 -12.0 6.0 -32.0]
1789
[ 0.0 -2.0 -1.0 1.0]
1790
sage: spectrum = m.right_eigenvectors()
1791
sage: for i in range(len(spectrum)):
1792
... spectrum[i][1][0]=matrix(RDF, spectrum[i][1]).echelon_form()[0]
1793
sage: spectrum[0]
1794
(2.0, [(1.0, -2.0, 3.0, 1.0)], 1)
1795
sage: spectrum[1]
1796
(1.0, [(1.0, -0.666666666667, 1.33333333333, 0.333333333333)], 1)
1797
sage: spectrum[2]
1798
(-2.0, [(1.0, -0.2, 1.0, 0.2)], 1)
1799
sage: spectrum[3]
1800
(-1.0, [(1.0, -0.5, 2.0, 0.5)], 1)
1801
"""
1802
if not self.is_square():
1803
raise ArithmeticError("self must be a square matrix")
1804
if self._nrows == 0:
1805
return [], self.__copy__()
1806
global scipy
1807
if scipy is None:
1808
import scipy
1809
import scipy.linalg
1810
v,eig = scipy.linalg.eig(self._matrix_numpy, right=True, left=False)
1811
# scipy puts eigenvectors in columns, we will extract from rows
1812
eig = matrix(eig.T)
1813
return [(sage.rings.complex_double.CDF(v[i]), [eig[i]], 1) for i in range(len(v))]
1814
1815
eigenvectors_right = right_eigenvectors
1816
1817
def solve_left_LU(self, b):
1818
"""
1819
Solve the equation `A x = b` using LU decomposition.
1820
1821
.. WARNING::
1822
1823
This function is broken. See trac 4932.
1824
1825
INPUT:
1826
1827
- self -- an invertible matrix
1828
- b -- a vector
1829
1830
.. NOTE::
1831
1832
This method precomputes and stores the LU decomposition
1833
before solving. If many equations of the form Ax=b need to be
1834
solved for a singe matrix A, then this method should be used
1835
instead of solve. The first time this method is called it will
1836
compute the LU decomposition. If the matrix has not changed
1837
then subsequent calls will be very fast as the precomputed LU
1838
decomposition will be reused.
1839
1840
EXAMPLES::
1841
1842
sage: A = matrix(RDF, 3,3, [1,2,5,7.6,2.3,1,1,2,-1]); A
1843
[ 1.0 2.0 5.0]
1844
[ 7.6 2.3 1.0]
1845
[ 1.0 2.0 -1.0]
1846
sage: b = vector(RDF,[1,2,3])
1847
sage: x = A.solve_left_LU(b); x
1848
Traceback (most recent call last):
1849
...
1850
NotImplementedError: this function is not finished (see trac 4932)
1851
1852
1853
TESTS:
1854
1855
We test two degenerate cases::
1856
1857
sage: A = matrix(RDF, 0, 3, [])
1858
sage: A.solve_left_LU(vector(RDF,[]))
1859
(0.0, 0.0, 0.0)
1860
sage: A = matrix(RDF, 3, 0, [])
1861
sage: A.solve_left_LU(vector(RDF,3, [1,2,3]))
1862
()
1863
1864
"""
1865
if self._nrows != b.degree():
1866
raise ValueError("number of rows of self must equal degree of b")
1867
if self._nrows == 0 or self._ncols == 0:
1868
return self._row_ambient_module().zero_vector()
1869
1870
raise NotImplementedError("this function is not finished (see trac 4932)")
1871
self._c_compute_LU() # so self._L_M and self._U_M are defined below.
1872
cdef Matrix_double_dense M = self._new()
1873
lu = self._L_M*self._U_M
1874
global scipy
1875
if scipy is None:
1876
import scipy
1877
import scipy.linalg
1878
M._matrix_numpy = scipy.linalg.lu_solve((lu, self._P_M), b)
1879
return M
1880
1881
def solve_right(self, b):
1882
r"""
1883
Solve the vector equation ``A*x = b`` for a nonsingular ``A``.
1884
1885
INPUT:
1886
1887
- ``self`` - a square matrix that is nonsigular (of full rank).
1888
- ``b`` - a vector of the correct size. Elements of the vector
1889
must coerce into the base ring of the coefficient matrix. In
1890
particular, if ``b`` has entries from ``CDF`` then ``self``
1891
must have ``CDF`` as its base ring.
1892
1893
OUTPUT:
1894
1895
The unique solution ``x`` to the matrix equation ``A*x = b``,
1896
as a vector over the same base ring as ``self``.
1897
1898
ALGORITHM:
1899
1900
Uses the ``solve()`` routine from the SciPy ``scipy.linalg`` module.
1901
1902
EXAMPLES:
1903
1904
Over the reals. ::
1905
1906
sage: A = matrix(RDF, 3,3, [1,2,5,7.6,2.3,1,1,2,-1]); A
1907
[ 1.0 2.0 5.0]
1908
[ 7.6 2.3 1.0]
1909
[ 1.0 2.0 -1.0]
1910
sage: b = vector(RDF,[1,2,3])
1911
sage: x = A.solve_right(b); x
1912
(-0.113695090439, 1.39018087855, -0.333333333333)
1913
sage: x.parent()
1914
Vector space of dimension 3 over Real Double Field
1915
sage: A*x
1916
(1.0, 2.0, 3.0)
1917
1918
Over the complex numbers. ::
1919
1920
sage: A = matrix(CDF, [[ 0, -1 + 2*I, 1 - 3*I, I],
1921
... [2 + 4*I, -2 + 3*I, -1 + 2*I, -1 - I],
1922
... [ 2 + I, 1 - I, -1, 5],
1923
... [ 3*I, -1 - I, -1 + I, -3 + I]])
1924
sage: b = vector(CDF, [2 -3*I, 3, -2 + 3*I, 8])
1925
sage: x = A.solve_right(b); x
1926
(1.96841637... - 1.07606761...*I, -0.614323843... + 1.68416370...*I, 0.0733985765... + 1.73487544...*I, -1.6018683... + 0.524021352...*I)
1927
sage: x.parent()
1928
Vector space of dimension 4 over Complex Double Field
1929
sage: abs(A*x - b) < 1e-14
1930
True
1931
1932
The vector of constants, ``b``, can be given in a
1933
variety of forms, so long as it coerces to a vector
1934
over the same base ring as the coefficient matrix. ::
1935
1936
sage: A=matrix(CDF, 5, [1/(i+j+1) for i in range(5) for j in range(5)])
1937
sage: A.solve_right([1]*5) # rel tol 1e-11
1938
(5.0, -120.0, 630.0, -1120.0, 630.0)
1939
1940
TESTS:
1941
1942
A degenerate case. ::
1943
1944
sage: A = matrix(RDF, 0, 0, [])
1945
sage: A.solve_right(vector(RDF,[]))
1946
()
1947
1948
The coefficent matrix must be square. ::
1949
1950
sage: A = matrix(RDF, 2, 3, range(6))
1951
sage: b = vector(RDF, [1,2,3])
1952
sage: A.solve_right(b)
1953
Traceback (most recent call last):
1954
...
1955
ValueError: coefficient matrix of a system over RDF/CDF must be square, not 2 x 3
1956
1957
The coefficient matrix must be nonsingular. ::
1958
1959
sage: A = matrix(RDF, 5, range(25))
1960
sage: b = vector(RDF, [1,2,3,4,5])
1961
sage: A.solve_right(b)
1962
Traceback (most recent call last):
1963
...
1964
LinAlgError: singular matrix
1965
1966
The vector of constants needs the correct degree. ::
1967
1968
sage: A = matrix(RDF, 5, range(25))
1969
sage: b = vector(RDF, [1,2,3,4])
1970
sage: A.solve_right(b)
1971
Traceback (most recent call last):
1972
...
1973
TypeError: vector of constants over Real Double Field incompatible with matrix over Real Double Field
1974
1975
The vector of constants needs to be compatible with
1976
the base ring of the coefficient matrix. ::
1977
1978
sage: F.<a> = FiniteField(27)
1979
sage: b = vector(F, [a,a,a,a,a])
1980
sage: A.solve_right(b)
1981
Traceback (most recent call last):
1982
...
1983
TypeError: vector of constants over Finite Field in a of size 3^3 incompatible with matrix over Real Double Field
1984
1985
With a coefficient matrix over ``RDF``, a vector of constants
1986
over ``CDF`` can be accomodated by converting the base ring
1987
of the coefficient matrix. ::
1988
1989
sage: A = matrix(RDF, 2, range(4))
1990
sage: b = vector(CDF, [1+I,2])
1991
sage: A.solve_right(b)
1992
Traceback (most recent call last):
1993
...
1994
TypeError: vector of constants over Complex Double Field incompatible with matrix over Real Double Field
1995
1996
sage: B = A.change_ring(CDF)
1997
sage: B.solve_right(b)
1998
(-0.5 - 1.5*I, 1.0 + 1.0*I)
1999
"""
2000
if not self.is_square():
2001
raise ValueError("coefficient matrix of a system over RDF/CDF must be square, not %s x %s " % (self.nrows(), self.ncols()))
2002
M = self._column_ambient_module()
2003
try:
2004
vec = M(b)
2005
except TypeError:
2006
raise TypeError("vector of constants over %s incompatible with matrix over %s" % (b.base_ring(), self.base_ring()))
2007
if vec.degree() != self.ncols():
2008
raise 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() )
2009
2010
if self._ncols == 0:
2011
return M.zero_vector()
2012
2013
global scipy
2014
if scipy is None:
2015
import scipy
2016
import scipy.linalg
2017
# may raise a LinAlgError for a singular matrix
2018
return M(scipy.linalg.solve(self._matrix_numpy, vec.numpy()))
2019
2020
def solve_left(self, b):
2021
r"""
2022
Solve the vector equation ``x*A = b`` for a nonsingular ``A``.
2023
2024
INPUT:
2025
2026
- ``self`` - a square matrix that is nonsigular (of full rank).
2027
- ``b`` - a vector of the correct size. Elements of the vector
2028
must coerce into the base ring of the coefficient matrix. In
2029
particular, if ``b`` has entries from ``CDF`` then ``self``
2030
must have ``CDF`` as its base ring.
2031
2032
OUTPUT:
2033
2034
The unique solution ``x`` to the matrix equation ``x*A = b``,
2035
as a vector over the same base ring as ``self``.
2036
2037
ALGORITHM:
2038
2039
Uses the ``solve()`` routine from the SciPy ``scipy.linalg`` module,
2040
after taking the tranpose of the coefficient matrix.
2041
2042
EXAMPLES:
2043
2044
Over the reals. ::
2045
2046
sage: A = matrix(RDF, 3,3, [1,2,5,7.6,2.3,1,1,2,-1]); A
2047
[ 1.0 2.0 5.0]
2048
[ 7.6 2.3 1.0]
2049
[ 1.0 2.0 -1.0]
2050
sage: b = vector(RDF,[1,2,3])
2051
sage: x = A.solve_left(b); x.zero_at(1e-18) # fix noisy zeroes
2052
(0.666666666..., 0.0, 0.333333333...)
2053
sage: x.parent()
2054
Vector space of dimension 3 over Real Double Field
2055
sage: x*A
2056
(1.0, 2.0, 3.0)
2057
2058
Over the complex numbers. ::
2059
2060
sage: A = matrix(CDF, [[ 0, -1 + 2*I, 1 - 3*I, I],
2061
... [2 + 4*I, -2 + 3*I, -1 + 2*I, -1 - I],
2062
... [ 2 + I, 1 - I, -1, 5],
2063
... [ 3*I, -1 - I, -1 + I, -3 + I]])
2064
sage: b = vector(CDF, [2 -3*I, 3, -2 + 3*I, 8])
2065
sage: x = A.solve_left(b); x
2066
(-1.55765124... - 0.644483985...*I, 0.183274021... + 0.286476868...*I, 0.270818505... + 0.246619217...*I, -1.69003558... - 0.828113879...*I)
2067
sage: x.parent()
2068
Vector space of dimension 4 over Complex Double Field
2069
sage: abs(x*A - b) < 1e-14
2070
True
2071
2072
The vector of constants, ``b``, can be given in a
2073
variety of forms, so long as it coerces to a vector
2074
over the same base ring as the coefficient matrix. ::
2075
2076
sage: A=matrix(CDF, 5, [1/(i+j+1) for i in range(5) for j in range(5)])
2077
sage: A.solve_left([1]*5) # rel tol 1e-11
2078
(5.0, -120.0, 630.0, -1120.0, 630.0)
2079
2080
TESTS:
2081
2082
A degenerate case. ::
2083
2084
sage: A = matrix(RDF, 0, 0, [])
2085
sage: A.solve_left(vector(RDF,[]))
2086
()
2087
2088
The coefficent matrix must be square. ::
2089
2090
sage: A = matrix(RDF, 2, 3, range(6))
2091
sage: b = vector(RDF, [1,2,3])
2092
sage: A.solve_left(b)
2093
Traceback (most recent call last):
2094
...
2095
ValueError: coefficient matrix of a system over RDF/CDF must be square, not 2 x 3
2096
2097
The coefficient matrix must be nonsingular. ::
2098
2099
sage: A = matrix(RDF, 5, range(25))
2100
sage: b = vector(RDF, [1,2,3,4,5])
2101
sage: A.solve_left(b)
2102
Traceback (most recent call last):
2103
...
2104
LinAlgError: singular matrix
2105
2106
The vector of constants needs the correct degree. ::
2107
2108
sage: A = matrix(RDF, 5, range(25))
2109
sage: b = vector(RDF, [1,2,3,4])
2110
sage: A.solve_left(b)
2111
Traceback (most recent call last):
2112
...
2113
TypeError: vector of constants over Real Double Field incompatible with matrix over Real Double Field
2114
2115
The vector of constants needs to be compatible with
2116
the base ring of the coefficient matrix. ::
2117
2118
sage: F.<a> = FiniteField(27)
2119
sage: b = vector(F, [a,a,a,a,a])
2120
sage: A.solve_left(b)
2121
Traceback (most recent call last):
2122
...
2123
TypeError: vector of constants over Finite Field in a of size 3^3 incompatible with matrix over Real Double Field
2124
2125
With a coefficient matrix over ``RDF``, a vector of constants
2126
over ``CDF`` can be accomodated by converting the base ring
2127
of the coefficient matrix. ::
2128
2129
sage: A = matrix(RDF, 2, range(4))
2130
sage: b = vector(CDF, [1+I,2])
2131
sage: A.solve_left(b)
2132
Traceback (most recent call last):
2133
...
2134
TypeError: vector of constants over Complex Double Field incompatible with matrix over Real Double Field
2135
2136
sage: B = A.change_ring(CDF)
2137
sage: B.solve_left(b)
2138
(0.5 - 1.5*I, 0.5 + 0.5*I)
2139
"""
2140
if not self.is_square():
2141
raise ValueError("coefficient matrix of a system over RDF/CDF must be square, not %s x %s " % (self.nrows(), self.ncols()))
2142
M = self._row_ambient_module()
2143
try:
2144
vec = M(b)
2145
except TypeError:
2146
raise TypeError("vector of constants over %s incompatible with matrix over %s" % (b.base_ring(), self.base_ring()))
2147
if vec.degree() != self.nrows():
2148
raise 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() )
2149
2150
if self._nrows == 0:
2151
return M.zero_vector()
2152
2153
global scipy
2154
if scipy is None:
2155
import scipy
2156
import scipy.linalg
2157
# may raise a LinAlgError for a singular matrix
2158
# call "right solve" routine with the transpose
2159
return M(scipy.linalg.solve(self._matrix_numpy.T, vec.numpy()))
2160
2161
def determinant(self):
2162
"""
2163
Return the determinant of self.
2164
2165
ALGORITHM:
2166
2167
Use numpy
2168
2169
EXAMPLES::
2170
2171
sage: m = matrix(RDF,2,range(4)); m.det()
2172
-2.0
2173
sage: m = matrix(RDF,0,[]); m.det()
2174
1.0
2175
sage: m = matrix(RDF, 2, range(6)); m.det()
2176
Traceback (most recent call last):
2177
...
2178
ValueError: self must be a square matrix
2179
"""
2180
if not self.is_square():
2181
raise ValueError("self must be a square matrix")
2182
if self._nrows == 0 or self._ncols == 0:
2183
return self._sage_dtype(1)
2184
global scipy
2185
if scipy is None:
2186
import scipy
2187
import scipy.linalg
2188
2189
return self._sage_dtype(scipy.linalg.det(self._matrix_numpy))
2190
2191
2192
def log_determinant(self):
2193
"""
2194
Compute the log of the absolute value of the determinant
2195
using LU decomposition.
2196
2197
.. NOTE::
2198
2199
This is useful if the usual determinant overflows.
2200
2201
EXAMPLES::
2202
2203
sage: m = matrix(RDF,2,2,range(4)); m
2204
[0.0 1.0]
2205
[2.0 3.0]
2206
sage: RDF(log(abs(m.determinant())))
2207
0.69314718056
2208
sage: m.log_determinant()
2209
0.69314718056
2210
sage: m = matrix(RDF,0,0,[]); m
2211
[]
2212
sage: m.log_determinant()
2213
0.0
2214
sage: m = matrix(CDF,2,2,range(4)); m
2215
[0.0 1.0]
2216
[2.0 3.0]
2217
sage: RDF(log(abs(m.determinant())))
2218
0.69314718056
2219
sage: m.log_determinant()
2220
0.69314718056
2221
sage: m = matrix(CDF,0,0,[]); m
2222
[]
2223
sage: m.log_determinant()
2224
0.0
2225
2226
"""
2227
global numpy
2228
cdef Matrix_double_dense U
2229
2230
if self._nrows == 0 or self._ncols == 0:
2231
return sage.rings.real_double.RDF(0)
2232
2233
if not self.is_square():
2234
raise ArithmeticError("self must be a square matrix")
2235
2236
P, L, U = self.LU()
2237
if numpy is None:
2238
import numpy
2239
2240
return sage.rings.real_double.RDF(sum(numpy.log(abs(numpy.diag(U._matrix_numpy)))))
2241
2242
def transpose(self):
2243
"""
2244
Return the transpose of this matrix, without changing self.
2245
2246
EXAMPLES::
2247
2248
sage: m = matrix(RDF,2,3,range(6)); m
2249
[0.0 1.0 2.0]
2250
[3.0 4.0 5.0]
2251
sage: m2 = m.transpose()
2252
sage: m[0,0] = 2
2253
sage: m2 #note that m2 hasn't changed
2254
[0.0 3.0]
2255
[1.0 4.0]
2256
[2.0 5.0]
2257
2258
``.T`` is a convenient shortcut for the transpose::
2259
2260
sage: m.T
2261
[2.0 3.0]
2262
[1.0 4.0]
2263
[2.0 5.0]
2264
2265
sage: m = matrix(RDF,0,3); m
2266
[]
2267
sage: m.transpose()
2268
[]
2269
sage: m.transpose().parent()
2270
Full MatrixSpace of 3 by 0 dense matrices over Real Double Field
2271
2272
"""
2273
if self._nrows == 0 or self._ncols == 0:
2274
return self.new_matrix(self._ncols, self._nrows)
2275
2276
cdef Matrix_double_dense trans
2277
trans = self._new(self._ncols, self._nrows)
2278
trans._matrix_numpy = self._matrix_numpy.transpose().copy()
2279
if self._subdivisions is not None:
2280
row_divs, col_divs = self.subdivisions()
2281
trans.subdivide(col_divs, row_divs)
2282
return trans
2283
2284
def SVD(self, *args, **kwds):
2285
r"""
2286
Return the singular value decomposition of this matrix.
2287
2288
The U and V matrices are not unique and may be returned with different
2289
values in the future or on different systems. The S matrix is unique
2290
and contains the singular values in descending order.
2291
2292
The computed decomposition is cached and returned on subsequent calls.
2293
2294
INPUT:
2295
2296
- A -- a matrix
2297
2298
OUTPUT:
2299
2300
- U, S, V -- immutable matrices such that $A = U*S*V.conj().transpose()$
2301
where U and V are orthogonal and S is zero off of the diagonal.
2302
2303
Note that if self is m-by-n, then the dimensions of the
2304
matrices that this returns are (m,m), (m,n), and (n, n).
2305
2306
.. NOTE::
2307
2308
If all you need is the singular values of the matrix, see
2309
the more convenient :meth:`singular_values`.
2310
2311
EXAMPLES::
2312
2313
sage: m = matrix(RDF,4,range(1,17))
2314
sage: U,S,V = m.SVD()
2315
sage: U*S*V.transpose()
2316
[ 1.0 2.0 3.0 4.0]
2317
[ 5.0 6.0 7.0 8.0]
2318
[ 9.0 10.0 11.0 12.0]
2319
[13.0 14.0 15.0 16.0]
2320
2321
A non-square example::
2322
2323
sage: m = matrix(RDF, 2, range(1,7)); m
2324
[1.0 2.0 3.0]
2325
[4.0 5.0 6.0]
2326
sage: U, S, V = m.SVD()
2327
sage: U*S*V.transpose()
2328
[1.0 2.0 3.0]
2329
[4.0 5.0 6.0]
2330
2331
S contains the singular values::
2332
2333
sage: S.round(4)
2334
[ 9.508 0.0 0.0]
2335
[ 0.0 0.7729 0.0]
2336
sage: [round(sqrt(abs(x)),4) for x in (S*S.transpose()).eigenvalues()]
2337
[9.508, 0.7729]
2338
2339
U and V are orthogonal matrices::
2340
2341
sage: U # random, SVD is not unique
2342
[-0.386317703119 -0.922365780077]
2343
[-0.922365780077 0.386317703119]
2344
[-0.274721127897 -0.961523947641]
2345
[-0.961523947641 0.274721127897]
2346
sage: (U*U.transpose()).zero_at(1e-15)
2347
[1.0 0.0]
2348
[0.0 1.0]
2349
sage: V # random, SVD is not unique
2350
[-0.428667133549 0.805963908589 0.408248290464]
2351
[-0.566306918848 0.112382414097 -0.816496580928]
2352
[-0.703946704147 -0.581199080396 0.408248290464]
2353
sage: (V*V.transpose()).zero_at(1e-15)
2354
[1.0 0.0 0.0]
2355
[0.0 1.0 0.0]
2356
[0.0 0.0 1.0]
2357
2358
TESTS::
2359
2360
sage: m = matrix(RDF,3,2,range(1, 7)); m
2361
[1.0 2.0]
2362
[3.0 4.0]
2363
[5.0 6.0]
2364
sage: U,S,V = m.SVD()
2365
sage: U*S*V.transpose()
2366
[1.0 2.0]
2367
[3.0 4.0]
2368
[5.0 6.0]
2369
2370
sage: m = matrix(RDF, 3, 0, []); m
2371
[]
2372
sage: m.SVD()
2373
([], [], [])
2374
sage: m = matrix(RDF, 0, 3, []); m
2375
[]
2376
sage: m.SVD()
2377
([], [], [])
2378
sage: def shape(x): return (x.nrows(), x.ncols())
2379
sage: m = matrix(RDF, 2, 3, range(6))
2380
sage: map(shape, m.SVD())
2381
[(2, 2), (2, 3), (3, 3)]
2382
sage: for x in m.SVD(): x.is_immutable()
2383
True
2384
True
2385
True
2386
"""
2387
global scipy, numpy
2388
cdef Py_ssize_t i
2389
cdef Matrix_double_dense U, S, V
2390
2391
if len(args)>0 or len(kwds)>0:
2392
from sage.misc.superseded import deprecation
2393
deprecation(7852, "Arguments passed to SVD, but SVD no longer supports different methods (it only uses numpy now).")
2394
2395
if self._nrows == 0 or self._ncols == 0:
2396
U_t = self.new_matrix(self._nrows, self._ncols)
2397
S_t = self.new_matrix(self._nrows, self._ncols)
2398
V_t = self.new_matrix(self._ncols, self._nrows)
2399
return U_t, S_t, V_t
2400
2401
USV = self.fetch('SVD_factors')
2402
if USV is None:
2403
# TODO: More efficient representation of non-square diagonal matrix S
2404
if scipy is None:
2405
import scipy
2406
import scipy.linalg
2407
if numpy is None:
2408
import numpy
2409
U_mat, S_diagonal, V_mat = scipy.linalg.svd(self._matrix_numpy)
2410
2411
U = self._new(self._nrows, self._nrows)
2412
S = self._new(self._nrows, self._ncols)
2413
V = self._new(self._ncols, self._ncols)
2414
2415
S_mat = numpy.zeros((self._nrows, self._ncols), dtype=self._numpy_dtype)
2416
for i in range(S_diagonal.shape[0]):
2417
S_mat[i,i] = S_diagonal[i]
2418
2419
U._matrix_numpy = numpy.ascontiguousarray(U_mat)
2420
S._matrix_numpy = S_mat
2421
V._matrix_numpy = numpy.ascontiguousarray(V_mat.conj().T)
2422
USV = U, S, V
2423
for M in USV: M.set_immutable()
2424
self.cache('SVD_factors', USV)
2425
2426
return USV
2427
2428
def QR(self):
2429
r"""
2430
Returns a factorization into a unitary matrix and an
2431
upper-triangular matrix.
2432
2433
INPUT:
2434
2435
Any matrix over ``RDF`` or ``CDF``.
2436
2437
OUTPUT:
2438
2439
``Q``, ``R`` -- a pair of matrices such that if `A`
2440
is the original matrix, then
2441
2442
.. math::
2443
2444
A = QR, \quad Q^\ast Q = I
2445
2446
where `R` is upper-triangular. `Q^\ast` is the
2447
conjugate-transpose in the complex case, and just
2448
the transpose in the real case. So `Q` is a unitary
2449
matrix (or rather, orthogonal, in the real case),
2450
or equivalently `Q` has orthogonal columns. For a
2451
matrix of full rank this factorization is unique
2452
up to adjustments via multiples of rows and columns
2453
by multiples with scalars having modulus `1`. So
2454
in the full-rank case, `R` is unique if the diagonal
2455
entries are required to be positive real numbers.
2456
2457
The resulting decomposition is cached.
2458
2459
ALGORITHM:
2460
2461
Calls "linalg.qr" from SciPy, which is in turn an
2462
interface to LAPACK routines.
2463
2464
EXAMPLES:
2465
2466
Over the reals, the inverse of ``Q`` is its transpose,
2467
since including a conjugate has no effect. In the real
2468
case, we say ``Q`` is orthogonal. ::
2469
2470
sage: A = matrix(RDF, [[-2, 0, -4, -1, -1],
2471
... [-2, 1, -6, -3, -1],
2472
... [1, 1, 7, 4, 5],
2473
... [3, 0, 8, 3, 3],
2474
... [-1, 1, -6, -6, 5]])
2475
sage: Q, R = A.QR()
2476
2477
At this point, ``Q`` is only well-defined up to the
2478
signs of its columns, and similarly for ``R`` and its
2479
rows, so we normalize them::
2480
2481
sage: Qnorm = Q._normalize_columns()
2482
sage: Rnorm = R._normalize_rows()
2483
sage: Qnorm.round(6).zero_at(10^-6)
2484
[ 0.458831 0.126051 0.381212 0.394574 0.68744]
2485
[ 0.458831 -0.47269 -0.051983 -0.717294 0.220963]
2486
[-0.229416 -0.661766 0.661923 0.180872 -0.196411]
2487
[-0.688247 -0.189076 -0.204468 -0.09663 0.662889]
2488
[ 0.229416 -0.535715 -0.609939 0.536422 -0.024551]
2489
sage: Rnorm.round(6).zero_at(10^-6)
2490
[ 4.358899 -0.458831 13.076697 6.194225 2.982405]
2491
[ 0.0 1.670172 0.598741 -1.29202 6.207997]
2492
[ 0.0 0.0 5.444402 5.468661 -0.682716]
2493
[ 0.0 0.0 0.0 1.027626 -3.6193]
2494
[ 0.0 0.0 0.0 0.0 0.024551]
2495
sage: (Q*Q.transpose()).zero_at(10^-14)
2496
[1.0 0.0 0.0 0.0 0.0]
2497
[0.0 1.0 0.0 0.0 0.0]
2498
[0.0 0.0 1.0 0.0 0.0]
2499
[0.0 0.0 0.0 1.0 0.0]
2500
[0.0 0.0 0.0 0.0 1.0]
2501
sage: (Q*R - A).zero_at(10^-14)
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]
2506
[0.0 0.0 0.0 0.0 0.0]
2507
2508
Now over the complex numbers, demonstrating that the SciPy libraries
2509
are (properly) using the Hermitian inner product, so that ``Q`` is
2510
a unitary matrix (its inverse is the conjugate-transpose). ::
2511
2512
sage: A = matrix(CDF, [[-8, 4*I + 1, -I + 2, 2*I + 1],
2513
... [1, -2*I - 1, -I + 3, -I + 1],
2514
... [I + 7, 2*I + 1, -2*I + 7, -I + 1],
2515
... [I + 2, 0, I + 12, -1]])
2516
sage: Q, R = A.QR()
2517
sage: Q._normalize_columns().round(6).zero_at(10^-6)
2518
[ 0.730297 0.207057 + 0.538347*I 0.246305 - 0.076446*I 0.238162 - 0.10366*I]
2519
[ -0.091287 -0.207057 - 0.377878*I 0.378656 - 0.195222*I 0.701244 - 0.364371*I]
2520
[ -0.63901 - 0.091287*I 0.170822 + 0.667758*I -0.034115 + 0.040902*I 0.314017 - 0.082519*I]
2521
[-0.182574 - 0.091287*I -0.036235 + 0.07247*I 0.863228 + 0.063228*I -0.449969 - 0.011612*I]
2522
sage: R._normalize_rows().round(6).zero_at(10^-6)
2523
[ 10.954451 -1.917029*I 5.385938 - 2.19089*I -0.273861 - 2.19089*I]
2524
[ 0.0 4.829596 -0.869638 - 5.864879*I 0.993872 - 0.305409*I]
2525
[ 0.0 0.0 12.001608 -0.270953 + 0.442063*I]
2526
[ 0.0 0.0 0.0 1.942964]
2527
sage: (Q.conjugate().transpose()*Q).zero_at(10^-15)
2528
[1.0 0.0 0.0 0.0]
2529
[0.0 1.0 0.0 0.0]
2530
[0.0 0.0 1.0 0.0]
2531
[0.0 0.0 0.0 1.0]
2532
sage: (Q*R - A).zero_at(10^-14)
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]
2536
[0.0 0.0 0.0 0.0]
2537
2538
An example of a rectangular matrix that is also rank-deficient.
2539
If you run this example yourself, you may see a very small, nonzero
2540
entries in the third row, in the third column, even though the exact
2541
version of the matrix has rank 2. The final two columns of ``Q``
2542
span the left kernel of ``A`` (as evidenced by the two zero rows of
2543
``R``). Different platforms will compute different bases for this
2544
left kernel, so we do not exhibit the actual matrix. ::
2545
2546
sage: Arat = matrix(QQ, [[2, -3, 3],
2547
... [-1, 1, -1],
2548
... [-1, 3, -3],
2549
... [-5, 1, -1]])
2550
sage: Arat.rank()
2551
2
2552
sage: A = Arat.change_ring(CDF)
2553
sage: Q, R = A.QR()
2554
sage: R._normalize_rows().round(6).zero_at(10^-6)
2555
[ 5.567764 -2.69408 2.69408]
2556
[ 0.0 3.569585 -3.569585]
2557
[ 0.0 0.0 0.0]
2558
[ 0.0 0.0 0.0]
2559
sage: (Q.conjugate_transpose()*Q).zero_at(10^-14)
2560
[1.0 0.0 0.0 0.0]
2561
[0.0 1.0 0.0 0.0]
2562
[0.0 0.0 1.0 0.0]
2563
[0.0 0.0 0.0 1.0]
2564
2565
Results are cached, meaning they are immutable matrices.
2566
Make a copy if you need to manipulate a result. ::
2567
2568
sage: A = random_matrix(CDF, 2, 2)
2569
sage: Q, R = A.QR()
2570
sage: Q.is_mutable()
2571
False
2572
sage: R.is_mutable()
2573
False
2574
sage: Q[0,0] = 0
2575
Traceback (most recent call last):
2576
...
2577
ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
2578
sage: Qcopy = copy(Q)
2579
sage: Qcopy[0,0] = 679
2580
sage: Qcopy[0,0]
2581
679.0
2582
2583
TESTS:
2584
2585
Trivial cases return trivial results of the correct size,
2586
and we check ``Q`` itself in one case, verifying a fix for
2587
:trac:`10795`. ::
2588
2589
sage: A = zero_matrix(RDF, 0, 10)
2590
sage: Q, R = A.QR()
2591
sage: Q.nrows(), Q.ncols()
2592
(0, 0)
2593
sage: R.nrows(), R.ncols()
2594
(0, 10)
2595
sage: A = zero_matrix(RDF, 3, 0)
2596
sage: Q, R = A.QR()
2597
sage: Q.nrows(), Q.ncols()
2598
(3, 3)
2599
sage: R.nrows(), R.ncols()
2600
(3, 0)
2601
sage: Q
2602
[1.0 0.0 0.0]
2603
[0.0 1.0 0.0]
2604
[0.0 0.0 1.0]
2605
"""
2606
global scipy
2607
cdef Matrix_double_dense Q,R
2608
2609
if self._nrows == 0 or self._ncols == 0:
2610
return self.new_matrix(self._nrows, self._nrows, entries=1), self.new_matrix()
2611
2612
QR = self.fetch('QR_factors')
2613
if QR is None:
2614
Q = self._new(self._nrows, self._nrows)
2615
R = self._new(self._nrows, self._ncols)
2616
if scipy is None:
2617
import scipy
2618
import scipy.linalg
2619
Q._matrix_numpy, R._matrix_numpy = scipy.linalg.qr(self._matrix_numpy)
2620
Q.set_immutable()
2621
R.set_immutable()
2622
QR = (Q, R)
2623
self.cache('QR_factors', QR)
2624
return QR
2625
2626
def is_symmetric(self, tol = 1e-12):
2627
"""
2628
Return whether this matrix is symmetric, to the given tolerance.
2629
2630
EXAMPLES::
2631
2632
sage: m = matrix(RDF,2,2,range(4)); m
2633
[0.0 1.0]
2634
[2.0 3.0]
2635
sage: m.is_symmetric()
2636
False
2637
sage: m[1,0] = 1.1; m
2638
[0.0 1.0]
2639
[1.1 3.0]
2640
sage: m.is_symmetric()
2641
False
2642
2643
The tolerance inequality is strict:
2644
sage: m.is_symmetric(tol=0.1)
2645
False
2646
sage: m.is_symmetric(tol=0.11)
2647
True
2648
"""
2649
cdef Py_ssize_t i, j
2650
tol = float(tol)
2651
key = 'symmetric_%s'%tol
2652
b = self.fetch(key)
2653
if not b is None:
2654
return b
2655
if self._nrows != self._ncols:
2656
self.cache(key, False)
2657
return False
2658
b = True
2659
for i from 0 < i < self._nrows:
2660
for j from 0 <= j < i:
2661
if math.fabs(self.get_unsafe(i,j) - self.get_unsafe(j,i)) > tol:
2662
b = False
2663
break
2664
self.cache(key, b)
2665
return b
2666
2667
def is_unitary(self, tol=1e-12, algorithm='orthonormal'):
2668
r"""
2669
Returns ``True`` if the columns of the matrix are an orthonormal basis.
2670
2671
For a matrix with real entries this determines if a matrix is
2672
"orthogonal" and for a matrix with complex entries this determines
2673
if the matrix is "unitary."
2674
2675
INPUT:
2676
2677
- ``tol`` - default: ``1e-12`` - the largest value of the
2678
absolute value of the difference between two matrix entries
2679
for which they will still be considered equal.
2680
2681
- ``algorithm`` - default: 'orthonormal' - set to 'orthonormal'
2682
for a stable procedure and set to 'naive' for a fast
2683
procedure.
2684
2685
OUTPUT:
2686
2687
``True`` if the matrix is square and its conjugate-transpose is
2688
its inverse, and ``False`` otherwise. In other words, a matrix
2689
is orthogonal or unitary if the product of its conjugate-transpose
2690
times the matrix is the identity matrix.
2691
2692
The tolerance parameter is used to allow for numerical values
2693
to be equal if there is a slight difference due to round-off
2694
and other imprecisions.
2695
2696
The result is cached, on a per-tolerance and per-algorithm basis.
2697
2698
ALGORITHMS:
2699
2700
The naive algorithm simply computes the product of the
2701
conjugate-transpose with the matrix and compares the entries
2702
to the identity matrix, with equality controlled by the
2703
tolerance parameter.
2704
2705
The orthonormal algorithm first computes a Schur decomposition
2706
(via the :meth:`schur` method) and checks that the result is a
2707
diagonal matrix with entries of modulus 1, which is equivalent to
2708
being unitary.
2709
2710
So the naive algorithm might finish fairly quickly for a matrix
2711
that is not unitary, once the product has been computed.
2712
However, the orthonormal algorithm will compute a Schur
2713
decomposition before going through a similar check of a
2714
matrix entry-by-entry.
2715
2716
EXAMPLES:
2717
2718
A matrix that is far from unitary. ::
2719
2720
sage: A = matrix(RDF, 4, range(16))
2721
sage: A.conjugate().transpose()*A
2722
[224.0 248.0 272.0 296.0]
2723
[248.0 276.0 304.0 332.0]
2724
[272.0 304.0 336.0 368.0]
2725
[296.0 332.0 368.0 404.0]
2726
sage: A.is_unitary()
2727
False
2728
sage: A.is_unitary(algorithm='naive')
2729
False
2730
sage: A.is_unitary(algorithm='orthonormal')
2731
False
2732
2733
The QR decoposition will produce a unitary matrix as Q and the
2734
SVD decomposition will create two unitary matrices, U and V. ::
2735
2736
sage: A = matrix(CDF, [[ 1 - I, -3*I, -2 + I, 1, -2 + 3*I],
2737
... [ 1 - I, -2 + I, 1 + 4*I, 0, 2 + I],
2738
... [ -1, -5 + I, -2 + I, 1 + I, -5 - 4*I],
2739
... [-2 + 4*I, 2 - I, 8 - 4*I, 1 - 8*I, 3 - 2*I]])
2740
sage: Q, R = A.QR()
2741
sage: Q.is_unitary()
2742
True
2743
sage: U, S, V = A.SVD()
2744
sage: U.is_unitary(algorithm='naive')
2745
True
2746
sage: U.is_unitary(algorithm='orthonormal')
2747
True
2748
sage: V.is_unitary(algorithm='naive') # not tested - known bug (trac #11248)
2749
True
2750
2751
If we make the tolerance too strict we can get misleading results. ::
2752
2753
sage: A = matrix(RDF, 10, 10, [1/(i+j+1) for i in range(10) for j in range(10)])
2754
sage: Q, R = A.QR()
2755
sage: Q.is_unitary(algorithm='naive', tol=1e-16)
2756
False
2757
sage: Q.is_unitary(algorithm='orthonormal', tol=1e-17)
2758
False
2759
2760
Rectangular matrices are not unitary/orthogonal, even if their
2761
columns form an orthonormal set. ::
2762
2763
sage: A = matrix(CDF, [[1,0], [0,0], [0,1]])
2764
sage: A.is_unitary()
2765
False
2766
2767
The smallest cases. The Schur decomposition used by the
2768
orthonormal algorithm will fail on a matrix of size zero. ::
2769
2770
sage: P = matrix(CDF, 0, 0)
2771
sage: P.is_unitary(algorithm='naive')
2772
True
2773
2774
sage: P = matrix(CDF, 1, 1, [1])
2775
sage: P.is_unitary(algorithm='orthonormal')
2776
True
2777
2778
sage: P = matrix(CDF, 0, 0,)
2779
sage: P.is_unitary(algorithm='orthonormal')
2780
Traceback (most recent call last):
2781
...
2782
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (0,)
2783
2784
TESTS::
2785
2786
sage: P = matrix(CDF, 2, 2)
2787
sage: P.is_unitary(tol='junk')
2788
Traceback (most recent call last):
2789
...
2790
TypeError: tolerance must be a real number, not junk
2791
2792
sage: P.is_unitary(tol=-0.3)
2793
Traceback (most recent call last):
2794
...
2795
ValueError: tolerance must be positive, not -0.3
2796
2797
sage: P.is_unitary(algorithm='junk')
2798
Traceback (most recent call last):
2799
...
2800
ValueError: algorithm must be 'naive' or 'orthonormal', not junk
2801
2802
2803
AUTHOR:
2804
2805
- Rob Beezer (2011-05-04)
2806
"""
2807
global numpy
2808
try:
2809
tol = float(tol)
2810
except Exception:
2811
raise TypeError('tolerance must be a real number, not {0}'.format(tol))
2812
if tol <= 0:
2813
raise ValueError('tolerance must be positive, not {0}'.format(tol))
2814
if not algorithm in ['naive', 'orthonormal']:
2815
raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm))
2816
key = 'unitary_{0}_{1}'.format(algorithm, tol)
2817
b = self.fetch(key)
2818
if not b is None:
2819
return b
2820
if not self.is_square():
2821
self.cache(key, False)
2822
return False
2823
if numpy is None:
2824
import numpy
2825
cdef Py_ssize_t i, j
2826
cdef Matrix_double_dense T, P
2827
if algorithm == 'orthonormal':
2828
# Schur decomposition over CDF will be unitary
2829
# iff diagonal with unit modulus entries
2830
_, T = self.schur(base_ring=sage.rings.complex_double.CDF)
2831
unitary = T._is_lower_triangular(tol)
2832
if unitary:
2833
for 0 <= i < self._nrows:
2834
if numpy.absolute(numpy.absolute(T.get_unsafe(i,i)) - 1) > tol:
2835
unitary = False
2836
break
2837
elif algorithm == 'naive':
2838
P = self.conjugate().transpose()*self
2839
unitary = True
2840
for i from 0 <= i < self._nrows:
2841
# off-diagonal, since P is Hermitian
2842
for j from 0 <= j < i:
2843
if numpy.absolute(P.get_unsafe(i,j)) > tol:
2844
unitary = False
2845
break
2846
# at diagonal
2847
if numpy.absolute(P.get_unsafe(i,i)-1) > tol:
2848
unitary = False
2849
if not unitary:
2850
break
2851
self.cache(key, unitary)
2852
return unitary
2853
2854
def _is_lower_triangular(self, tol):
2855
r"""
2856
Returns ``True`` if the entries above the diagonal are all zero.
2857
2858
INPUT:
2859
2860
- ``tol`` - the largest value of the absolute value of the
2861
difference between two matrix entries for which they will
2862
still be considered equal.
2863
2864
OUTPUT:
2865
2866
Returns ``True`` if each entry above the diagonal (entries
2867
with a row index less than the column index) is zero.
2868
2869
EXAMPLES::
2870
2871
sage: A = matrix(RDF, [[ 2.0, 0.0, 0.0],
2872
... [ 1.0, 3.0, 0.0],
2873
... [-4.0, 2.0, -1.0]])
2874
sage: A._is_lower_triangular(1.0e-17)
2875
True
2876
sage: A[1,2] = 10^-13
2877
sage: A._is_lower_triangular(1.0e-14)
2878
False
2879
"""
2880
global numpy
2881
if numpy is None:
2882
import numpy
2883
cdef Py_ssize_t i, j
2884
for i in range(self._nrows):
2885
for j in range(i+1, self._ncols):
2886
if numpy.absolute(self.get_unsafe(i,j)) > tol:
2887
return False
2888
return True
2889
2890
def is_hermitian(self, tol = 1e-12, algorithm='orthonormal'):
2891
r"""
2892
Returns ``True`` if the matrix is equal to its conjugate-transpose.
2893
2894
INPUT:
2895
2896
- ``tol`` - default: ``1e-12`` - the largest value of the
2897
absolute value of the difference between two matrix entries
2898
for which they will still be considered equal.
2899
2900
- ``algorithm`` - default: 'orthonormal' - set to 'orthonormal'
2901
for a stable procedure and set to 'naive' for a fast
2902
procedure.
2903
2904
OUTPUT:
2905
2906
``True`` if the matrix is square and equal to the transpose
2907
with every entry conjugated, and ``False`` otherwise.
2908
2909
Note that if conjugation has no effect on elements of the base
2910
ring (such as for integers), then the :meth:`is_symmetric`
2911
method is equivalent and faster.
2912
2913
The tolerance parameter is used to allow for numerical values
2914
to be equal if there is a slight difference due to round-off
2915
and other imprecisions.
2916
2917
The result is cached, on a per-tolerance and per-algorithm basis.
2918
2919
ALGORITHMS:
2920
2921
The naive algorithm simply compares corresponing entries on either
2922
side of the diagonal (and on the diagonal itself) to see if they are
2923
conjugates, with equality controlled by the tolerance parameter.
2924
2925
The orthonormal algorithm first computes a Schur decomposition
2926
(via the :meth:`schur` method) and checks that the result is a
2927
diagonal matrix with real entries.
2928
2929
So the naive algorithm can finish quickly for a matrix that is not
2930
Hermitian, while the orthonormal algorithm will always compute a
2931
Schur decomposition before going through a similar check of the matrix
2932
entry-by-entry.
2933
2934
EXAMPLES::
2935
2936
sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],
2937
... [-3 - I, -4*I, -2],
2938
... [-1 + I, -2 - 8*I, 2 + I]])
2939
sage: A.is_hermitian(algorithm='orthonormal')
2940
False
2941
sage: A.is_hermitian(algorithm='naive')
2942
False
2943
sage: B = A*A.conjugate_transpose()
2944
sage: B.is_hermitian(algorithm='orthonormal')
2945
True
2946
sage: B.is_hermitian(algorithm='naive')
2947
True
2948
2949
A matrix that is nearly Hermitian, but for one non-real
2950
diagonal entry. ::
2951
2952
sage: A = matrix(CDF, [[ 2, 2-I, 1+4*I],
2953
... [ 2+I, 3+I, 2-6*I],
2954
... [1-4*I, 2+6*I, 5]])
2955
sage: A.is_hermitian(algorithm='orthonormal')
2956
False
2957
sage: A[1,1] = 132
2958
sage: A.is_hermitian(algorithm='orthonormal')
2959
True
2960
2961
We get a unitary matrix from the SVD routine and use this
2962
numerical matrix to create a matrix that should be Hermitian
2963
(indeed it should be the identity matrix), but with some
2964
imprecision. We use this to illustrate that if the tolerance
2965
is set too small, then we can be too strict about the equality
2966
of entries and may achieve the wrong result (depending on
2967
the system)::
2968
2969
sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],
2970
... [-3 - I, -4*I, -2],
2971
... [-1 + I, -2 - 8*I, 2 + I]])
2972
sage: U, _, _ = A.SVD()
2973
sage: B=U*U.conjugate_transpose()
2974
sage: B.is_hermitian(algorithm='naive')
2975
True
2976
sage: B.is_hermitian(algorithm='naive', tol=1.0e-17) # random
2977
False
2978
sage: B.is_hermitian(algorithm='naive', tol=1.0e-15)
2979
True
2980
2981
A square, empty matrix is trivially Hermitian. ::
2982
2983
sage: A = matrix(RDF, 0, 0)
2984
sage: A.is_hermitian()
2985
True
2986
2987
Rectangular matrices are never Hermitian, no matter which
2988
algorithm is requested. ::
2989
2990
sage: A = matrix(CDF, 3, 4)
2991
sage: A.is_hermitian()
2992
False
2993
2994
TESTS:
2995
2996
The tolerance must be strictly positive. ::
2997
2998
sage: A = matrix(RDF, 2, range(4))
2999
sage: A.is_hermitian(tol = -3.1)
3000
Traceback (most recent call last):
3001
...
3002
ValueError: tolerance must be positive, not -3.1
3003
3004
The ``algorithm`` keyword gets checked. ::
3005
3006
sage: A = matrix(RDF, 2, range(4))
3007
sage: A.is_hermitian(algorithm='junk')
3008
Traceback (most recent call last):
3009
...
3010
ValueError: algorithm must be 'naive' or 'orthonormal', not junk
3011
3012
AUTHOR:
3013
3014
- Rob Beezer (2011-03-30)
3015
"""
3016
import sage.rings.complex_double
3017
global numpy
3018
tol = float(tol)
3019
if tol <= 0:
3020
raise ValueError('tolerance must be positive, not {0}'.format(tol))
3021
if not algorithm in ['naive', 'orthonormal']:
3022
raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm))
3023
3024
key = 'hermitian_{0}_{1}'.format(algorithm, tol)
3025
h = self.fetch(key)
3026
if not h is None:
3027
return h
3028
if not self.is_square():
3029
self.cache(key, False)
3030
return False
3031
if self._nrows == 0:
3032
self.cache(key, True)
3033
return True
3034
if numpy is None:
3035
import numpy
3036
cdef Py_ssize_t i, j
3037
cdef Matrix_double_dense T
3038
if algorithm == 'orthonormal':
3039
# Schur decomposition over CDF will be diagonal and real iff Hermitian
3040
_, T = self.schur(base_ring=sage.rings.complex_double.CDF)
3041
hermitian = T._is_lower_triangular(tol)
3042
if hermitian:
3043
for i in range(T._nrows):
3044
if numpy.absolute(numpy.imag(T.get_unsafe(i,i))) > tol:
3045
hermitian = False
3046
break
3047
elif algorithm == 'naive':
3048
hermitian = True
3049
for i in range(self._nrows):
3050
for j in range(i+1):
3051
if numpy.absolute(self.get_unsafe(i,j) - self.get_unsafe(j,i).conjugate()) > tol:
3052
hermitian = False
3053
break
3054
if not hermitian:
3055
break
3056
self.cache(key, hermitian)
3057
return hermitian
3058
3059
def is_normal(self, tol=1e-12, algorithm='orthonormal'):
3060
r"""
3061
Returns ``True`` if the matrix commutes with its conjugate-transpose.
3062
3063
INPUT:
3064
3065
- ``tol`` - default: ``1e-12`` - the largest value of the
3066
absolute value of the difference between two matrix entries
3067
for which they will still be considered equal.
3068
3069
- ``algorithm`` - default: 'orthonormal' - set to 'orthonormal'
3070
for a stable procedure and set to 'naive' for a fast
3071
procedure.
3072
3073
OUTPUT:
3074
3075
``True`` if the matrix is square and commutes with its
3076
conjugate-transpose, and ``False`` otherwise.
3077
3078
Normal matrices are precisely those that can be diagonalized
3079
by a unitary matrix.
3080
3081
The tolerance parameter is used to allow for numerical values
3082
to be equal if there is a slight difference due to round-off
3083
and other imprecisions.
3084
3085
The result is cached, on a per-tolerance and per-algorithm basis.
3086
3087
ALGORITHMS:
3088
3089
The naive algorithm simply compares entries of the two possible
3090
products of the matrix with its conjugate-transpose, with equality
3091
controlled by the tolerance parameter.
3092
3093
The orthonormal algorithm first computes a Schur decomposition
3094
(via the :meth:`schur` method) and checks that the result is a
3095
diagonal matrix. An orthonormal diagonalization
3096
is equivalent to being normal.
3097
3098
So the naive algorithm can finish fairly quickly for a matrix
3099
that is not normal, once the products have been computed.
3100
However, the orthonormal algorithm will compute a Schur
3101
decomposition before going through a similar check of a
3102
matrix entry-by-entry.
3103
3104
EXAMPLES:
3105
3106
First over the complexes. ``B`` is Hermitian, hence normal. ::
3107
3108
sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],
3109
... [-3 - I, -4*I, -2],
3110
... [-1 + I, -2 - 8*I, 2 + I]])
3111
sage: B = A*A.conjugate_transpose()
3112
sage: B.is_hermitian()
3113
True
3114
sage: B.is_normal(algorithm='orthonormal')
3115
True
3116
sage: B.is_normal(algorithm='naive')
3117
True
3118
sage: B[0,0] = I
3119
sage: B.is_normal(algorithm='orthonormal')
3120
False
3121
sage: B.is_normal(algorithm='naive')
3122
False
3123
3124
Now over the reals. Circulant matrices are normal. ::
3125
3126
sage: G = graphs.CirculantGraph(20, [3, 7])
3127
sage: D = digraphs.Circuit(20)
3128
sage: A = 3*D.adjacency_matrix() - 5*G.adjacency_matrix()
3129
sage: A = A.change_ring(RDF)
3130
sage: A.is_normal()
3131
True
3132
sage: A.is_normal(algorithm = 'naive')
3133
True
3134
sage: A[19,0] = 4.0
3135
sage: A.is_normal()
3136
False
3137
sage: A.is_normal(algorithm = 'naive')
3138
False
3139
3140
Skew-Hermitian matrices are normal. ::
3141
3142
sage: A = matrix(CDF, [[ 1 + I, 1 - 6*I, -1 - I],
3143
... [-3 - I, -4*I, -2],
3144
... [-1 + I, -2 - 8*I, 2 + I]])
3145
sage: B = A - A.conjugate_transpose()
3146
sage: B.is_hermitian()
3147
False
3148
sage: B.is_normal()
3149
True
3150
sage: B.is_normal(algorithm='naive')
3151
True
3152
3153
A small matrix that does not fit into any of the usual categories
3154
of normal matrices. ::
3155
3156
sage: A = matrix(RDF, [[1, -1],
3157
... [1, 1]])
3158
sage: A.is_normal()
3159
True
3160
sage: not A.is_hermitian() and not A.is_skew_symmetric()
3161
True
3162
3163
Sage has several fields besides the entire complex numbers
3164
where conjugation is non-trivial. ::
3165
3166
sage: F.<b> = QuadraticField(-7)
3167
sage: C = matrix(F, [[-2*b - 3, 7*b - 6, -b + 3],
3168
... [-2*b - 3, -3*b + 2, -2*b],
3169
... [ b + 1, 0, -2]])
3170
sage: C = C*C.conjugate_transpose()
3171
sage: C.is_normal()
3172
True
3173
3174
A square, empty matrix is trivially normal. ::
3175
3176
sage: A = matrix(CDF, 0, 0)
3177
sage: A.is_normal()
3178
True
3179
3180
Rectangular matrices are never normal, no matter which
3181
algorithm is requested. ::
3182
3183
sage: A = matrix(CDF, 3, 4)
3184
sage: A.is_normal()
3185
False
3186
3187
TESTS:
3188
3189
The tolerance must be strictly positive. ::
3190
3191
sage: A = matrix(RDF, 2, range(4))
3192
sage: A.is_normal(tol = -3.1)
3193
Traceback (most recent call last):
3194
...
3195
ValueError: tolerance must be positive, not -3.1
3196
3197
The ``algorithm`` keyword gets checked. ::
3198
3199
sage: A = matrix(RDF, 2, range(4))
3200
sage: A.is_normal(algorithm='junk')
3201
Traceback (most recent call last):
3202
...
3203
ValueError: algorithm must be 'naive' or 'orthonormal', not junk
3204
3205
AUTHOR:
3206
3207
- Rob Beezer (2011-03-31)
3208
"""
3209
import sage.rings.complex_double
3210
global numpy
3211
tol = float(tol)
3212
if tol <= 0:
3213
raise ValueError('tolerance must be positive, not {0}'.format(tol))
3214
if not algorithm in ['naive', 'orthonormal']:
3215
raise ValueError("algorithm must be 'naive' or 'orthonormal', not {0}".format(algorithm))
3216
3217
key = 'normal_{0}_{1}'.format(algorithm, tol)
3218
b = self.fetch(key)
3219
if not b is None:
3220
return b
3221
if not self.is_square():
3222
self.cache(key, False)
3223
return False
3224
if self._nrows == 0:
3225
self.cache(key, True)
3226
return True
3227
cdef Py_ssize_t i, j
3228
cdef Matrix_double_dense T, left, right
3229
if algorithm == 'orthonormal':
3230
# Schur decomposition over CDF will be diagonal iff normal
3231
_, T = self.schur(base_ring=sage.rings.complex_double.CDF)
3232
normal = T._is_lower_triangular(tol)
3233
elif algorithm == 'naive':
3234
if numpy is None:
3235
import numpy
3236
CT = self.conjugate_transpose()
3237
left = self*CT
3238
right = CT*self
3239
normal = True
3240
# two products are Hermitian, need only check lower triangle
3241
for i in range(self._nrows):
3242
for j in range(i+1):
3243
if numpy.absolute(left.get_unsafe(i,j) - right.get_unsafe(i,j)) > tol:
3244
normal = False
3245
break
3246
if not normal:
3247
break
3248
self.cache(key, normal)
3249
return normal
3250
3251
def schur(self, base_ring=None):
3252
r"""
3253
Returns the Schur decomposition of the matrix.
3254
3255
INPUT:
3256
3257
- ``base_ring`` - optional, defaults to the base ring of ``self``.
3258
Use this to request the base ring of the returned matrices, which
3259
will affect the format of the results.
3260
3261
OUTPUT:
3262
3263
A pair of immutable matrices. The first is a unitary matrix `Q`.
3264
The second, `T`, is upper-triangular when returned over the complex
3265
numbers, while it is almost upper-triangular over the reals. In the
3266
latter case, there can be some `2\times 2` blocks on the diagonal
3267
which represent a pair of conjugate complex eigenvalues of ``self``.
3268
3269
If ``self`` is the matrix `A`, then
3270
3271
.. math::
3272
3273
A = QT({\overline Q})^t
3274
3275
where the latter matrix is the conjugate-transpose of ``Q``, which
3276
is also the inverse of ``Q``, since ``Q`` is unitary.
3277
3278
Note that in the case of a normal matrix (Hermitian, symmetric, and
3279
others), the upper-triangular matrix is a diagonal matrix with
3280
eigenvalues of ``self`` on the diagonal, and the unitary matrix
3281
has columns that form an orthonormal basis composed of eigenvectors
3282
of ``self``. This is known as "orthonormal diagonalization".
3283
3284
.. WARNING::
3285
3286
The Schur decomposition is not unique, as there may be numerous
3287
choices for the vectors of the orthonormal basis, and consequently
3288
different possibilities for the upper-triangular matrix. However,
3289
the diagonal of the upper-triangular matrix will always contain the
3290
eigenvalues of the matrix (in the complex version), or `2\times 2`
3291
block matrices in the real version representing pairs of conjugate
3292
complex eigenvalues.
3293
3294
In particular, results may vary across systems and processors.
3295
3296
EXAMPLES:
3297
3298
First over the complexes. The similar matrix is always
3299
upper-triangular in this case. ::
3300
3301
sage: A = matrix(CDF, 4, 4, range(16)) + matrix(CDF, 4, 4, [x^3*I for x in range(0, 16)])
3302
sage: Q, T = A.schur()
3303
sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)
3304
[1.0 0.0 0.0 0.0]
3305
[0.0 1.0 0.0 0.0]
3306
[0.0 0.0 1.0 0.0]
3307
[0.0 0.0 0.0 1.0]
3308
sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i)])
3309
True
3310
sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)
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]
3314
[0.0 0.0 0.0 0.0]
3315
sage: eigenvalues = [T[i,i] for i in range(4)]; eigenvalues
3316
[30.733... + 4648.541...*I, -0.184... - 159.057...*I, -0.523... + 11.158...*I, -0.025... - 0.642...*I]
3317
sage: A.eigenvalues()
3318
[30.733... + 4648.541...*I, -0.184... - 159.057...*I, -0.523... + 11.158...*I, -0.025... - 0.642...*I]
3319
sage: abs(A.norm()-T.norm()) < 1e-10
3320
True
3321
3322
We begin with a real matrix but ask for a decomposition over the
3323
complexes. The result will yield an upper-triangular matrix over
3324
the complex numbers for ``T``. ::
3325
3326
sage: A = matrix(RDF, 4, 4, [x^3 for x in range(16)])
3327
sage: Q, T = A.schur(base_ring=CDF)
3328
sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)
3329
[1.0 0.0 0.0 0.0]
3330
[0.0 1.0 0.0 0.0]
3331
[0.0 0.0 1.0 0.0]
3332
[0.0 0.0 0.0 1.0]
3333
sage: T.parent()
3334
Full MatrixSpace of 4 by 4 dense matrices over Complex Double Field
3335
sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i)])
3336
True
3337
sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)
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]
3341
[0.0 0.0 0.0 0.0]
3342
3343
Now totally over the reals. But with complex eigenvalues, the
3344
similar matrix may not be upper-triangular. But "at worst" there
3345
may be some `2\times 2` blocks on the diagonal which represent
3346
a pair of conjugate complex eigenvalues. These blocks will then
3347
just interrupt the zeros below the main diagonal. This example
3348
has a pair of these of the blocks. ::
3349
3350
sage: A = matrix(RDF, 4, 4, [[1, 0, -3, -1],
3351
... [4, -16, -7, 0],
3352
... [1, 21, 1, -2],
3353
... [26, -1, -2, 1]])
3354
sage: Q, T = A.schur()
3355
sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)
3356
[1.0 0.0 0.0 0.0]
3357
[0.0 1.0 0.0 0.0]
3358
[0.0 0.0 1.0 0.0]
3359
[0.0 0.0 0.0 1.0]
3360
sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i)])
3361
False
3362
sage: all([T.zero_at(1.0e-12)[i,j] == 0 for i in range(4) for j in range(i-1)])
3363
True
3364
sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)
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]
3368
[0.0 0.0 0.0 0.0]
3369
sage: sorted(T[0:2,0:2].eigenvalues() + T[2:4,2:4].eigenvalues())
3370
[-5.710... - 8.382...*I, -5.710... + 8.382...*I, -0.789... - 2.336...*I, -0.789... + 2.336...*I]
3371
sage: sorted(A.eigenvalues())
3372
[-5.710... - 8.382...*I, -5.710... + 8.382...*I, -0.789... - 2.336...*I, -0.789... + 2.336...*I]
3373
sage: abs(A.norm()-T.norm()) < 1e-12
3374
True
3375
3376
Starting with complex numbers and requesting a result over the reals
3377
will never happen. ::
3378
3379
sage: A = matrix(CDF, 2, 2, [[2+I, -1+3*I], [5-4*I, 2-7*I]])
3380
sage: A.schur(base_ring=RDF)
3381
Traceback (most recent call last):
3382
...
3383
TypeError: unable to convert input matrix over CDF to a matrix over RDF
3384
3385
If theory predicts your matrix is real, but it contains some
3386
very small imaginary parts, you can specify the cutoff for "small"
3387
imaginary parts, then request the output as real matrices, and let
3388
the routine do the rest. ::
3389
3390
sage: A = matrix(RDF, 2, 2, [1, 1, -1, 0]) + matrix(CDF, 2, 2, [1.0e-14*I]*4)
3391
sage: B = A.zero_at(1.0e-12)
3392
sage: B.parent()
3393
Full MatrixSpace of 2 by 2 dense matrices over Complex Double Field
3394
sage: Q, T = B.schur(RDF)
3395
sage: Q.parent()
3396
Full MatrixSpace of 2 by 2 dense matrices over Real Double Field
3397
sage: T.parent()
3398
Full MatrixSpace of 2 by 2 dense matrices over Real Double Field
3399
sage: Q.round(6)
3400
[ 0.707107 0.707107]
3401
[-0.707107 0.707107]
3402
sage: T.round(6)
3403
[ 0.5 1.5]
3404
[-0.5 0.5]
3405
sage: (Q*T*Q.conjugate().transpose()-B).zero_at(1.0e-11)
3406
[0.0 0.0]
3407
[0.0 0.0]
3408
3409
A Hermitian matrix has real eigenvalues, so the similar matrix
3410
will be upper-triangular. Furthermore, a Hermitian matrix is
3411
diagonalizable with respect to an orthonormal basis, composed
3412
of eigenvectors of the matrix. Here that basis is the set of
3413
columns of the unitary matrix. ::
3414
3415
sage: A = matrix(CDF, [[ 52, -9*I - 8, 6*I - 187, -188*I + 2],
3416
... [ 9*I - 8, 12, -58*I + 59, 30*I + 42],
3417
... [-6*I - 187, 58*I + 59, 2677, 2264*I + 65],
3418
... [ 188*I + 2, -30*I + 42, -2264*I + 65, 2080]])
3419
sage: Q, T = A.schur()
3420
sage: T = T.zero_at(1.0e-12).change_ring(RDF)
3421
sage: T.round(6)
3422
[4680.13301 0.0 0.0 0.0]
3423
[ 0.0 102.715967 0.0 0.0]
3424
[ 0.0 0.0 35.039344 0.0]
3425
[ 0.0 0.0 0.0 3.11168]
3426
sage: (Q*Q.conjugate().transpose()).zero_at(1.0e-12)
3427
[1.0 0.0 0.0 0.0]
3428
[0.0 1.0 0.0 0.0]
3429
[0.0 0.0 1.0 0.0]
3430
[0.0 0.0 0.0 1.0]
3431
sage: (Q*T*Q.conjugate().transpose()-A).zero_at(1.0e-11)
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]
3435
[0.0 0.0 0.0 0.0]
3436
3437
Similarly, a real symmetric matrix has only real eigenvalues,
3438
and there is an orthonormal basis composed of eigenvectors of
3439
the matrix. ::
3440
3441
sage: A = matrix(RDF, [[ 1, -2, 5, -3],
3442
... [-2, 9, 1, 5],
3443
... [ 5, 1, 3 , 7],
3444
... [-3, 5, 7, -8]])
3445
sage: Q, T = A.schur()
3446
sage: Q.round(4)
3447
[-0.3027 -0.751 0.576 -0.1121]
3448
[ 0.139 -0.3892 -0.2648 0.8713]
3449
[ 0.4361 0.359 0.7599 0.3217]
3450
[ -0.836 0.3945 0.1438 0.3533]
3451
sage: T = T.zero_at(10^-12)
3452
sage: all(abs(e) < 10^-4 for e in (T - diagonal_matrix(RDF, [-13.5698, -0.8508, 7.7664, 11.6542])).list())
3453
True
3454
sage: (Q*Q.transpose()).zero_at(1.0e-12)
3455
[1.0 0.0 0.0 0.0]
3456
[0.0 1.0 0.0 0.0]
3457
[0.0 0.0 1.0 0.0]
3458
[0.0 0.0 0.0 1.0]
3459
sage: (Q*T*Q.transpose()-A).zero_at(1.0e-11)
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]
3463
[0.0 0.0 0.0 0.0]
3464
3465
The results are cached, both as a real factorization and also as a
3466
complex factorization. This means the returned matrices are
3467
immutable. ::
3468
3469
sage: A = matrix(RDF, 2, 2, [[0, -1], [1, 0]])
3470
sage: Qr, Tr = A.schur(base_ring=RDF)
3471
sage: Qc, Tc = A.schur(base_ring=CDF)
3472
sage: all([M.is_immutable() for M in [Qr, Tr, Qc, Tc]])
3473
True
3474
sage: Tr.round(6) != Tc.round(6)
3475
True
3476
3477
TESTS:
3478
3479
The Schur factorization is only defined for square matrices. ::
3480
3481
sage: A = matrix(RDF, 4, 5, range(20))
3482
sage: A.schur()
3483
Traceback (most recent call last):
3484
...
3485
ValueError: Schur decomposition requires a square matrix, not a 4 x 5 matrix
3486
3487
A base ring request is checked. ::
3488
3489
sage: A = matrix(RDF, 3, range(9))
3490
sage: A.schur(base_ring=QQ)
3491
Traceback (most recent call last):
3492
...
3493
ValueError: base ring of Schur decomposition matrices must be RDF or CDF, not Rational Field
3494
3495
AUTHOR:
3496
3497
- Rob Beezer (2011-03-31)
3498
"""
3499
global scipy
3500
from sage.rings.real_double import RDF
3501
from sage.rings.complex_double import CDF
3502
3503
cdef Matrix_double_dense Q, T
3504
3505
if not self.is_square():
3506
raise ValueError('Schur decomposition requires a square matrix, not a {0} x {1} matrix'.format(self.nrows(), self.ncols()))
3507
if base_ring == None:
3508
base_ring = self.base_ring()
3509
if not base_ring in [RDF, CDF]:
3510
raise ValueError('base ring of Schur decomposition matrices must be RDF or CDF, not {0}'.format(base_ring))
3511
3512
if self.base_ring() != base_ring:
3513
try:
3514
self = self.change_ring(base_ring)
3515
except TypeError:
3516
raise TypeError('unable to convert input matrix over CDF to a matrix over RDF')
3517
if base_ring == CDF:
3518
format = 'complex'
3519
else:
3520
format = 'real'
3521
3522
schur = self.fetch('schur_factors_' + format)
3523
if not schur is None:
3524
return schur
3525
if scipy is None:
3526
import scipy
3527
import scipy.linalg
3528
Q = self._new(self._nrows, self._nrows)
3529
T = self._new(self._nrows, self._nrows)
3530
T._matrix_numpy, Q._matrix_numpy = scipy.linalg.schur(self._matrix_numpy, output=format)
3531
Q.set_immutable()
3532
T.set_immutable()
3533
# Our return order is the reverse of NumPy's
3534
schur = (Q, T)
3535
self.cache('schur_factors_' + format, schur)
3536
return schur
3537
3538
def cholesky(self):
3539
r"""
3540
Returns the Cholesky factorization of a matrix that
3541
is real symmetric, or complex Hermitian.
3542
3543
INPUT:
3544
3545
Any square matrix with entries from ``RDF`` that is symmetric, or
3546
with entries from ``CDF`` that is Hermitian. The matrix must
3547
be positive definite for the Cholesky decomposition to exist.
3548
3549
OUTPUT:
3550
3551
For a matrix `A` the routine returns a lower triangular
3552
matrix `L` such that,
3553
3554
.. math::
3555
3556
A = LL^\ast
3557
3558
where `L^\ast` is the conjugate-transpose in the complex case,
3559
and just the transpose in the real case. If the matrix fails
3560
to be positive definite (perhaps because it is not symmetric
3561
or Hermitian), then this function raises a ``ValueError``.
3562
3563
IMPLEMENTATION:
3564
3565
The existence of a Cholesky decomposition and the
3566
positive definite property are equivalent. So this
3567
method and the :meth:`is_positive_definite` method compute and
3568
cache both the Cholesky decomposition and the
3569
positive-definiteness. So the :meth:`is_positive_definite`
3570
method or catching a ``ValueError`` from the :meth:`cholesky`
3571
method are equally expensive computationally and if the
3572
decomposition exists, it is cached as a side-effect of either
3573
routine.
3574
3575
EXAMPLES:
3576
3577
A real matrix that is symmetric and positive definite. ::
3578
3579
sage: M = matrix(RDF,[[ 1, 1, 1, 1, 1],
3580
... [ 1, 5, 31, 121, 341],
3581
... [ 1, 31, 341, 1555, 4681],
3582
... [ 1,121, 1555, 7381, 22621],
3583
... [ 1,341, 4681, 22621, 69905]])
3584
sage: M.is_symmetric()
3585
True
3586
sage: L = M.cholesky()
3587
sage: L.round(6).zero_at(10^-10)
3588
[ 1.0 0.0 0.0 0.0 0.0]
3589
[ 1.0 2.0 0.0 0.0 0.0]
3590
[ 1.0 15.0 10.723805 0.0 0.0]
3591
[ 1.0 60.0 60.985814 7.792973 0.0]
3592
[ 1.0 170.0 198.623524 39.366567 1.7231]
3593
sage: (L*L.transpose()).round(6).zero_at(10^-10)
3594
[ 1.0 1.0 1.0 1.0 1.0]
3595
[ 1.0 5.0 31.0 121.0 341.0]
3596
[ 1.0 31.0 341.0 1555.0 4681.0]
3597
[ 1.0 121.0 1555.0 7381.0 22621.0]
3598
[ 1.0 341.0 4681.0 22621.0 69905.0]
3599
3600
A complex matrix that is Hermitian and positive definite. ::
3601
3602
sage: A = matrix(CDF, [[ 23, 17*I + 3, 24*I + 25, 21*I],
3603
... [ -17*I + 3, 38, -69*I + 89, 7*I + 15],
3604
... [-24*I + 25, 69*I + 89, 976, 24*I + 6],
3605
... [ -21*I, -7*I + 15, -24*I + 6, 28]])
3606
sage: A.is_hermitian()
3607
True
3608
sage: L = A.cholesky()
3609
sage: L.round(6).zero_at(10^-10)
3610
[ 4.795832 0.0 0.0 0.0]
3611
[ 0.625543 - 3.544745*I 5.004346 0.0 0.0]
3612
[ 5.21286 - 5.004346*I 13.588189 + 10.721116*I 24.984023 0.0]
3613
[ -4.378803*I -0.104257 - 0.851434*I -0.21486 + 0.371348*I 2.811799]
3614
sage: (L*L.conjugate_transpose()).round(6).zero_at(10^-10)
3615
[ 23.0 3.0 + 17.0*I 25.0 + 24.0*I 21.0*I]
3616
[ 3.0 - 17.0*I 38.0 89.0 - 69.0*I 15.0 + 7.0*I]
3617
[25.0 - 24.0*I 89.0 + 69.0*I 976.0 6.0 + 24.0*I]
3618
[ -21.0*I 15.0 - 7.0*I 6.0 - 24.0*I 28.0]
3619
3620
This routine will recognize when the input matrix is not
3621
positive definite. The negative eigenvalues are an
3622
equivalent indicator. (Eigenvalues of a Hermitian matrix
3623
must be real, so there is no loss in ignoring the imprecise
3624
imaginary parts). ::
3625
3626
sage: A = matrix(RDF, [[ 3, -6, 9, 6, -9],
3627
... [-6, 11, -16, -11, 17],
3628
... [ 9, -16, 28, 16, -40],
3629
... [ 6, -11, 16, 9, -19],
3630
... [-9, 17, -40, -19, 68]])
3631
sage: A.is_symmetric()
3632
True
3633
sage: A.eigenvalues()
3634
[108.07..., 13.02..., -0.02..., -0.70..., -1.37...]
3635
sage: A.cholesky()
3636
Traceback (most recent call last):
3637
...
3638
ValueError: matrix is not positive definite
3639
3640
sage: B = matrix(CDF, [[ 2, 4 - 2*I, 2 + 2*I],
3641
... [4 + 2*I, 8, 10*I],
3642
... [2 - 2*I, -10*I, -3]])
3643
sage: B.is_hermitian()
3644
True
3645
sage: [ev.real() for ev in B.eigenvalues()]
3646
[15.88..., 0.08..., -8.97...]
3647
sage: B.cholesky()
3648
Traceback (most recent call last):
3649
...
3650
ValueError: matrix is not positive definite
3651
3652
TESTS:
3653
3654
A trivial case. ::
3655
3656
sage: A = matrix(RDF, 0, [])
3657
sage: A.cholesky()
3658
[]
3659
3660
The Cholesky factorization is only defined for square matrices. ::
3661
3662
sage: A = matrix(RDF, 4, 5, range(20))
3663
sage: A.cholesky()
3664
Traceback (most recent call last):
3665
...
3666
ValueError: Cholesky decomposition requires a square matrix, not a 4 x 5 matrix
3667
"""
3668
from sage.rings.real_double import RDF
3669
from sage.rings.complex_double import CDF
3670
3671
cdef Matrix_double_dense L
3672
cache_cholesky = 'cholesky'
3673
cache_posdef = 'positive_definite'
3674
3675
if not self.is_square():
3676
msg = "Cholesky decomposition requires a square matrix, not a {0} x {1} matrix"
3677
self.cache(cache_posdef, False)
3678
raise ValueError(msg.format(self.nrows(), self.ncols()))
3679
if self._nrows == 0: # special case
3680
self.cache(cache_posdef, True)
3681
return self.__copy__()
3682
3683
L = self.fetch(cache_cholesky)
3684
if L is None:
3685
L = self._new()
3686
global scipy
3687
if scipy is None:
3688
import scipy
3689
import scipy.linalg
3690
from numpy.linalg import LinAlgError
3691
try:
3692
L._matrix_numpy = scipy.linalg.cholesky(self._matrix_numpy, lower=1)
3693
except LinAlgError:
3694
self.cache(cache_posdef, False)
3695
raise ValueError("matrix is not positive definite")
3696
L.set_immutable()
3697
self.cache(cache_cholesky, L)
3698
self.cache(cache_posdef, True)
3699
return L
3700
3701
def is_positive_definite(self):
3702
r"""
3703
Determines if a matrix is positive definite.
3704
3705
A matrix `A` is positive definite if it is square,
3706
is Hermitian (which reduces to symmetric in the real case),
3707
and for every nonzero vector `\vec{x}`,
3708
3709
.. math::
3710
3711
\vec{x}^\ast A \vec{x} > 0
3712
3713
where `\vec{x}^\ast` is the conjugate-transpose in the
3714
complex case and just the transpose in the real case.
3715
Equivalently, a positive definite matrix has only positive
3716
eigenvalues and only positive determinants of leading
3717
principal submatrices.
3718
3719
INPUT:
3720
3721
Any matrix over ``RDF`` or ``CDF``.
3722
3723
OUTPUT:
3724
3725
``True`` if and only if the matrix is square, Hermitian,
3726
and meets the condition above on the quadratic form.
3727
The result is cached.
3728
3729
IMPLEMENTATION:
3730
3731
The existence of a Cholesky decomposition and the
3732
positive definite property are equivalent. So this
3733
method and the :meth:`cholesky` method compute and
3734
cache both the Cholesky decomposition and the
3735
positive-definiteness. So the :meth:`is_positive_definite`
3736
method or catching a ``ValueError`` from the :meth:`cholesky`
3737
method are equally expensive computationally and if the
3738
decomposition exists, it is cached as a side-effect of either
3739
routine.
3740
3741
EXAMPLES:
3742
3743
A matrix over ``RDF`` that is positive definite. ::
3744
3745
sage: M = matrix(RDF,[[ 1, 1, 1, 1, 1],
3746
... [ 1, 5, 31, 121, 341],
3747
... [ 1, 31, 341, 1555, 4681],
3748
... [ 1,121, 1555, 7381, 22621],
3749
... [ 1,341, 4681, 22621, 69905]])
3750
sage: M.is_symmetric()
3751
True
3752
sage: M.eigenvalues()
3753
[77547.66..., 82.44..., 2.41..., 0.46..., 0.011...]
3754
sage: [round(M[:i,:i].determinant()) for i in range(1, M.nrows()+1)]
3755
[1, 4, 460, 27936, 82944]
3756
sage: M.is_positive_definite()
3757
True
3758
3759
A matrix over ``CDF`` that is positive definite. ::
3760
3761
sage: C = matrix(CDF, [[ 23, 17*I + 3, 24*I + 25, 21*I],
3762
... [ -17*I + 3, 38, -69*I + 89, 7*I + 15],
3763
... [-24*I + 25, 69*I + 89, 976, 24*I + 6],
3764
... [ -21*I, -7*I + 15, -24*I + 6, 28]])
3765
sage: C.is_hermitian()
3766
True
3767
sage: [x.real() for x in C.eigenvalues()]
3768
[991.46..., 55.96..., 3.69..., 13.87...]
3769
sage: [round(C[:i,:i].determinant().real()) for i in range(1, C.nrows()+1)]
3770
[23, 576, 359540, 2842600]
3771
sage: C.is_positive_definite()
3772
True
3773
3774
A matrix over ``RDF`` that is not positive definite. ::
3775
3776
sage: A = matrix(RDF, [[ 3, -6, 9, 6, -9],
3777
... [-6, 11, -16, -11, 17],
3778
... [ 9, -16, 28, 16, -40],
3779
... [ 6, -11, 16, 9, -19],
3780
... [-9, 17, -40, -19, 68]])
3781
sage: A.is_symmetric()
3782
True
3783
sage: A.eigenvalues()
3784
[108.07..., 13.02..., -0.02..., -0.70..., -1.37...]
3785
sage: [round(A[:i,:i].determinant()) for i in range(1, A.nrows()+1)]
3786
[3, -3, -15, 30, -30]
3787
sage: A.is_positive_definite()
3788
False
3789
3790
A matrix over ``CDF`` that is not positive definite. ::
3791
3792
sage: B = matrix(CDF, [[ 2, 4 - 2*I, 2 + 2*I],
3793
... [4 + 2*I, 8, 10*I],
3794
... [2 - 2*I, -10*I, -3]])
3795
sage: B.is_hermitian()
3796
True
3797
sage: [ev.real() for ev in B.eigenvalues()]
3798
[15.88..., 0.08..., -8.97...]
3799
sage: [round(B[:i,:i].determinant().real()) for i in range(1, B.nrows()+1)]
3800
[2, -4, -12]
3801
sage: B.is_positive_definite()
3802
False
3803
3804
A large random matrix that is guaranteed by theory to be
3805
positive definite. ::
3806
3807
sage: R = random_matrix(CDF, 200)
3808
sage: H = R.conjugate_transpose()*R
3809
sage: H.is_positive_definite()
3810
True
3811
3812
TESTS:
3813
3814
A trivially small case. ::
3815
3816
sage: S = matrix(CDF, [])
3817
sage: S.nrows(), S.ncols()
3818
(0, 0)
3819
sage: S.is_positive_definite()
3820
True
3821
3822
A rectangular matrix will never be positive definite. ::
3823
3824
sage: R = matrix(RDF, 2, 3, range(6))
3825
sage: R.is_positive_definite()
3826
False
3827
3828
A non-Hermitian matrix will never be positive definite. ::
3829
3830
sage: T = matrix(CDF, 8, 8, range(64))
3831
sage: T.is_positive_definite()
3832
False
3833
3834
AUTHOR:
3835
3836
- Rob Beezer (2012-05-28)
3837
"""
3838
cache_str = 'positive_definite'
3839
posdef = self.fetch(cache_str)
3840
if posdef is None:
3841
try:
3842
self.cholesky()
3843
except ValueError:
3844
pass
3845
posdef = self.fetch(cache_str)
3846
return posdef
3847
3848
cdef Vector _vector_times_matrix_(self,Vector v):
3849
if self._nrows == 0 or self._ncols == 0:
3850
return self._row_ambient_module().zero_vector()
3851
global numpy
3852
if numpy is None:
3853
import numpy
3854
3855
v_numpy = numpy.array([self._python_dtype(i) for i in v])
3856
3857
M = self._row_ambient_module()
3858
ans = numpy.dot(v_numpy,self._matrix_numpy)
3859
return M(ans)
3860
3861
cdef Vector _matrix_times_vector_(self,Vector v):
3862
if self._nrows == 0 or self._ncols == 0:
3863
return self._column_ambient_module().zero_vector()
3864
3865
global numpy
3866
if numpy is None:
3867
import numpy
3868
3869
v_numpy = numpy.array([self._python_dtype(i) for i in v], dtype=self._numpy_dtype)
3870
3871
M = self._column_ambient_module()
3872
ans = numpy.dot(self._matrix_numpy, v_numpy)
3873
return M(ans)
3874
3875
def numpy(self, dtype=None):
3876
"""
3877
This method returns a copy of the matrix as a numpy array. It
3878
uses the numpy C/api so is very fast.
3879
3880
INPUT:
3881
3882
- ``dtype`` - The desired data-type for the array. If not given,
3883
then the type will be determined as the minimum type required
3884
to hold the objects in the sequence.
3885
3886
EXAMPLES::
3887
3888
sage: m = matrix(RDF,[[1,2],[3,4]])
3889
sage: n = m.numpy()
3890
sage: import numpy
3891
sage: numpy.linalg.eig(n)
3892
(array([-0.37228132, 5.37228132]), array([[-0.82456484, -0.41597356],
3893
[ 0.56576746, -0.90937671]]))
3894
sage: m = matrix(RDF, 2, range(6)); m
3895
[0.0 1.0 2.0]
3896
[3.0 4.0 5.0]
3897
sage: m.numpy()
3898
array([[ 0., 1., 2.],
3899
[ 3., 4., 5.]])
3900
3901
Alternatively, numpy automatically calls this function (via
3902
the magic :meth:`__array__` method) to convert Sage matrices
3903
to numpy arrays::
3904
3905
sage: import numpy
3906
sage: m = matrix(RDF, 2, range(6)); m
3907
[0.0 1.0 2.0]
3908
[3.0 4.0 5.0]
3909
sage: numpy.array(m)
3910
array([[ 0., 1., 2.],
3911
[ 3., 4., 5.]])
3912
sage: numpy.array(m).dtype
3913
dtype('float64')
3914
sage: m = matrix(CDF, 2, range(6)); m
3915
[0.0 1.0 2.0]
3916
[3.0 4.0 5.0]
3917
sage: numpy.array(m)
3918
array([[ 0.+0.j, 1.+0.j, 2.+0.j],
3919
[ 3.+0.j, 4.+0.j, 5.+0.j]])
3920
sage: numpy.array(m).dtype
3921
dtype('complex128')
3922
3923
TESTS::
3924
3925
sage: m = matrix(RDF,0,5,[]); m
3926
[]
3927
sage: m.numpy()
3928
array([], shape=(0, 5), dtype=float64)
3929
sage: m = matrix(RDF,5,0,[]); m
3930
[]
3931
sage: m.numpy()
3932
array([], shape=(5, 0), dtype=float64)
3933
"""
3934
import numpy as np
3935
if dtype is None or self._numpy_dtype == np.dtype(dtype):
3936
return self._matrix_numpy.copy()
3937
else:
3938
return matrix_dense.Matrix_dense.numpy(self, dtype=dtype)
3939
3940
def _replace_self_with_numpy(self,numpy_matrix):
3941
"""
3942
3943
EXAMPLES::
3944
3945
sage: import numpy
3946
sage: a = numpy.array([[1,2],[3,4]], 'float64')
3947
sage: m = matrix(RDF,2,2,0)
3948
sage: m._replace_self_with_numpy(a)
3949
sage: m
3950
[1.0 2.0]
3951
[3.0 4.0]
3952
"""
3953
if (<object>self._matrix_numpy).shape != (<object>numpy_matrix).shape:
3954
raise ValueError("matrix shapes are not the same")
3955
self._matrix_numpy = numpy_matrix.astype(self._numpy_dtype)
3956
3957
3958
def _replace_self_with_numpy32(self,numpy_matrix):
3959
"""
3960
3961
EXAMPLES::
3962
3963
sage: import numpy
3964
sage: a = numpy.array([[1,2],[3,4]], 'float32')
3965
sage: m = matrix(RDF,2,2,0)
3966
sage: m._replace_self_with_numpy32(a)
3967
sage: m
3968
[1.0 2.0]
3969
[3.0 4.0]
3970
"""
3971
#TODO find where this is used and change it
3972
self._replace_self_with_numpy(numpy_matrix)
3973
3974
3975
def _hadamard_row_bound(self):
3976
r"""
3977
Return an integer n such that the absolute value of the
3978
determinant of this matrix is at most $10^n$.
3979
3980
EXAMPLES::
3981
3982
sage: a = matrix(RDF, 3, [1,2,5,7,-3,4,2,1,123])
3983
sage: a._hadamard_row_bound()
3984
4
3985
sage: a.det()
3986
-2014.0
3987
sage: 10^4
3988
10000
3989
"""
3990
cdef double d = 0, s
3991
cdef Py_ssize_t i, j
3992
for i from 0 <= i < self._nrows:
3993
s = 0
3994
for j from 0 <= j < self._ncols:
3995
s += self.get_unsafe(i,j)**2
3996
d += math.log(s)
3997
d /= 2
3998
return int(math.ceil(d / math.log(10)))
3999
4000
@rename_keyword(deprecation=6094, method="algorithm")
4001
def exp(self, algorithm='pade', order=None):
4002
r"""
4003
Calculate the exponential of this matrix X, which is the matrix
4004
4005
.. math::
4006
4007
e^X = \sum_{k=0}^{\infty} \frac{X^k}{k!}.
4008
4009
INPUT:
4010
4011
- algorithm -- 'pade', 'eig', or 'taylor'; the algorithm used to
4012
compute the exponential.
4013
4014
- order -- for the Taylor series algorithm, this specifies the
4015
order of the Taylor series used. This is ignored for the
4016
other algorithms. The current default (from scipy) is 20.
4017
4018
EXAMPLES::
4019
4020
sage: A=matrix(RDF, 2, [1,2,3,4]); A
4021
[1.0 2.0]
4022
[3.0 4.0]
4023
sage: A.exp()
4024
[51.9689561987 74.736564567]
4025
[112.104846851 164.073803049]
4026
sage: A.exp(algorithm='eig')
4027
[51.9689561987 74.736564567]
4028
[112.104846851 164.073803049]
4029
sage: A.exp(algorithm='taylor', order=5)
4030
[19.9583333333 28.0833333333]
4031
[ 42.125 62.0833333333]
4032
sage: A.exp(algorithm='taylor')
4033
[51.9689035511 74.7364878369]
4034
[112.104731755 164.073635306]
4035
4036
sage: A=matrix(CDF, 2, [1,2+I,3*I,4]); A
4037
[ 1.0 2.0 + 1.0*I]
4038
[ 3.0*I 4.0]
4039
sage: A.exp()
4040
[-19.6146029538 + 12.5177438468*I 3.79496364496 + 28.8837993066*I]
4041
[-32.3835809809 + 21.8842359579*I 2.26963300409 + 44.9013248277*I]
4042
sage: A.exp(algorithm='eig')
4043
[-19.6146029538 + 12.5177438468*I 3.79496364496 + 28.8837993066*I]
4044
[-32.3835809809 + 21.8842359579*I 2.26963300409 + 44.9013248277*I]
4045
sage: A.exp(algorithm='taylor', order=5)
4046
[ -6.29166666667 + 14.25*I 14.0833333333 + 15.7916666667*I]
4047
[ -10.5 + 26.375*I 20.0833333333 + 24.75*I]
4048
sage: A.exp(algorithm='taylor')
4049
[-19.6146006163 + 12.5177432169*I 3.79496442472 + 28.8837964828*I]
4050
[-32.3835771246 + 21.8842351994*I 2.26963458304 + 44.9013203415*I]
4051
"""
4052
if algorithm not in ('pade', 'eig', 'taylor'):
4053
raise ValueError("algorithm must be 'pade', 'eig', or 'taylor'")
4054
4055
global scipy
4056
if scipy is None:
4057
import scipy
4058
import scipy.linalg
4059
4060
cdef Matrix_double_dense M
4061
M = self._new()
4062
4063
if algorithm=='pade':
4064
M._matrix_numpy = scipy.linalg.expm(self._matrix_numpy)
4065
elif algorithm=='eig':
4066
M._matrix_numpy = scipy.linalg.expm2(self._matrix_numpy)
4067
elif algorithm=='taylor':
4068
if order is None:
4069
M._matrix_numpy = scipy.linalg.expm3(self._matrix_numpy)
4070
else:
4071
M._matrix_numpy = scipy.linalg.expm3(self._matrix_numpy, q=order)
4072
4073
return M
4074
4075
def zero_at(self, eps):
4076
"""
4077
Returns a copy of the matrix where elements smaller than or
4078
equal to ``eps`` are replaced with zeroes. For complex matrices,
4079
the real and imaginary parts are considered individually.
4080
4081
This is useful for modifying output from algorithms which have large
4082
relative errors when producing zero elements, e.g. to create reliable
4083
doctests.
4084
4085
INPUT:
4086
4087
- ``eps`` - Cutoff value
4088
4089
OUTPUT:
4090
4091
A modified copy of the matrix.
4092
4093
EXAMPLES::
4094
4095
sage: a=matrix([[1, 1e-4r, 1+1e-100jr], [1e-8+3j, 0, 1e-58r]])
4096
sage: a
4097
[ 1.0 0.0001 1.0 + 1e-100*I]
4098
[ 1e-08 + 3.0*I 0.0 1e-58]
4099
sage: a.zero_at(1e-50)
4100
[ 1.0 0.0001 1.0]
4101
[1e-08 + 3.0*I 0.0 0.0]
4102
sage: a.zero_at(1e-4)
4103
[ 1.0 0.0 1.0]
4104
[3.0*I 0.0 0.0]
4105
4106
4107
4108
"""
4109
global numpy
4110
cdef Matrix_double_dense M
4111
if numpy is None:
4112
import numpy
4113
eps = float(eps)
4114
out = self._matrix_numpy.copy()
4115
if self._sage_dtype is sage.rings.complex_double.CDF:
4116
out.real[numpy.abs(out.real) <= eps] = 0
4117
out.imag[numpy.abs(out.imag) <= eps] = 0
4118
else:
4119
out[numpy.abs(out) <= eps] = 0
4120
M = self._new()
4121
M._matrix_numpy = out
4122
return M
4123
4124
def round(self, ndigits=0):
4125
"""
4126
Returns a copy of the matrix where all entries have been rounded
4127
to a given precision in decimal digits (default 0 digits).
4128
4129
INPUT:
4130
4131
- ``ndigits`` - The precision in number of decimal digits
4132
4133
OUTPUT:
4134
4135
A modified copy of the matrix
4136
4137
EXAMPLES::
4138
4139
sage: M=matrix(CDF, [[10.234r + 34.2343jr, 34e10r]])
4140
sage: M
4141
[10.234 + 34.2343*I 3.4e+11]
4142
sage: M.round(2)
4143
[10.23 + 34.23*I 3.4e+11]
4144
sage: M.round()
4145
[10.0 + 34.0*I 3.4e+11]
4146
"""
4147
global numpy
4148
cdef Matrix_double_dense M
4149
if numpy is None:
4150
import numpy
4151
ndigits = int(ndigits)
4152
M = self._new()
4153
M._matrix_numpy = numpy.round(self._matrix_numpy, ndigits)
4154
return M
4155
4156
def _normalize_columns(self):
4157
"""
4158
Returns a copy of the matrix where each column has been
4159
multiplied by plus or minus 1, to guarantee that the real
4160
part of the leading entry of each nonzero column is positive.
4161
4162
This is useful for modifying output from algorithms which
4163
produce matrices which are only well-defined up to signs of
4164
the columns, for example an algorithm which should produce an
4165
orthogonal matrix.
4166
4167
OUTPUT:
4168
4169
A modified copy of the matrix.
4170
4171
EXAMPLES::
4172
4173
sage: a=matrix(CDF, [[1, -2+I, 0, -3*I], [2, 2, -2, 2], [-3, -3, -3, -2]])
4174
sage: a
4175
[ 1.0 -2.0 + 1.0*I 0.0 -3.0*I]
4176
[ 2.0 2.0 -2.0 2.0]
4177
[ -3.0 -3.0 -3.0 -2.0]
4178
sage: a._normalize_columns()
4179
[ 1.0 2.0 - 1.0*I -0.0 -3.0*I]
4180
[ 2.0 -2.0 2.0 2.0]
4181
[ -3.0 3.0 3.0 -2.0]
4182
"""
4183
M = self.__copy__()
4184
cdef Py_ssize_t i, j
4185
for j from 0 <= j < M.ncols():
4186
for i from 0 <= i < M.column(j).degree():
4187
a = M.column(j)[i].real()
4188
if a != 0:
4189
if a < 0:
4190
M.rescale_col(j, -1)
4191
break
4192
return M
4193
4194
def _normalize_rows(self):
4195
"""
4196
Returns a copy of the matrix where each row has been
4197
multiplied by plus or minus 1, to guarantee that the real
4198
part of the leading entry of each nonzero row is positive.
4199
4200
This is useful for modifying output from algorithms which
4201
produce matrices which are only well-defined up to signs of
4202
the rows, for example an algorithm which should produce an
4203
upper triangular matrix.
4204
4205
OUTPUT:
4206
4207
A modified copy of the matrix.
4208
4209
EXAMPLES::
4210
4211
sage: a=matrix(CDF, [[1, 2, -3], [-2+I, 2, -3], [0, -2, -3], [-3*I, 2, -2]])
4212
sage: a
4213
[ 1.0 2.0 -3.0]
4214
[-2.0 + 1.0*I 2.0 -3.0]
4215
[ 0.0 -2.0 -3.0]
4216
[ -3.0*I 2.0 -2.0]
4217
sage: a._normalize_rows()
4218
[ 1.0 2.0 -3.0]
4219
[2.0 - 1.0*I -2.0 3.0]
4220
[ -0.0 2.0 3.0]
4221
[ -3.0*I 2.0 -2.0]
4222
"""
4223
return self.transpose()._normalize_columns().transpose()
4224
4225