Skip to content

Improved behavior of comparison between quantities #249

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

wagenadl
Copy link
Contributor

For quantities A and B of unequal dimensionality, the == operator now returns an array of the same shape that numpy would return if A and B were different and filled with False.

In particular, if both A and B are scalar arrays, the result is a scalar False. Previously, a scalar array (np.array(False)) would be returned under these circumstances.

If A and B do not have compatible sizes, an exception is generated.

The != operator behaves the same except that it fills the result with True values.

The other comparison operators now raise exceptions if a quantity that is not dimensionless is compared to a plain number. For effectively dimensionless quantities the comparison is now consistent. Previously, if

    n = pq.s * pq.kHz

then n < 2 would return True but n.simplified < 2 would return False. Now, both return False.

Comparison between incompatible quantities raises exceptions as before.

Closes #245

For quantities A and B of unequal dimensionality, the `==` operator
now returns an array of the same shape that numpy would return
if A and B were different and filled with False.

In particular, if both A and B are scalar arrays, the result is
a scalar False. Previously, a scalar array (`np.array(False)`)
would be returned under these circumstances.

If A and B do not have compatible sizes, an exception is generated.

The `!=` operator behaves the same except that it fills the result
with True values.

The other comparison operators now raise exceptions if a quantity
that is not dimensionless is compared to a plain number. For
effectively dimensionless quantities the comparison is now
consistent. Previously, if
    n = pq.s * pq.kHz,
then n < 2 would return True but n.simplified < 2 would return
False. Now, both return False.

Comparison between incompatible quantities raises exceptions as
before.
Typo caused __gt__ to behave like __ge__. No more.
@wagenadl
Copy link
Contributor Author

Implementation detail: To keep things simple, I have reimplemented Quantity's __gt__ operator as (self - other).magnitude > 0. If this is undesirable because of corner cases with unsigned integer dtypes, I am happy to rework it.

@apdavison
Copy link
Contributor

Thank you for this, the new behaviour as you describe it does seem to be an improvement. There is, however, a risk that it breaks downstream code that relies on the current behaviour, so I would like to test it first with some of the downstream projects, such as Neo.

I note that this would require #248 to be merged first. @wagenadl perhaps you could modify the implementation, at least temporarily, to not use the .plain property, while we discuss that in parallel?

In addition, please could you add some unit tests, for the "standard" usage, and for any potential corner cases you can foresee?

The "test_comparison.py" test file now verifies that the behavior
of the comparison operators matches the new specification.
This involved changing various assertions and adding some new ones.

Also, this patch now also works without the new “dimensionless_magnitude”
property.
@wagenadl
Copy link
Contributor Author

wagenadl commented Dec 3, 2024

I agree that this patch may break some extant code. It is, of course, not up to me to decide whether that is an acceptable cost for the improvement.

To reiterate why I think the new behavior is preferable: Both versions of the code agree that pq.ms == pq.s is false, which makes sense because 1 ms ≠ 1 s. However, under the old behavior, both pq.ms == 1 and pq.s == 1 were true. This violated transitivity (if A==B and B==C then A==C). Under the new behavior, pq.ms == 1 and pq.s == 1 are both false. Likewise, under the old behavior, pq.s * pq.kHz == 1 was true, even though manifestly 1 s × 1 kHz = 1000 ≠ 1.

In my latest commit, the "test_comparison.py" test file now verifies that the behavior of the comparison operators matches the new specification. This involved changing various assertions and adding some new ones.

Also, this patch now also works independently of the recently merged “dimensionless_magnitude” property.

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.

Comparison between quantities and plain numbers is inconsistent and unsafe
2 participants