Implementing Comprehensive Number Comparison in Python?

I know that comparison operators with complex numbers cannot be defined at all. This is why python throws a TypeError exception when trying to use complex comparison. I understand why this is so (please do not leave the topic trying to explain why it is impossible to compare two complex numbers).

However, in this particular case, I would like to implement a comprehensive comparison of numbers based on their values. In other words, for z1 and z2 complex values, then z1 > z2 if-and-only-if abs(z1) > abs(z2) , where abs() implements the value of the complex number, as in numpy.abs() .

I came up with a solution (at least I think I have one):

 import numpy as np class CustomComplex(complex): def __lt__(self, other): return np.abs(self) < np.abs(other) def __le__(self, other): return np.abs(self) <= np.abs(other) def __eq__(self, other): return np.abs(self) == np.abs(other) def __ne__(self, other): return np.abs(self) != np.abs(other) def __gt__(self, other): return np.abs(self) > np.abs(other) def __ge__(self, other): return np.abs(self) >= np.abs(other) complex = CustomComplex 

This seems to work, but I have a few questions:

  • Is this a way to go or is there a better alternative?
  • I want my package to transparently work with the integrated data type complex , as well as numpy.complex . How can this be done elegantly without code duplication?
+7
python numpy complex-numbers
source share
2 answers

I'm afraid that I will be off topic (yes, I completely read your message :-)). So, Python really allows you to compare complex numbers this way, because you can define all the statements separately, even if I strongly recommend that you not override __eq__ as you did: you are saying 1 == -1 !

IMHO the problem there will be spring on your face at one point (or the face of anyone who will use your package): when using equalities and inequalities, ordinary mortals (and most python code) make simple assumptions like -1 != 1 , and (a <= b) && (b <= a) means a == b . And you simply cannot have these 2 assumptions at the same time true for purely mathematical reasons.

Another classic assumption that a <= b equivalent to -b <= -a . But with you, the preorder a <= b equivalent to -a <= -b !

Having said that, I will try to answer your 2 questions:

  • 1: IMHO this is a harmful path (as described above), but I have no better alternative ...
  • 2: I think mixin can be an elegant way to limit code duplication.

Code example (based on your own code but not validated):

 import numpy as np class ComplexOrder(Object): def __lt__(self, other): return np.absolute(self) < np.absolute(other) # ... keep want you want (including or not eq and ne) def __ge__(self, other): return np.absolute(self) >= np.absolute(other) class OrderedComplex(ComplexOrder, complex): def __init__(self, real, imag = 0): complex.__init__(self, real, imag) class NPOrderedComplex64(ComplexOrder, np.complex64): def __init__(self, real = 0): np.complex64.__init__(self, real) 
+5
source share

I will give up all the reasons why this might be a bad idea, as per your request.

Is this a way to go or is there a better alternative?

No need to go with numpy when regular abs takes complex numbers and is much faster *. There is also convenient total_ordering in functools , which is well suited for such simple comparisons if you want to reduce the code (but it can be slower):

 from functools import total_ordering @total_ordering class CustomComplex(complex): def __eq__(self, other): return abs(self) == abs(other) def __lt__(self, other): return abs(self) < abs(other) 

(This is all the code you need.)


I want my package to transparently work with the built-in complex data type, as well as with numpy.complex. How can this be done elegantly without code duplication?

It automatically works when the correct argument is a normal complex (or any) number:

 >>> CustomComplex(1+7j) < 2+8j True 

But the best thing you can do if you want to use < operators, etc., and not functions. The complex type does not allow __lt__ to be set, and the TypeError is hard-coded.

If you want to make such comparisons in normal complex numbers, you must define and use your own comparison functions instead of the usual operators. Or just use abs(a) < abs(b) , which is clear and not very verbose.


* Dates of built-in abs and numpy.abs :

 >>> timeit.timeit('abs(7+6j)') 0.10257387161254883 >>> timeit.timeit('np.abs(7+6j)', 'import numpy as np') 1.6638610363006592 
+1
source share

All Articles