Skip to content

Exponential of a matrix #968

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

Open
wants to merge 31 commits into
base: master
Choose a base branch
from

Conversation

loiseaujc
Copy link
Contributor

@loiseaujc loiseaujc commented Mar 27, 2025

Following a comment by Meromorphic on the discourse here, this PR implements the matrix exponential function expm. It does so in a new stdlib_linalg_matrix_functions submodule which might eventually accomodate later on implementations of other matrix functions (e.g. logm, sqrtm, cosm, etc).

Proposed interface

  • E = expm(A [, order, err]) where A is the input n x n matrix, order (optional) is the order of the Pade approximation, and err of type linalg_state_type for error handling.

Key facts

The implementation makes use of a standard "scaling and squaring" approach to compute the Pade approximation with a fixed order. It is adapted from the implementation by John Burkardt.

Progress

  • Base implementation.
  • Tests
  • in-code Documentation
  • Specifications
  • Example

Possible improvements

The implementation is fully working and validated. Computational performances and/or robustness could potentially be improved eventually by considering:

  • Automatic estimation of the best order for the Pade approximation based on the data. This is used for instance in scipy.linalg.expm.
  • Balancing is not used currently, but it might improve the robustness.
  • At the end of the algorithm, the squaring step is implemented using a simple loop involving matmul. Additional performances can be gained by implementing a fast matrix_power function (see np.matrix_power for instance). (irrelevant for this particular task since the scaling factor is chosen as a power of 2, might still be a useful function in the grand scheme of things)

In practice, it has however never failed me so far.

cc @perazz, @jvdp1, @jalvesz

@loiseaujc loiseaujc marked this pull request as draft March 27, 2025 11:34
@loiseaujc loiseaujc marked this pull request as ready for review March 27, 2025 13:25
@loiseaujc
Copy link
Contributor Author

loiseaujc commented Mar 27, 2025

I think it is pretty much ready for review. Note that the optimizations I've mentioned (i.e. variable order for the Pade approximation, balancing, etc) wouldn't change the signature of the function. Either we take it as is and gradually improve it over time, or we improve it right away (but I won't have much time to do it right now). As you like.

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 @loiseaujc ! LGTM. I have some minor suggestions and some additional ones that might lead to some discussions.

@loiseaujc loiseaujc marked this pull request as draft July 3, 2025 12:33
@loiseaujc loiseaujc marked this pull request as ready for review July 4, 2025 08:44
@loiseaujc
Copy link
Contributor Author

loiseaujc commented Jul 4, 2025

Seems like I have issues with mysys2-build stuff. Not sure what's going on. Anyone would have an idea ?


contains

!> schur decomposition tests
Copy link
Contributor

@jalvesz jalvesz Jul 8, 2025

Choose a reason for hiding this comment

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

I think this is a copy-paste typo: "Matrix exponential tests" ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes indeed. Will update shortly.

contains

#:for rk,rt,ri in RC_KINDS_TYPES
module function stdlib_expm_${ri}$(A, order, err) result(E)
Copy link
Contributor

Choose a reason for hiding this comment

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

For the base implementation I would have done it using a subroutine that pre-supposes the matrix is already allocated in memory, e.g.: real(<>), intent(inout) :: E(:,:). The function interface could be simply built on top of it to have automatic (costly) allocations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I mimicked what had been for the Cholesky factorization. The lowest-level driver is a subroutine doing in-place computation. I've also added the matrix_exp interface if anyone wants to do a subroutine call rather than a function one.

allocate(tests(0))

#:for rk,rt,ri in RC_KINDS_TYPES
tests = [tests, new_unittest("expm_${ri}$",test_expm_${ri}$)]
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe the current fails with msys CI is related to the changes in #1008

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I merged the latest additions from master into my branch but it does not seem to solve the problem. Really not sure what's going on since all other tests pass with flying colors. Will investigate further.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking a bit online, it seems that the exit code 0xC0000374 returned when the test fails indicates a heap corruption problem.

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.

4 participants