-
Notifications
You must be signed in to change notification settings - Fork 295
Add fuzzy equals comparisons #3895
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
Conversation
Job Coverage on 6af1064 wanted to post the following: Coverage
Warnings
This comment will be updated on new commits. |
include/numerics/numeric_vector.h
Outdated
* A given element will be deemed fuzzy equal if either a relative or absolute tolerance fuzzy | ||
* equal comparison returns true | ||
*/ | ||
bool fuzzy_equal(const NumericVector<T> & v, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have TypeVector<T>::relative_fuzzy_equals
and TypeVector<T>::absolute_fuzzy_equals
, so there is some precedent already for the "plural" form of "equal". So I would prefer to just maintain that unless you intend to replace the those existing "equals" functions as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to maintain that. "If you don't know whether you want absolute or relative tolerance, you don't know what you're doing" is a tautology on one level and a critically important statement on another. My "eh, it's all fuzzy equality" attitude in the libMesh shape-function-caching code long ago turned into a disaster when it eventually interacted with the MOOSE displaced-mesh code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took @jwpeterson's comment to be more aimed at the plural naming vs. the separation of relative
and absolute
. I've addressed the former. For the latter, I added distinct tolerances for relative and absolute to the APIs in this PR. I think this is better than having separate APIs. i don't care to loop over the vector or matrix twice when I can do it once. We don't run SNES twice to see whether we meet the relative convergence criteria or absolute convergence criteria
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, looks like I misinterpreted; thanks for clarifying.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read "plural" as "there's two of them", and the comment on the main page cuts off the function arguments below it. This looks good to me.
Job Distributed make check sweep (even) on 52a5ebe : invalidated by @lindsayad |
Job Test MOOSE min clang on 52a5ebe : invalidated by @lindsayad |
include/utils/fuzzy_equals.h
Outdated
typename std::enable_if<ScalarTraits<T>::value && ScalarTraits<T2>::value && | ||
ScalarTraits<T3>::value, | ||
int>::type = 0> | ||
bool absolute_fuzzy_equals(const T & var1, const T2 & var2, const T3 & tol = TOLERANCE * TOLERANCE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tol
should always conceptually be a positive real, right? What's the use case for const T3 &
vs just Real
?
include/utils/fuzzy_equals.h
Outdated
* within an absolute tolerance | ||
* @param var1 The first complex variable to be checked | ||
* @param var2 The second complex variable to be checked | ||
* @param tol The real tolerance to be used for comparing both real and imgainary parts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* @param tol The real tolerance to be used for comparing both real and imgainary parts | |
* @param tol The real tolerance to be used for comparing both real and imaginary parts |
I feel like I just successfully found the "No Brown M&Ms" clause.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need a confused emoji
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the poor spelling is fixed though 😄
include/utils/fuzzy_equals.h
Outdated
for (const auto i : make_range(libmesh_dim)) | ||
if (!relative_fuzzy_equals(var1(i), var2(i), tol)) | ||
return false; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this conclude that the vectors [1e9, 1] and [1e9, 1+1e-5] aren't relatively equal? That doesn't seem like the behavior we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The simplest comparable thing to do here is to get the l∞ norm in one pass, then use that as the scale for each difference comparison on a second pass. We could avoid a 2-pass solution by keeping track of l∞ of both the values and the differences in a single pass and then comparing at the end. That doesn't let us take advantage of early-return cases, but I don't think I see a way around that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the norm we use really matter? I think I'm going to do a bit of consolidation. It looks like we're using the L1 norm in TypeVector
I'm thinking that I might drop the |
Those are the trickiest, I admit. That's exactly the reason we want library routines for them IMHO, but if you want to get the obviously-acceptable parts merged right away then I'm fine with postponing anything that will take longer. |
Developers should overload `libMesh::l1_norm` and `libMesh::l1_norm_diff` for their numeric types and then they automatically get correct application of `libMesh::*fuzzy_equals`
Ok I've attempted a fairly generic, yet I believe simple, implementation that supports the more complicated global data structures. Let's see what you think |
Job Test mac on 6af1064 : invalidated by @lindsayad |
Failures are unrelated. I think this should be ready for review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where/how do we end up instantiating TypeVector<bool>
?
MOOSE solid mechanics module does it 🤷♂️ |
refs change `std::norm` overload in libMesh/libmesh#3895
Split off from #3883