Skip to content

Commit 414dabf

Browse files
wrap eigenvalues
1 parent dd55dfa commit 414dabf

3 files changed

Lines changed: 179 additions & 0 deletions

File tree

src/acb_mat.pyx

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,3 +616,162 @@ cdef class acb_mat(flint_mat):
616616
for j from 0 <= j < m:
617617
acb_get_imag(arb_mat_entry(u.val, i, j), acb_mat_entry(s.val, i, j))
618618
return u
619+
620+
def eig(s, bint left=False, bint right=False, multiple=False, algorithm=None, tol=None, long maxiter=0, bint nonstop=False):
621+
r"""
622+
Computes eigenvalues and optionally eigenvectors of this matrix.
623+
Returns either *E*, (*E*, *L*), (*E*, *R*) or (*E*, *L*, *R*)
624+
depending on whether the flags *left* and *right* are set,
625+
where *E* is a row matrix of the eigenvalues, *L* is a matrix
626+
with the left eigenvectors as rows, and *R* is a matrix
627+
with the right eigenvectors as columns.
628+
629+
The *algorithm* can be "rump", "vdhoeven_mourrain", or *None*
630+
to use a default algorithm. Typically "rump" is slower and more
631+
accurate while "vdhoeven_mourrain" (the current default)
632+
is faster and less accurate.
633+
634+
>>> A = acb_mat([[2,3,5],[7,11,13],[17,19,23]])
635+
>>> for c in A.eig(): print(c)
636+
...
637+
[1.105299634957 +/- 6.34e-13] + [+/- 1.83e-13]j
638+
[-1.917027627441 +/- 2.64e-13] + [+/- 1.83e-13]j
639+
[36.811727992483 +/- 6.97e-13] + [+/- 1.83e-13]j
640+
>>> for c in A.eig(algorithm="rump"): print(c)
641+
...
642+
[1.10529963495745 +/- 4.71e-15] + [+/- 2.92e-15]j
643+
[-1.91702762744092 +/- 8.45e-15] + [+/- 3.86e-15]j
644+
[36.8117279924835 +/- 4.72e-14] + [+/- 9.07e-15]j
645+
646+
With the left and right eigenvector matrices, a complete
647+
diagonalization of the matrix is produced:
648+
649+
>>> A = acb_mat([[2,3,5],[7,11,13],[17,19,23]])
650+
>>> E, L, R = A.eig(left=True, right=True)
651+
>>> D = acb_mat(3,3)
652+
>>> for i in range(3): D[i,i] = E[0,i]
653+
...
654+
>>> (L*A*R - D).contains(acb_mat(3,3))
655+
True
656+
>>> (R*D*L - A).contains(acb_mat(3,3))
657+
True
658+
659+
Ill-conditioned or large matrices may require high precision
660+
to isolate the eigenvalues::
661+
662+
>>> sum(acb_mat(arb_mat.hilbert(20,20)).eig())
663+
Traceback (most recent call last):
664+
...
665+
ValueError: failed to isolate eigenvalues (try higher prec, multiple=True for multiple eigenvalues, or nonstop=True to avoid the exception)
666+
>>> sum(acb_mat(arb_mat.hilbert(20,20)).eig(nonstop=True))
667+
nan + nanj
668+
>>> showgood(lambda: sum(acb_mat(arb_mat.hilbert(20,20)).eig(nonstop=True)), parts=False)
669+
2.47967321036454 + [+/- 1.48e-56]j
670+
671+
With default options, the method only succeeds if all eigenvalues can be
672+
isolated. Multiple (overlapping) eigenvalues can be handled by
673+
setting *multiple* = *True*.
674+
675+
>>> acb_mat.dft(4).eig()
676+
Traceback (most recent call last):
677+
...
678+
ValueError: failed to isolate eigenvalues (try higher prec, multiple=True for multiple eigenvalues, or nonstop=True to avoid the exception)
679+
>>> acb_mat.dft(4).eig(nonstop=True)
680+
[nan + nanj, nan + nanj, nan + nanj, nan + nanj]
681+
>>> acb_mat.dft(4).eig(multiple=True)
682+
[[-1.0000000000000 +/- 2.26e-15] + [+/- 1.23e-15]j, [+/- 4.96e-16] + [-1.00000000000000 +/- 3.72e-16]j, [1.00000000000000 +/- 4.98e-16] + [+/- 3.42e-16]j, [1.00000000000000 +/- 4.98e-16] + [+/- 3.42e-16]j]
683+
684+
At this time, computing the eigenvectors is not supported
685+
with multiple eigenvalues:
686+
687+
>>> acb_mat.dft(4).eig(multiple=True, right=True)
688+
Traceback (most recent call last):
689+
...
690+
NotImplementedError: eigenvectors not supported with multiple=True
691+
692+
The *algorithm* can also be set to "approx" to compute
693+
approximate eigenvalues and/or eigenvectors without error bounds.
694+
695+
>>> for c in acb_mat.dft(4).eig(algorithm="approx"): print(c.str(radius=False))
696+
...
697+
-0.999999999999999 - 7.85046229341892e-17j
698+
-2.35513868802566e-16 - 1.00000000000000j
699+
1.00000000000000 - 6.64346650360854e-17j
700+
0.999999999999999 - 5.14675360671472e-17j
701+
702+
If *algorithm* is set to "approx", then *multiple* has
703+
no effect, and both eigenvalues and eigenvectors can be computed
704+
regardless of overlap.
705+
706+
>>> E = acb_mat.dft(4).eig(algorithm="approx")
707+
>>> E, R = acb_mat.dft(4).eig(right=True, algorithm="approx")
708+
>>> E, L = acb_mat.dft(4).eig(left=True, algorithm="approx")
709+
>>> E, L, R = acb_mat.dft(4).eig(left=True, right=True, algorithm="approx")
710+
711+
"""
712+
cdef acb_mat E, L, R
713+
cdef acb_mat_struct *LP
714+
cdef acb_mat_struct *RP
715+
cdef mag_struct * magp
716+
cdef long n, prec
717+
cdef int success
718+
cdef mag_t tolm
719+
n = s.nrows()
720+
if n != s.ncols():
721+
raise ValueError("matrix must be square")
722+
prec = getprec()
723+
E = acb_mat(1, n)
724+
if left:
725+
L = acb_mat(n, n)
726+
LP = &(L.val[0])
727+
else:
728+
LP = NULL
729+
if right or algorithm != "approx":
730+
R = acb_mat(n, n)
731+
RP = &(R.val[0])
732+
else:
733+
RP = NULL
734+
if tol is not None:
735+
mag_init(tolm)
736+
tol = arb(tol)
737+
arb_get_mag(tolm, (<arb>tol).val)
738+
magp = tolm
739+
else:
740+
magp = NULL
741+
if n != 0:
742+
if algorithm == "approx":
743+
acb_mat_approx_eig_qr(acb_mat_entry(E.val, 0, 0),
744+
LP, RP, s.val, magp, maxiter, getprec())
745+
else:
746+
acb_mat_approx_eig_qr(acb_mat_entry(E.val, 0, 0),
747+
NULL, RP, s.val, magp, maxiter, getprec())
748+
if multiple:
749+
if left or right:
750+
raise NotImplementedError("eigenvectors not supported with multiple=True")
751+
if algorithm == "rump":
752+
success = acb_mat_eig_multiple_rump(acb_mat_entry(E.val, 0, 0),
753+
s.val, acb_mat_entry(E.val, 0, 0), RP, prec)
754+
else:
755+
success = acb_mat_eig_multiple(acb_mat_entry(E.val, 0, 0),
756+
s.val, acb_mat_entry(E.val, 0, 0), RP, prec)
757+
else:
758+
if algorithm == "rump":
759+
success = acb_mat_eig_simple_rump(acb_mat_entry(E.val, 0, 0),
760+
LP, RP, s.val, acb_mat_entry(E.val, 0, 0), RP, prec)
761+
elif algorithm == "vdhoeven_mourrain":
762+
success = acb_mat_eig_simple_vdhoeven_mourrain(acb_mat_entry(E.val, 0, 0),
763+
LP, RP, s.val, acb_mat_entry(E.val, 0, 0), RP, prec)
764+
else:
765+
success = acb_mat_eig_simple(acb_mat_entry(E.val, 0, 0),
766+
LP, RP, s.val, acb_mat_entry(E.val, 0, 0), RP, prec)
767+
if not (nonstop or success):
768+
raise ValueError("failed to isolate eigenvalues (try higher prec, multiple=True for multiple eigenvalues, or nonstop=True to avoid the exception)")
769+
if tol is not None:
770+
mag_clear(tolm)
771+
if not left and not right:
772+
return E
773+
if left and right:
774+
return E, L, R
775+
if left:
776+
return E, L
777+
return E, R

