8000 Additional GEVP method with errors by JanNeuendorf · Pull Request #195 · fjosw/pyerrors · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Additional GEVP method with errors #195

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

Merged
merged 13 commits into from
Nov 17, 2023
Merged

Conversation

JanNeuendorf
Copy link
Collaborator
@JanNeuendorf JanNeuendorf commented Jun 15, 2023

This adds an additional method to the correlator class called error_gevp().
It uses cholesky decomposition to give the eigenvectors with errors in the same form as the output of Corr.GEVP().
This could have been implemented as an extra flag for Corr.GEVP(), but I think this would not be optimal for the following reasons.

  1. It would need to work with the different sorting methods and all changes to them.
  2. The output might differ due to numerical issues and the different solver. The user might turn on errors and suddenly see a different result.
  3. The presence of an error flag suggests to the user that they should always leave it on for correct error propagation. That is likely not what they want.

If you do not want this as a method on Corr , this could just be moved to misc?

@JanNeuendorf JanNeuendorf requested a review from fjosw as a code owner June 15, 2023 17:30
@fjosw fjosw requested a review from s-kuberski June 15, 2023 17:32
@fjosw
Copy link
Owner
fjosw commented Jun 15, 2023

Thanks Jan, this looks good. I will have a closer look tomorrow. Maybe @s-kuberski also has an opinion?

As mentioned in #191 this feature will break with numpy 1.25 due to an incompatability in autograd. Another reason to fix this behavior upstream.

Copy link
Owner
@fjosw fjosw left a comment

Choose a reason for hiding this comment

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

I left a few small comments in the code.

Can we also have some consistency checks versus the standard GEVP method? You mention in the comments that the results do not necessarily agree but can we have some check within a tolerance?

@JanNeuendorf
Copy link
Collaborator Author

I added a test that shows that the mean values of the vectors with error are close to the ones from the other method. It also checks that there are errors, that they increase the errors of the projected correlator and, finally, that the result is None for a ts where the correlator is None.

@fjosw
Copy link
Owner
fjosw commented Jun 16, 2023

Very good, thanks a lot. I would wait for @s-kuberski's feedback but from my side, this is ready to merge.

Another question is maybe how to interpret the fact that the error of the projected correlator is larger when including the error of the eigenvectors. Do you have a feeling if one neglects anything when using the original GEVP method?

@fjosw fjosw linked an issue Jun 16, 2023 that may be closed by this pull request
@JanNeuendorf
Copy link
Collaborator Author
JanNeuendorf commented Jun 16, 2023

That is an interesting question. We had a discussion about this before. My opinion is that it is correct to project without errors. If I measure a correlator with some source, I get some linear combination of states. If I measure with multiple sources and project with some (error-less) vector, I get another linear combination of states. Ideally, i got this vector from the GEVP and it produces a better overlap with the state I care about, but this is something that will be reflected in my later analysis. For example, I get longer plateaus. The GEVP-error does not help to quantify state assignment. I either found a projection to the state I want, or I did not. I feel like this error should only be used when we are taking about the vector or source itself.

@s-kuberski
Copy link
Collaborator

Hi! Thanks for implementing this. I try to have a look as soon as possible, sorry for being slow.

@s-kuberski
Copy link
Collaborator

Hi,

first, thank you very much for taking the time to implement this. Also, I think that the perspective that you take in the last comment is an interesting one and I would agree concerning the fact that one does not need to include the uncertainties on the eigenvectors if one is just interested in having a nice projection.

Concerning the changes:

  • Conceptually, I would really prefer to have more overlap between the GEVP and the error_GEVP methods or even a combination. You write that the user could be tempted to switch on the errors, but I think that is not necessarily the case if you set the default to no error and carefully explain the flag in the docstring.
  • I agree that in this case one should have matching central values. But why not using the same algorithm to solve the GEVP in both cases? As I see it, you could just implement your new setup in _GEVP_solver, taking the correct routines for either scalars or Obs, and end up with the same central values in both cases. I think this would be a cleaner solution. Do you have a feeling if one of the two variants is more stable?
  • I also think that the sorting could be applied to both variants by just using the central values (floats) to sort the vectors, which might be Obs. If one is interested in the eigenvectors themselves, the correct sorting would be mandatory.

To conclude: I very much like the addition. For smooth integration in the existing workflow, I would really like to have an implementation where the user can work with or without errors on the eigenvectors, just by changing a flag, with the same possibilities in both cases. I would be really interested in your opinion. I think I could also contribute to this, if you like.

@fjosw
Copy link
Owner
fjosw commented Jun 23, 2023

Just a GitHub technicality: As a maintainer of the repo @s-kuberski should be able to push to @JanNeuendorf's feature branch in case you want to work on this together.

@JanNeuendorf
Copy link
Collaborator Author

Okay, I think there is nothing wrong with having errors as an option in the GEVP method, if explained well.
I agree, that both methods would need to use the same algorithm under the hood. I do not know, what the current scipy solver is doing under the hood; I believe, it is calling some LAPACK routines. Whatever it is doing, I assume it is better than the straightforward implementation used here. We should test this extensively, or give the user the choice which solver to use. There could be a solver option with three possible values: scipy, cholesky and cholesky_errors, where the last two are guaranteed to agree in the mean. I think that just switching the default behavior of the GEVP method is a breaking change. Someone might be using this in a scenario with lot of numerical instability and if the solver now behaves differently, the results could change completely. So the default flag would be the one used now.
Yes, feel very free to contribute!

