Closed galjos closed 1 month ago
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 80.72%. Comparing base (
caeddba
) to head (f9fa8c8
). Report is 8 commits behind head on dev.:exclamation: Current head f9fa8c8 differs from pull request most recent head 48668aa
Please upload reports for the commit 48668aa to get more accurate results.
what happens if both atol and rtol are given?
what happens if both atol and rtol are given?
Or to put it in a different way. The numpy implementation has two default values - we use the same ones as numpy.isclose
.
But how do they handle these values? Is it always the stricter value or the other one?
np.isclose
is a or
between rtol
and atol
. So, it only gives False
if both are False
.
And, since we had np.isclose
already implemented with with the default values, there is no real change in functionality.
So actually I had a look at the numpy documentation. I think we should really reconsider at least the eq implementation:
For finite values, isclose uses the following equation to test whether two floating point values are equivalent.
absolute(a - b) <= (atol + rtol * absolute(b))
Unlike the built-in math.isclose, the above equation is not symmetric in a and b – it assumes b is the reference value – so that isclose(a, b) might be different from isclose(b, a). Furthermore, the default value of atol is not zero, and is used to determine what small values should be considered close to zero. The default value is appropriate for expected values of order unity: if the expected values are significantly smaller than one, it can result in false positives. atol should be carefully selected for the use case at hand. A zero value for atol will result in False if either a or b is zero.
So what should we do now?
I had a quick look at the math.isclose
function and in my opinion, this would be the best way to go. The non-symmetry of the numpy.isclose
definiton seems like sort of a "bug" of numpy. There exists an issue within the numpy repository issue 10161, where the basic tenor of the discussions seems a bit similar to ours. My main conclusion when reading/analyszing the discussion within this OPEN issue is that they should have used the "more correct" implementation of math.isclose
but now it is to late to change the current implementation as everything would break and they wouldn't be backward compatible anymore.
So my suggestion would be to implement the same isclose
member functions as we did now, but using math.isclose
along with their default values (rel_tol=1e-09, abs_tol=0.0
), which would also ensure symmetry for the __eq__
methods. After implementing these new routines, we should definitly write a new benchmark group in order to compare both implementations. If the numpy.isclose
function is considerably faster we would have to discuss further on how critical the performance of these member function is and might be in future use cases.
What do you think @galjos ?
@97gamjak we could suggest to numpy to fix the isclose
In my opinion this is a bug on their side
@97gamjak we could suggest to numpy to fix the isclose
yes but if you read their issue, i think they have no intentions in closing or resolving this 😔
Obviously, this change could break alot of projects based on numpy. Although it would be correct to have the math implementation 🥲
I tried to include math.isclose
, but imo the code is unreadable now:
result = []
for self_pos, other_pos in zip(self.pos, other.pos):
for self_pos_i, other_pos_i in zip(self_pos, other_pos):
result.append(
math.isclose(
self_pos_i, other_pos_i, rel_tol=rtol, abs_tol=atol
)
)
if not np.all(result):
return False
I tried to include
math.isclose
, but imo the code is unreadable now:result = [] for self_pos, other_pos in zip(self.pos, other.pos): for self_pos_i, other_pos_i in zip(self_pos, other_pos): result.append( math.isclose( self_pos_i, other_pos_i, rel_tol=rtol, abs_tol=atol ) ) if not np.all(result): return False
How about doing something like this:
isclose_vectorized = np.vectorize(math.isclose)
np.all(isclose_vectorized(self.pos, other.pos, rel_tol=rtol, abs_tol=atol))
Probably best would be to define a function in utils (or somewhere else) like this:
def allclose_vectorized(a, b, rtol=..., atol=...):
isclose_vectorized = np.vectorize(math.isclose)
result = isclose_vectorized(a, b, rel_tol=rtol, abs_tol=atol)
return np.all(result)
Then, we could always use it like this:
allclose_vectorized(self.pos, other.pos, rtol=rtol, atol=atol)
I tried to include
math.isclose
, but imo the code is unreadable now:result = [] for self_pos, other_pos in zip(self.pos, other.pos): for self_pos_i, other_pos_i in zip(self_pos, other_pos): result.append( math.isclose( self_pos_i, other_pos_i, rel_tol=rtol, abs_tol=atol ) ) if not np.all(result): return False
How about doing something like this:
isclose_vectorized = np.vectorize(math.isclose) np.all(isclose_vectorized(self.pos, other.pos, rel_tol=rtol, abs_tol=atol))
Probably best would be to define a function in utils (or somewhere else) like this:
def allclose_vectorized(a, b, rtol=..., atol=...): isclose_vectorized = np.vectorize(math.isclose) result = isclose_vectorized(a, b, rel_tol=rtol, abs_tol=atol) return np.all(result)
Then, we could always use it like this:
allclose_vectorized(self.pos, other.pos, rtol=rtol, atol=atol)
I just added the described function in utils.math
with the last commit.
I just had a quick look at the codecov CI
- Probably we never checked the detailed run results. The tests checking if the baertype
approach works by setting the the environment to development in the bash script gives a total of 8 failed tests.
This happens probably because the return value of the quick and dirty shell script hack is overwritten by the second run of pytest, which fully succeeds.
I just had a quick look at the
codecov CI
- Probably we never checked the detailed run results. The tests checking if thebaertype
approach works by setting the the environment to development in the bash script gives a total of 8 failed tests.This happens probably because the return value of the quick and dirty shell script hack is overwritten by the second run of pytest, which fully succeeds.
This issue has been resolved. The problem was that the isclose
member function of Trajectory
did not return a bool
but a np.boo_
. Apparently, beartype
can't handle this, so I introduced a new type in PQAnalysis.types
called Bool
, which handles both. So if there is no need to distinguish between bool
and np.bool_
, we should always use the PQAnalysis
specific type Bool
and not bool
.
PYLINT REPORT
Your code has been rated at 9.53/10 (previous run: 9.85/10, -0.33)
Added isclose member function to
Trajectory
,AtomicSystem
andCell
.__eq__
now callsisclose
with default arguments.