Solve for the roots of a complex polynomial equation by computing the eigenvalues of the companion matrix.
This one uses LAPACK for the eigen solver (cgeev
or zgeev
).
Note
Works only for single and double precision.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
integer, | intent(in) | :: | n |
polynomial degree |
||
complex(kind=wp), | intent(in), | dimension(n+1) | :: | p |
polynomial coefficients array, in order of decreasing powers |
|
complex(kind=wp), | intent(out), | dimension(n) | :: | w |
roots |
|
integer, | intent(out) | :: | info |
output from the lapack solver.
|
subroutine cpolyroots(n, p, w, info) implicit none integer, intent(in) :: n !! polynomial degree complex(wp), dimension(n+1), intent(in) :: p !! polynomial coefficients array, in order of decreasing powers complex(wp), dimension(n), intent(out) :: w !! roots integer, intent(out) :: info !! output from the lapack solver. !! !! * if `info=0` the routine converged. !! * if `info=-999`, then the leading coefficient is zero. integer :: i !! counter complex(wp), allocatable, dimension(:,:) :: a !! companion matrix complex(wp), allocatable, dimension(:) :: work !! work array real(wp), allocatable, dimension(:) :: rwork !! rwork array (2*n) complex(wp), dimension(1) :: vl, vr !! not used here #ifdef REAL32 interface subroutine cgeev( jobvl, jobvr, n, a, lda, w, vl, ldvl, vr, ldvr, work, lwork, rwork, info ) implicit none character :: jobvl, jobvr integer :: info, lda, ldvl, ldvr, lwork, n real :: rwork( * ) complex :: a( lda, * ), vl( ldvl, * ), vr( ldvr, * ), w( * ), work( * ) end subroutine cgeev end interface #elif REAL128 ! do not have a quad solver in lapack #else interface subroutine zgeev( jobvl, jobvr, n, a, lda, w, vl, ldvl, vr, ldvr, work, lwork, rwork, info ) implicit none character :: jobvl, jobvr integer :: info, lda, ldvl, ldvr, lwork, n double precision :: rwork( * ) complex*16 :: a( lda, * ), vl( ldvl, * ), vr( ldvr, * ), w( * ), work( * ) end subroutine zgeev end interface #endif ! error check: if (abs(p(1)) == 0.0_wp) then info = -999 return end if ! allocate the work array: allocate (work(3*n)) allocate(rwork(2*n)) ! create the companion matrix allocate (a(n, n)) a = 0.0_wp do i = 1, n - 1 a(i, i + 1) = 1.0_wp end do do i = n, 1, -1 a(n, n - i + 1) = -p(i + 1)/p(1) end do ! call the lapack solver: #ifdef REAL32 ! single precision call cgeev('N', 'N', n, a, n, w, vl, 1, vr, 1, work, 3*n, rwork, info) #elif REAL128 error stop 'do not have a quad solver in lapack' #else ! by default, use double precision: call zgeev('N', 'N', n, a, n, w, vl, 1, vr, 1, work, 3*n, rwork, info) #endif end subroutine cpolyroots