src/arb_mat.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,3 +657,11 @@ cdef class arb_mat(flint_mat):
657657
else:
658658
res = arb_mat_ne((<arb_mat>s).val, (<arb_mat>t).val)
659659
return res
660+
661+
def eig(s, *args, **kwargs):
662+
r"""
663+
Computes eigenvalues and/or eigenvectors of this matrix.
664+
This is just a wrapper for :meth:`.acb_mat.eig`; see the
665+
documentation for that method for details.
666+
"""
667+
return acb_mat(s).eig(*args, **kwargs)

src/flint.pxd

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,6 +1892,18 @@ cdef extern from "acb_mat.h":
18921892

18931893
int acb_mat_approx_solve(acb_mat_t X, const acb_mat_t A, const acb_mat_t B, long prec)
18941894

1895+
void acb_mat_randtest_eig(acb_mat_t A, flint_rand_t state, acb_srcptr E, long prec)
1896+
int acb_mat_approx_eig_qr(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, const mag_t tol, long maxiter, long prec)
1897+
void acb_mat_eig_global_enclosure(mag_t eps, const acb_mat_t A, acb_srcptr E, const acb_mat_t R, long prec)
1898+
1899+
int acb_mat_eig_simple_rump(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec)
1900+
int acb_mat_eig_simple_vdhoeven_mourrain(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec)
1901+
int acb_mat_eig_simple(acb_ptr E, acb_mat_t L, acb_mat_t R, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec)
1902+
1903+
int acb_mat_eig_multiple_rump(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec)
1904+
int acb_mat_eig_multiple(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec)
1905+
1906+
18951907
cdef extern from "acb_modular.h":
18961908
void acb_modular_theta(acb_t theta1, acb_t theta2, acb_t theta3, acb_t theta4, const acb_t z, const acb_t tau, long prec)
18971909
void acb_modular_eta(acb_t r, const acb_t tau, long prec)

0 commit comments

Comments
 (0)