1 # Complex numbers 2 # --------------- 3 4 # [Now that Python has a complex data type built-in, this is not very 5 # useful, but it's still a nice example class] 6 7 # This module represents complex numbers as instances of the class Complex. 8 # A Complex instance z has two data attribues, z.re (the real part) and z.im 9 # (the imaginary part). In fact, z.re and z.im can have any value -- all 10 # arithmetic operators work regardless of the type of z.re and z.im (as long 11 # as they support numerical operations). 12 # 13 # The following functions exist (Complex is actually a class): 14 # Complex([re [,im]) -> creates a complex number from a real and an imaginary part 15 # IsComplex(z) -> true iff z is a complex number (== has .re and .im attributes) 16 # ToComplex(z) -> a complex number equal to z; z itself if IsComplex(z) is true 17 # if z is a tuple(re, im) it will also be converted 18 # PolarToComplex([r [,phi [,fullcircle]]]) -> 19 # the complex number z for which r == z.radius() and phi == z.angle(fullcircle) 20 # (r and phi default to 0) 21 # exp(z) -> returns the complex exponential of z. Equivalent to pow(math.e,z). 22 # 23 # Complex numbers have the following methods: 24 # z.abs() -> absolute value of z 25 # z.radius() == z.abs() 26 # z.angle([fullcircle]) -> angle from positive X axis; fullcircle gives units 27 # z.phi([fullcircle]) == z.angle(fullcircle) 28 # 29 # These standard functions and unary operators accept complex arguments: 30 # abs(z) 31 # -z 32 # +z 33 # not z 34 # repr(z) == `z` 35 # str(z) 36 # hash(z) -> a combination of hash(z.re) and hash(z.im) such that if z.im is zero 37 # the result equals hash(z.re) 38 # Note that hex(z) and oct(z) are not defined. 39 # 40 # These conversions accept complex arguments only if their imaginary part is zero: 41 # int(z) 42 # long(z) 43 # float(z) 44 # 45 # The following operators accept two complex numbers, or one complex number 46 # and one real number (int, long or float): 47 # z1 + z2 48 # z1 - z2 49 # z1 * z2 50 # z1 / z2 51 # pow(z1, z2) 52 # cmp(z1, z2) 53 # Note that z1 % z2 and divmod(z1, z2) are not defined, 54 # nor are shift and mask operations. 55 # 56 # The standard module math does not support complex numbers. 57 # The cmath modules should be used instead. 58 # 59 # Idea: 60 # add a class Polar(r, phi) and mixed-mode arithmetic which 61 # chooses the most appropriate type for the result: 62 # Complex for +,-,cmp 63 # Polar for *,/,pow 64 65 import math 66 import sys 67 68 twopi = math.pi*2.0 69 halfpi = math.pi/2.0 70 71 def IsComplex(obj): 72 return hasattr(obj, 're') and hasattr(obj, 'im') 73 74 def ToComplex(obj): 75 if IsComplex(obj): 76 return obj 77 elif isinstance(obj, tuple): 78 return Complex(*obj) 79 else: 80 return Complex(obj) 81 82 def PolarToComplex(r = 0, phi = 0, fullcircle = twopi): 83 phi = phi * (twopi / fullcircle) 84 return Complex(math.cos(phi)*r, math.sin(phi)*r) 85 86 def Re(obj): 87 if IsComplex(obj): 88 return obj.re 89 return obj 90 91 def Im(obj): 92 if IsComplex(obj): 93 return obj.im 94 return 0 95 96 class Complex: 97 98 def __init__(self, re=0, im=0): 99 _re = 0 100 _im = 0 101 if IsComplex(re): 102 _re = re.re 103 _im = re.im 104 else: 105 _re = re 106 if IsComplex(im): 107 _re = _re - im.im 108 _im = _im + im.re 109 else: 110 _im = _im + im 111 # this class is immutable, so setting self.re directly is 112 # not possible. 113 self.__dict__['re'] = _re 114 self.__dict__['im'] = _im 115 116 def __setattr__(self, name, value): 117 raise TypeError, 'Complex numbers are immutable' 118 119 def __hash__(self): 120 if not self.im: 121 return hash(self.re) 122 return hash((self.re, self.im)) 123 124 def __repr__(self): 125 if not self.im: 126 return 'Complex(%r)' % (self.re,) 127 else: 128 return 'Complex(%r, %r)' % (self.re, self.im) 129 130 def __str__(self): 131 if not self.im: 132 return repr(self.re) 133 else: 134 return 'Complex(%r, %r)' % (self.re, self.im) 135 136 def __neg__(self): 137 return Complex(-self.re, -self.im) 138 139 def __pos__(self): 140 return self 141 142 def __abs__(self): 143 return math.hypot(self.re, self.im) 144 145 def __int__(self): 146 if self.im: 147 raise ValueError, "can't convert Complex with nonzero im to int" 148 return int(self.re) 149 150 def __long__(self): 151 if self.im: 152 raise ValueError, "can't convert Complex with nonzero im to long" 153 return long(self.re) 154 155 def __float__(self): 156 if self.im: 157 raise ValueError, "can't convert Complex with nonzero im to float" 158 return float(self.re) 159 160 def __cmp__(self, other): 161 other = ToComplex(other) 162 return cmp((self.re, self.im), (other.re, other.im)) 163 164 def __rcmp__(self, other): 165 other = ToComplex(other) 166 return cmp(other, self) 167 168 def __nonzero__(self): 169 return not (self.re == self.im == 0) 170 171 abs = radius = __abs__ 172 173 def angle(self, fullcircle = twopi): 174 return (fullcircle/twopi) * ((halfpi - math.atan2(self.re, self.im)) % twopi) 175 176 phi = angle 177 178 def __add__(self, other): 179 other = ToComplex(other) 180 return Complex(self.re + other.re, self.im + other.im) 181 182 __radd__ = __add__ 183 184 def __sub__(self, other): 185 other = ToComplex(other) 186 return Complex(self.re - other.re, self.im - other.im) 187 188 def __rsub__(self, other): 189 other = ToComplex(other) 190 return other - self 191 192 def __mul__(self, other): 193 other = ToComplex(other) 194 return Complex(self.re*other.re - self.im*other.im, 195 self.re*other.im + self.im*other.re) 196 197 __rmul__ = __mul__ 198 199 def __div__(self, other): 200 other = ToComplex(other) 201 d = float(other.re*other.re + other.im*other.im) 202 if not d: raise ZeroDivisionError, 'Complex division' 203 return Complex((self.re*other.re + self.im*other.im) / d, 204 (self.im*other.re - self.re*other.im) / d) 205 206 def __rdiv__(self, other): 207 other = ToComplex(other) 208 return other / self 209 210 def __pow__(self, n, z=None): 211 if z is not None: 212 raise TypeError, 'Complex does not support ternary pow()' 213 if IsComplex(n): 214 if n.im: 215 if self.im: raise TypeError, 'Complex to the Complex power' 216 else: return exp(math.log(self.re)*n) 217 n = n.re 218 r = pow(self.abs(), n) 219 phi = n*self.angle() 220 return Complex(math.cos(phi)*r, math.sin(phi)*r) 221 222 def __rpow__(self, base): 223 base = ToComplex(base) 224 return pow(base, self) 225 226 def exp(z): 227 r = math.exp(z.re) 228 return Complex(math.cos(z.im)*r,math.sin(z.im)*r) 229 230 231 def checkop(expr, a, b, value, fuzz = 1e-6): 232 print ' ', a, 'and', b, 233 try: 234 result = eval(expr) 235 except: 236 result = sys.exc_type 237 print '->', result 238 if isinstance(result, str) or isinstance(value, str): 239 ok = (result == value) 240 else: 241 ok = abs(result - value) <= fuzz 242 if not ok: 243 print '!!\t!!\t!! should be', value, 'diff', abs(result - value) 244 245 def test(): 246 print 'test constructors' 247 constructor_test = ( 248 # "expect" is an array [re,im] "got" the Complex. 249 ( (0,0), Complex() ), 250 ( (0,0), Complex() ), 251 ( (1,0), Complex(1) ), 252 ( (0,1), Complex(0,1) ), 253 ( (1,2), Complex(Complex(1,2)) ), 254 ( (1,3), Complex(Complex(1,2),1) ), 255 ( (0,0), Complex(0,Complex(0,0)) ), 256 ( (3,4), Complex(3,Complex(4)) ), 257 ( (-1,3), Complex(1,Complex(3,2)) ), 258 ( (-7,6), Complex(Complex(1,2),Complex(4,8)) ) ) 259 cnt = [0,0] 260 for t in constructor_test: 261 cnt[0] += 1 262 if ((t[0][0]!=t[1].re)or(t[0][1]!=t[1].im)): 263 print " expected", t[0], "got", t[1] 264 cnt[1] += 1 265 print " ", cnt[1], "of", cnt[0], "tests failed" 266 # test operators 267 testsuite = { 268 'a+b': [ 269 (1, 10, 11), 270 (1, Complex(0,10), Complex(1,10)), 271 (Complex(0,10), 1, Complex(1,10)), 272 (Complex(0,10), Complex(1), Complex(1,10)), 273 (Complex(1), Complex(0,10), Complex(1,10)), 274 ], 275 'a-b': [ 276 (1, 10, -9), 277 (1, Complex(0,10), Complex(1,-10)), 278 (Complex(0,10), 1, Complex(-1,10)), 279 (Complex(0,10), Complex(1), Complex(-1,10)), 280 (Complex(1), Complex(0,10), Complex(1,-10)), 281 ], 282 'a*b': [ 283 (1, 10, 10), 284 (1, Complex(0,10), Complex(0, 10)), 285 (Complex(0,10), 1, Complex(0,10)), 286 (Complex(0,10), Complex(1), Complex(0,10)), 287 (Complex(1), Complex(0,10), Complex(0,10)), 288 ], 289 'a/b': [ 290 (1., 10, 0.1), 291 (1, Complex(0,10), Complex(0, -0.1)), 292 (Complex(0, 10), 1, Complex(0, 10)), 293 (Complex(0, 10), Complex(1), Complex(0, 10)), 294 (Complex(1), Complex(0,10), Complex(0, -0.1)), 295 ], 296 'pow(a,b)': [ 297 (1, 10, 1), 298 (1, Complex(0,10), 1), 299 (Complex(0,10), 1, Complex(0,10)), 300 (Complex(0,10), Complex(1), Complex(0,10)), 301 (Complex(1), Complex(0,10), 1), 302 (2, Complex(4,0), 16), 303 ], 304 'cmp(a,b)': [ 305 (1, 10, -1), 306 (1, Complex(0,10), 1), 307 (Complex(0,10), 1, -1), 308 (Complex(0,10), Complex(1), -1), 309 (Complex(1), Complex(0,10), 1), 310 ], 311 } 312 for expr in sorted(testsuite): 313 print expr + ':' 314 t = (expr,) 315 for item in testsuite[expr]: 316 checkop(*(t+item)) 317 318 319 if __name__ == '__main__': 320 test() 321