@s-kuberski
Copy link
Collaborator

Hi,
I finally implemented the changes that I suggested some time ago:

  • The error propagation for the eigenvectors can now be chosen via a keyword in GEVP. Everything works as in the old case, including the sorting algorithms.
  • The solver for the case without error propagation may be chosen (more on this later).
  • I got a significant speedup (~ factor 7) compared to the earlier version in this pr. If I use the new implementation via Cholesky decomposition without uncertainties, I am about 40% percent slower than with scipy.linalg.eigh, which I consider a success since some python work is done here (compared to the fancy fortran implementation). When switching on the error propagation, it takes O(40) times longer to compute the eigenvectors.

Things to discuss:

  • I did some checks and the new implementation method='cholesky' seems to agree to machine precision with scipy.linalg.eigh. Therefore, we could abolish the whole idea of choosing the solver and just using the new one, if one wants to propagate uncertainties. Did you, @JanNeuendorf have an example, where the two methods differ?
  • The sorting can be significantly improved in terms of computer time. I'd open a new pull request, as soon as we have settle this one.

I am happy to discuss and to do changes, if you come up with ideas (also considering the keywords etc.)

pyerrors/misc.py Outdated
@@ -183,3 +183,18 @@ def _assert_equal_properties(ol, otype=Obs):
if hasattr(ol[0], attr):
if not getattr(ol[0], attr) == getattr(o, attr):
raise Exception(f"All Obs in list have to have the same state '{attr}'.")


def obsval(o):
Copy link
Owner
@fjosw fjosw Oct 20, 2023

Choose a reason for hiding this comment

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

I think the built-in float function should be able to do this as well.

Copy link
Owner

Choose a reason for hiding this comment

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

float(o) is o.value

Copy link
Collaborator

Choose a reason for hiding this comment

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

Very good point. In general terms, float(o) could not cope with o being None, so obsval would be a bit more flexible. But that should not matter in this case. I can remove the function.

@fjosw
Copy link
Owner
fjosw commented Oct 27, 2023

The interface looks good to me, but I didn't run any thorough tests. Would be interesting to hear @JanNeuendorf's opinion.

@JanNeuendorf
Copy link
Collaborator Author

I apologize for the late reply.
As for your question. No, I have not found a scenario where the results between this solver and scipy.linalg.eigh differ wildly. But, as you said, the mean values are extremely close but not identical. This means that there could be a scenario, where the ordering of states changes. This is certainly unlikely and I would agree that this might be worth it to have a unified solver.
I like the parameter name vector_obs a lot because it does not sound like it promises anything about the nature of the error propagation. As for your notes on performance, none of them sound too surprising. Is there anything I should do now?

@s-kuberski
Copy link
Collaborator

Thanks for you feedback.

If you did not see any other difference than machine precision between the two ways of solving the GEVP, I would not worry too much about the sorting of states because it would then also depend on every other step of the analysis chain... Now that I think about this, I would still leave everything as implemented as the scipy solver is still faster in the standard computation. If the user wants to have bit-identical results when switching vector_obs on/off, they can choose to use the same solver. Since the possibility is there, we do not have to remove it.... Is this fine with you?

I'll have another look at the code and push a small update (with obsval removed) that could then hopefully be merged.

@s-kuberski
Copy link
Collaborator

Sorry for the mess, I'll try to find out why my test fails (randomly or on specific versions).

@s-kuberski
Copy link
Collaborator

Turns out there is an ambiguity in the overall sign of single eigenvectors that might differ when changing the solver. This could also appear in production but would not have any impact for the physics because it cancels in the projection.
Therefore, it might be good to have the possibility to fix the solver to cholesky, when not using vector_obs.

@JanNeuendorf
Copy link
Collaborator Author

Okay, the sign difference might be important. I have a scenario where I project different sides of a matrix with different vectors. The end-results are independent from the sign, but maybe somewhere in between one might plot the correlator without taking an abs. This plot would then be flipped, which would be pretty major. Is there a simple convention that scipy.linalg.eigh uses for the sign, that we can just copy?

@s-kuberski
Copy link
Collaborator

Just to remind you: The current version of the pull request does not change any behavior of existing code, since you would have to turn on vector_obs or method='cholesky' to use the manual solver. I that sense, if one likes to be insensitive to this in future projects, one could set the method manually.

I don't know if there can be any simple convention as the whole problem is invariant under flipping the sign of an eigenvector. I can think a bit about this, but I would not be sure if there is a way to streamline this (after all, everything is just passed to Fortran code). The routines act on different data in the two cases. Funnily, when I use scipy.linalg.eigh instead of np.linalg.eigh for the ordinary EVP in the Cholesky implementation, I get sign flips for basically every case, whereas, in the current implementation, this only happens in about 1% of the cases.

@fjosw fjosw self-requested a review November 17, 2023 17:56
@fjosw fjosw merged commit e1a4d0c into fjosw:develop Nov 17, 2023
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.

GEVP eigenvectors with errors
3 participants
0