Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

linalg: Eigenvalues and Eigenvectors #816

Merged
merged 35 commits into from
Jun 30, 2024
Merged

Conversation

perazz
Copy link
Contributor

@perazz perazz commented May 16, 2024

Computing eigenvalues and eigenvectors of a square matrix: $A \cdot \bar{v} - \lambda \cdot \bar{v} = 0$ .
Based on LAPACK General (*GEEV) or real symmetric (*SYEV) / complex Hermitian (*HEEV) operations.

  • base implementation
  • tests
  • exclude unsupported xdp
  • documentation
  • submodule
  • examples
  • subroutine interface

General matrix

Prior art

  • Numpy: eigenvalues, eigenvectors = eig(a)
  • Numpy: eigenvalues = eigvals(a)
  • Scipy: eig(a, b=None, left=False, right=True, overwrite_a=False, overwrite_b=False, check_finite=True, homogeneous_eigvals=False)

Proposed implementation

  • Eigenvalues only: two function interfaces lambda = eigvals(A [, err])

  • Eigendecomposition: subroutine interface call eig(a,lambda [,right] [,left] [,overwrite_a] [,err])

    • Eigenvalues of real matrices can be complex-conjugate. To avoid confusion, right, left are postprocessed and always returned as complex arrays. LAPACK instead stores (re,im) of conjugates as consecutive real values [j, j+1] in a real array, this would be very confusing and hard to track down.

    • The eigendecomposition can be used either for eigenvalues only, or to retrieve left or right eigenvectors.

Real Symmetric / Complex Hermitian

Prior art

  • Numpy: eigenvalues, eigenvectors = eigh(a, uplo='L')
  • Numpy: eigenvalues = eigvalsh(a)
  • Scipy: eigh(a, b=None, *, lower=True, eigvals_only=False, overwrite_a=False, overwrite_b=False, turbo=<object object>, eigvals=<object object>, type=1, check_finite=True, subset_by_index=None, subset_by_value=None, driver=None)

Proposed implementation

  • Eigenvalues only: two function interfaces lambda = eigvalsh(A [, err])

  • Eigendecomposition: subroutine interface call eigh(a,lambda [,vectors] [,upper_a] [,overwrite_a] [,err] )

    • option to access lower/upper triangle part only
    • Avoid allocation when possible e.g. when providing vectors that can be used as temporary storage for a.

Note: the LAPACK backends are not pure due to functions with intent(out) arguments. The current proposed interfaces are ready to be made pure (e.g., function interface with all intent(in) arguments) to make it easy when the backend will be pure.

I believe this is ready for consideration, thank you @jvdp1 @jalvesz @fortran-lang/stdlib

@perazz perazz marked this pull request as ready for review May 16, 2024 10:31
@jvdp1 jvdp1 requested a review from a team May 27, 2024 20:11
Copy link
Member

@jvdp1 jvdp1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Here are some comments regarding the specs. I'll focus on the code later

doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
example/linalg/example_eig.f90 Outdated Show resolved Hide resolved
@perazz
Copy link
Contributor Author

perazz commented Jun 4, 2024

From Fortran Monthly call:

  • For general matrix, option to also return real array of singular values (it is complex by default). But, stop on errors if the matrix had complex eigenvalue pairs.

For example, NumPy returns real eigenvalues when all imaginary parts are zero:

    if not isComplexType(t) and all(w.imag == 0.0):
        w = w.real
        vt = vt.real
        result_t = _realType(result_t)
    else:
        result_t = _complexType(result_t)

Thank you @jvdp1 for the reviews. As pointed out during our last Monthly call with @everythingfunctional @jalvesz, I've added the option to return the real part of the eigenvalues only. In this latest case, we must check that there are no meaningful imaginary components, and raise an error if so:

! Check that no eigenvalues have meaningful imaginary part
if (err0%ok() .and. any(aimag(clambda)>atol+rtol*abs(abs(clambda)))) then
err0 = linalg_state_type(this,LINALG_VALUE_ERROR, &
'complex eigenvalues detected: max(imag(lambda))=',maxval(aimag(clambda)))
endif

src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
src/stdlib_linalg_eigenvalues.fypp Outdated Show resolved Hide resolved
@perazz
Copy link
Contributor Author

perazz commented Jun 21, 2024

Thanks a lot @jvdp1 @jalvesz for the reviews - this PR looks well polished now.
You will find references for each of your comments to the related commit.

Copy link
Member

@jvdp1 jvdp1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @perazz . Overall LGTM. Pending a few minor suggestions, it could be merged IMO.

doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
doc/specs/stdlib_linalg.md Outdated Show resolved Hide resolved
example/linalg/example_eigh.f90 Outdated Show resolved Hide resolved
@jalvesz
Copy link
Contributor

jalvesz commented Jun 27, 2024

LGTM @perazz, I think this is ready to go :)

@perazz
Copy link
Contributor Author

perazz commented Jun 28, 2024

Thank you both @jvdp1 @jalvesz, I would say let's wait a couple more days, and then merge if there are no further comments.

@perazz
Copy link
Contributor Author

perazz commented Jun 30, 2024

Thank you, I will merge this one.

@perazz perazz merged commit cc129c6 into fortran-lang:master Jun 30, 2024
17 checks passed
@perazz perazz deleted the eigenvalues branch June 30, 2024 08:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants