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

Use evd driver for eigh in TwoQubitWeylDecomposition #5251

Merged
merged 6 commits into from
Oct 20, 2020

Conversation

mtreinish
Copy link
Member

@mtreinish mtreinish commented Oct 16, 2020

Summary

The hermitian optimized eigen solver function in scipy has shown
to possibly produce varied results in different environments. For
example, the matrix returned by the eigh() call in the
TwoQubitWeylDecomposition class's __init__ method when called for
a CXGate:

TwoQubitWeylDecomposition(Operator(CXGate()))

was observed to be

[[ 0.70710678  0.         -0.70710678  0.        ]
 [ 0.         -0.70710678  0.          0.70710678]
 [ 0.          0.70710678  0.          0.70710678]
 [ 0.70710678  0.          0.70710678  0.        ]]

on one system/environment but

[[ 0.70710678  0.         -0.70710678  0.        ]
 [ 0.          0.70710678  0.          0.70710678]
 [ 0.         -0.70710678  0.          0.70710678]
 [ 0.70710678  0.          0.70710678  0.        ]]

on a different environment. This difference was resulting in
inconsistent decompositions being generated and made reproducing results
from the transpiler difficult. In testing the numpy equivalent
function [2] has proven to be more reliable across different
environments. It turns out that this was because the LAPACK driver
used by the numpy routine was evd while the default driver for scipy
was ev. Switching the scipy driver to use evd too fixes this issue. This
commit switches the eigh() usage in the TwoQubitWeylDecomposition
class to explicilty set the driver to evd instead of the default ev`,
this should fix the inconsistent decompositions we were seeing between
environments.

Details and comments

[1] https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.eigh.html
[2] https://numpy.org/doc/stable/reference/generated/numpy.linalg.eigh.html

The hermitian optimized eigen solver function in scipy has shown
to possibly produce varied results in different environments. For
example, the matrix returned by the eigh() call in the
TwoQubitWeylDecomposition class's __init__ method when called for
a CXGate:

TwoQubitWeylDecomposition(Operator(CXGate()))

was observed to be

[[ 0.70710678  0.         -0.70710678  0.        ]
 [ 0.         -0.70710678  0.          0.70710678]
 [ 0.          0.70710678  0.          0.70710678]
 [ 0.70710678  0.          0.70710678  0.        ]]

on one system/environment but

[[ 0.70710678  0.         -0.70710678  0.        ]
 [ 0.          0.70710678  0.          0.70710678]
 [ 0.         -0.70710678  0.          0.70710678]
 [ 0.70710678  0.          0.70710678  0.        ]]

on a different environment. This difference was resulting in
inconsistent decompositions being generated and made reproducing results
from the transpiler difficult. In testing the numpy equivalent
function [2] has proven to be more reliable across different
environments. This commit switches the eigh() usage in the
TwoQubitWeylDecomposition class to use the numpy function instead
this should fix the inconsistent decompositions we were seeing between
environments.

[1] https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.eigh.html
[2] https://numpy.org/doc/stable/reference/generated/numpy.linalg.eigh.html
This commit removes a test Qiskit#4835 which was added to validate that the
internal values for the decomposer didn't sign flip in the specific case
that the PR was fixing. However with the use of the numpy eigh()
function these internal values no longer the same. So instead of
updating the test this just deletes it since the original case it was
catching is no longer applicable.
@mtreinish
Copy link
Member Author

I just remembered that we will need to bump the minimum version of numpy in the requirements list to 1.18.0 with this. The numpy eigh() function was added in 1.18.0

@nonhermitian
Copy link
Contributor

So they both use zheevd in general. So it seems hard to fathom that there is any difference between the two when using that driver. Scipy selects between multiple drivers, but it can also be manually set. Did you try setting the driver explicitly in the scipy version?

@mtreinish
Copy link
Member Author

I didn't mostly because I assumed the driver selection would be deterministic and it would be using the same driver on both systems I was testing on (@ajavadia also had the same issue on two of his systems). I'll give setting a driver explicitly a try on Monday.

But at the end of the day if this solves the issue for us I don't see the harm in switching to use the numpy routine even if why it's happening is a bit of a mystery. My first thought after figuring out that eigh() was the source of the issue was a local BLAS+LAPACK version difference, but at least in my case the versions installed were from the same linux distro packages (at the same versions).

@nonhermitian
Copy link
Contributor

Actually looking theough the docs it is ev by default in scipy and evd in numpy. So driver='evd' in scipy should return the same in both modules

@nonhermitian
Copy link
Contributor

And yes not the end of the world, but then we wouldn't need to bump up a dependency for no reason.

@mtreinish
Copy link
Member Author

Ok, tested locally on my 2 systems and using evd as the driver produces a consistent result. So I'll update this to just use that with the scipy method. That way we can avoid the requirements bump

This commit reverts back to using the scipy eigh() function but sets a
the lapack driver to be fixed as evd. The numpy routine was using the
evd driver while scipy would default to use ev as the driver. Switching
the scipy driver from ev to evd seems to be the key difference between
the routines which was causing the inconsistent results with scipy.
Approaching it this way means we avoid need to bump the minimum numpy
version in the requirements list since the numpy eigh() function was
added in a fairly recent version.
@mtreinish mtreinish changed the title Use eigh from numpy for TwoQubitWeylDecomposition Use evd driver for eigh in TwoQubitWeylDecomposition Oct 19, 2020
@ajavadia
Copy link
Member

Yes I can confirm this is consistent for me too. Also I found that we can revert much of the roundings introduced in #4862 and #4835.
(We still need the M2 rounding but that seems to be it)

@mtreinish
Copy link
Member Author

@ajavadia do you want me to remove that rounding in this PR or do it in a followup?

@ajavadia
Copy link
Member

follow up is fine, thanks

@ajavadia ajavadia merged commit 651b460 into Qiskit:master Oct 20, 2020
@mtreinish mtreinish deleted the numpy-eigh branch October 20, 2020 18:36
mtreinish added a commit to mtreinish/qiskit-core that referenced this pull request Oct 20, 2020
In Qiskit#5251 we updated the LAPACK driver used for the scipy hermitian
optimized eigensovler to use 'evd' instead of the default 'ev'. However,
this implicitly added a scipy minimum version of 1.5.0 because the
driver kwarg was only added to the eigh() function in that release. [1]
In that PR we didn't bump the minimum scipy version to reflect this.
However, scipy 1.5.0 has a version conflict with our downstream users,
mainly qiskit-aqua/qiskit-chemistry which use PySCF as an optional
dependency. PySCF has a capped version of scipy in their requirements [2]
which makes bumping our minimum scipy version intractable currently. To
avoid this issue, this commit switches to use numpy's eigh()
function [3] instead of scipy's. The numpy function uses the evd driver
and was what we originally used in Qiskit#5251 but was switched to use scipy's
eigh() with the driver kwarg to avoid bumping the minimum version of
numpy. However, between bumping the minimum numpy version and bumping
the minimum scipy version, the numpy version is less problematic.

[1] https://docs.scipy.org/doc/scipy/reference/release.1.5.0.html#scipy-linalg-improvements
[2] https://github.com/pyscf/pyscf/blob/v1.7.5/setup.py#L490
[3] https://numpy.org/doc/stable/reference/generated/numpy.linalg.eigh.html
@mtreinish
Copy link
Member Author

Well it turns out that the driver kwarg for scipy was only added in scipy 1.5.0:

https://docs.scipy.org/doc/scipy/reference/release.1.5.0.html#scipy-linalg-improvements

So we should have bumped the minimum version in this PR. But, it turns out chemistry in aqua doesn't work with scipy 1.5 because pyscf caps at <1.5. I opened to #5263 address this an unbreak aqua ci.

mergify bot pushed a commit that referenced this pull request Oct 21, 2020
* Use numpy eigh instead of scipy eigh with driver kwarg

In #5251 we updated the LAPACK driver used for the scipy hermitian
optimized eigensovler to use 'evd' instead of the default 'ev'. However,
this implicitly added a scipy minimum version of 1.5.0 because the
driver kwarg was only added to the eigh() function in that release. [1]
In that PR we didn't bump the minimum scipy version to reflect this.
However, scipy 1.5.0 has a version conflict with our downstream users,
mainly qiskit-aqua/qiskit-chemistry which use PySCF as an optional
dependency. PySCF has a capped version of scipy in their requirements [2]
which makes bumping our minimum scipy version intractable currently. To
avoid this issue, this commit switches to use numpy's eigh()
function [3] instead of scipy's. The numpy function uses the evd driver
and was what we originally used in #5251 but was switched to use scipy's
eigh() with the driver kwarg to avoid bumping the minimum version of
numpy. However, between bumping the minimum numpy version and bumping
the minimum scipy version, the numpy version is less problematic.

[1] https://docs.scipy.org/doc/scipy/reference/release.1.5.0.html#scipy-linalg-improvements
[2] https://github.com/pyscf/pyscf/blob/v1.7.5/setup.py#L490
[3] https://numpy.org/doc/stable/reference/generated/numpy.linalg.eigh.html

* Remove numpy min version bump

* Remove leftover driver kwarg
@kdk kdk added the Changelog: None Do not include in changelog label Mar 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants