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

Fix issue 508 #509

Merged
merged 6 commits into from
May 21, 2024
Merged

Fix issue 508 #509

merged 6 commits into from
May 21, 2024

Conversation

GiggleLiu
Copy link
Member

@GiggleLiu GiggleLiu commented May 10, 2024

fix #508 by setting a looser threshold.

@@ -315,7 +315,7 @@ Base.vec(et::EntryTable) = Vector(et)
# convert a (maybe complex) number x to real number.
function safe_real(x)
img = imag(x)
if !(iszero(img) || isapprox(x - im*img, x))
if !(iszero(img) || isapprox(x - im*img, x; atol=1e-15))
Copy link
Contributor

Choose a reason for hiding this comment

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

What stops us from doing something like atol=eps(x)? Is it the fact that eps might not be defined for x? If yes, how about defining

function safe_real(x)
    # like what we had previously
end

function safe_real(x::Union{AbstractFloat,Complex{<:AbstractFloat}})
    # like in this PR
end

Copy link
Member Author

Choose a reason for hiding this comment

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

Using eps might be too strict.

julia> eps(0.0)
5.0e-324

But you are right, eps may not be defined for e.g. symbolic types. Maybe implementing two interfaces is better.

Copy link
Member Author

@GiggleLiu GiggleLiu May 13, 2024

Choose a reason for hiding this comment

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

Oops, defining a function on AbstractFloat causes Dual number input run into the other function. Got an error in the AD tests.

@Roger-luo
Copy link
Member

I don't like this change, this is too heurestic, we should really just let user decide what precision they want. And only guarantee on throw or nothrow behaviour.

@jlbosse
Copy link
Contributor

jlbosse commented May 13, 2024

In what case would the proposed behaviour not be desirable? Grepping through the code safe_real(x::T) is currently only used in situations where we would expect x to be real up to precision eps(T) since it is the output of sum large-ish calculation that would be exactly real if we had no floating point error.

Its behaviour is i) return its input if x is real, ii) return the real part of x if the imaginary part is zero up to eps(T) if eps(T) is defined and iii) return the real part of x if the imaginary part is exactly zero if eps(T) is not defined. To me this is (with the exception of maybe iii) ) exactly the behaviour I would find sensible. And iii) would fail loudly if we get something with tiny, but non-zero imaginary part.

@GiggleLiu
Copy link
Member Author

GiggleLiu commented May 13, 2024

The only issue is, the eps, which is defined by the gap between two floating point numbers, might be a bit too strict. What about using sqrt(eps(T)) or 10 * eps(T)?
I am fine to make it a keyword argument so that users can tune it when the program errors unexpectedly.

@jlbosse
Copy link
Contributor

jlbosse commented May 13, 2024

I personally would be happy with either option, I guess do whichever is more standard when doing numerical computations?

@Roger-luo
Copy link
Member

Roger-luo commented May 13, 2024

The problem is we cannot know how large this eps means fine. This is exactly why you have rtol and atol in isapprox to control such with a good default if you want an approximate behaviour. What I'm proposing is either:

a) forward these two options in expect;

expect(...; atol=..., rtol=...)

b) if user turns on nothrow=True, fallback to the unsafe real conversion and let users decide what they want to do with the result

expect(...; nothrow=True) # this is explicitly saying the calculation will not throw and may not be safe

@jlbosse for #508 , it is exactly the case of an unexpected throw when someone expects it to go through by just ignoring the imaginary part despite of how large it is.

@GiggleLiu
Copy link
Member Author

GiggleLiu commented May 13, 2024

Ok, I will assume users know what they are doing. I will throw a warning for large floating point errors to let user know they might want to use sandwich function instead. Then expect will never throw error.

@Roger-luo I think the interface that you proposed is too complicated. When the inputs are non-floating point types, it does not make sense to set eps.

@Roger-luo
Copy link
Member

I will throw a warning for large floating point errors to let user know they might want to use sandwich function instead. Then expect will never throw error.

that sounds good to me.

@Roger-luo Roger-luo merged commit a77d0dd into master May 21, 2024
24 checks passed
@Roger-luo Roger-luo deleted the jg/fix508 branch May 21, 2024 20:43
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.

Undesired handling of complex values when the expectation of an observable has zero real part
3 participants