Chapel: Add approximate equality function

Created on 27 Aug 2019  路  19Comments  路  Source: chapel-lang/chapel

Chapel doesn't really have an approximate equality function. It'd be a lot more convenient to have something that we can compare reals in a portable manner without doing abs(myReal-epsilon)

Python, for example, has math.isclose

Libraries / Modules easy / straightforward good first issue Feature Request

Most helpful comment

Do you know of any languages that support an actual infix operator for this? (like my head goes to ~==)

All 19 comments

Do you know of any languages that support an actual infix operator for this? (like my head goes to ~==)

Python, MatLab, and Julia use some variation of math.isclose(). I thought I had seen a ~= or ~== operator in some language before, but can't seem to find it now...

After a quick googling I couldn't find any language with that operator. However, I have seen that R seems to have a function for this, too.

This would be lovely to have! Two thoughts/requests --

  1. The threshold for equality should be adjustable.
  2. It would be very useful to have about absolute and relative precision tests (and an option that passes if either passes).

It looks like Julia has an isapprox function that does everything I described above, and two infix operators for default parameters. They use Unicode symbols for these....

There is a nice discussion here:
https://docs.julialang.org/en/v1/base/math/index.html#Base.isapprox

It looks like Julia has an isapprox function that does everything I described above

It seems numpy.isclose also supports this:

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

So, I think we can converge on a design to at least add a function to the Math library (and maybe followed up by an operator later on). I propose sticking with numpy.isclose, so something like:

isclose(x, y, rtol=1e-5, atol=1e-8, either=true)

if either==true then being close w.r.t either one of the tolerances returns true, otherwise both tolerances are checked.

I have no problem with the name isapprox or something else but my preference among what I have seen so far is isclose.

I realize that my point 2 above suggested the either option, but I'm not quite sure what I was thinking of when I typed that in. Thinking about it some more, I can't really think of a case where I want to check both absolute and relative precision.... so I'd be fine with just dropping that option. It doesn't look like either Numpy or Julia have such an option (as evidence for not really needing it).

Thinking about it some more, I can't really think of a case where I want to check both absolute and relative precision.... so I'd be fine with just dropping that option

:+1: - I believe both precision values are used in a typical implementation, e.g. in numpy.isclose:

The relative difference (rtol * abs(b)) and the absolute difference atol are added together to compare against the absolute difference between a and b.


I have no problem with the name isapprox or something else but my preference among what I have seen so far is isclose.

Agreed, though it should probably be camel-cased for interface consistency: isClose(..).

I agree that we can do without the either argument and check both as in numpy.isclose

Agreed, though it should probably be camel-cased for interface consistency: isClose(..)

I agree with this in general. However, we currently have isnan, isinf and isfinite in the Math library. So, if we want to switch all of these (there may be other similar functions) maybe we can add isClose and shortly thereafter deprecate all lowercase functions in favor of camelCase? It might worth to start a separate discussion for that, though

So, if we want to switch all of these (there may be other similar functions) maybe we can add isClose and shortly thereafter deprecate all lowercase functions in favor of camelCase? It might worth to start a separate discussion for that, though

You're right. It looks like no functions in the Math module uses camel casing currently.

In that case, I'd actually lean towards leaving it as isclose for consistency within the Math module, and hopefully switch the Math module to use camelCasing in the near future (also mentioned in https://github.com/chapel-lang/chapel/issues/6698). I wouldn't want that discussion to block progress of this feature.

Does anyone have any thoughts on whether the implementation should be symmetric or not?

E.g. numpy.isclose checks with absolute(a - b) <= (atol + rtol * absolute(b)) taking b as the reference value whereas math.isclose takes the larger of the two arguments as the reference.

I find the symmetric behavior more appealing. I imagine the numpy implementation dropped the max(a,b) for performance reasons.

(with admittedly only a little thought on this) I guess I prefer having a reference version. Most of the time I use a function like this is in tests, where I do have "truth" that I want to compare against, and I want to define my error based on the reference.

In particular, using the maximum value will cause trouble when comparing against zero, since you will always end up using the relative test, when you want to use the absolute value. edit ignore this, I'm wrong here.

Also is there a reason to combine the tests as atol + rtol*absolute(b) as opposed to ORing two separate tests? It feels like an optimization, but might have unintended consequences.

@npadmana - PEP 485 is a great reference for this topic in general, and explains all the design decisions behind Python's math.isclose.

Also is there a reason to combine the tests as atol + rtol*absolute(b) as opposed to ORing two separate tests? It feels like an optimization, but might have unintended consequences.

From Behavior near zero section:

However, while mathematically correct, there are many use cases where a user will need to know if a computed value is "close" to zero. This calls for an absolute tolerance test. If the user needs to call this function inside a loop or comprehension, where some, but not all, of the expected values may be zero, it is important that both a relative tolerance and absolute tolerance can be tested for with a single function with a single set of parameters.

Symmetry is also discussed in the PEP.

@ben-albrecht -- sorry, wasn't clear. I wasn't suggesting two tests but rather (absolute(a-b)<atol) || (absolute(a-b) < (rtol*absolute(b)). I think this works just as well.

@ben-albrecht --- that's a really nice reference, and I'm convinced by the argument behind the symmetric version (especially for iterative arguments). And I was incorrect about my issue regarding zero.... (fixed comment above)

I'll note that the implementation in the PEP is abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol ), which is equivalent (I think) to my ORed definition, but isn't quite the same as the numpy version (which mixes the two). While I think it probably doesn't make a difference, I do prefer the Python version.

Finally, I do agree with the choice of defaults (including 0 for the absolute error). I think it's much better to return incorrect negatives than incorrect positives.

Here is an (admittedly contrived) example where I think Python gets it right and Numpy gets it wrong.

>>> math.isclose(0.0,1.1,abs_tol=1.0, rel_tol=0.1)
False
>>> numpy.isclose(0.0,1.1,atol=1.0,rtol=0.1)
True

@npadmana - I'll just point out that:

I'll note that the implementation in the PEP is abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol ), which is equivalent (I think) to my ORed definition, but isn't quite the same as the numpy version (which mixes the two). While I think it probably doesn't make a difference, I do prefer the Python version.

This implementation shown here looks more like an ANDed definition rather than ORed. I do not think that it'll change the course of discussion, though.

I have very little preference personally on these matters. However, numpy behavior in your contrived example seems surprising to me as well. So I'd lean more towards following math.isclose rather than numpy.isclose.

I think in terms of design we all are on the same page here. #14249 aims to add this function to the Math library.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Aniket21mathur picture Aniket21mathur  路  3Comments

buddha314 picture buddha314  路  3Comments

BryantLam picture BryantLam  路  3Comments

ben-albrecht picture ben-albrecht  路  3Comments

ben-albrecht picture ben-albrecht  路  3Comments