| 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()
|
|---|