|
 |
CS 3723
Programming Languages |
Python Complex Class
|
|
Here is the Complex class from the
final exam
with a few extra features:
methods for the conjugate: conj(a + bj) = (a - bj),
absolute value squared: abs_sq(a + bj) = a^2 + b^2,
and absolute value: abs(a + bj) = √(a^2 + b^2).
I also tried out making the data members private and using the same names
for getReal and getImag as the original data members.
Complex
Class in Python, constructed "from scratch" |
# complex.py: complex numbers "from scratch"
# Builtin complex never uses cap C
import math
class Complex(object):
def __init__(self,real,imag=0):
self.__real = float(real)
self.__imag = float(imag)
def __str__(self):
return "(%.8g+%.8gj)" % \
(self.__real, self.__imag)
def __add__(self,other):
return Complex(self.__real + other.__real,
self.__imag + other.__imag)
def __sub__(self,other):
return Complex(self.__real - other.__real,
self.__imag - other.__imag)
def __mul__(self,other):
return Complex(self.__real * other.__real -
self.__imag * other.__imag,
self.__real * other.__imag +
self.__imag * other.__real)
def __div__(self,other):
denom = other.__real **2 + other.__imag **2
if denom == 0:
return None
return Complex((self.__real * other.__real +
self.__imag * other.__imag)/denom,
(self.__imag * other.__real -
self.__real * other.__imag)/denom)
def __eq__(self,other):
return self.__real == other.__real and \
self.__imag == other.__imag
def real(self):
return self.__real
def imag(self):
return self.__imag
def conj(self):
return Complex(self.__real, -self.__imag)
def abs_squared(self):
return self.__real **2 + self.__imag**2
def abs(self):
return math.sqrt(self.abs_squared())
| # test_complex.py: series for exp
import sys, math, cmath
from complex import Complex
def test_loop1(): # 8 multiplications
r = Complex(1.0,1.0)
r16 = Complex(16.0, 0.0)
p = Complex(1.0, 0.0)
print p
for i in range(0,8):
p = p*r
sys.stdout.write(str(p) + " " +
str(p == r16) + '\n')
def test_loop2(): # 8 divisions
r = Complex(1.0,1.0)
r16th = Complex(0.0625, 0.0)
p = Complex(1.0, 0.0)
print p
for i in range(0,8):
p = p/r
sys.stdout.write(str(p) + " " +
str(p == r16th) + '\n')
def test_8th_root_of_1(): # back to 1.0
r2 = math.sqrt(2.0)/2.0
r = Complex(r2,r2)
p = Complex(1.0, 0.0)
print p
for i in range(0,8):
p = p*r
sys.stdout.write(str(p) + '\n')
def cexp(x): # complex series for exp
term = Complex(1.0)
sum = term
for i in range(1,28):
term = term*x*Complex(1.0/float(i))
sum = sum + term
if i < 10:
sys.stdout.write(" ")
sys.stdout.write(str(i)+": " +
str(sum)+'\n')
return sum
sys.stdout.write("\nTest 8 mults\n")
test_loop1()
sys.stdout.write("\nTest 8 divisions\n")
test_loop2()
sys.stdout.write("\nTest 8 mults to 1.0\n")
test_8th_root_of_1()
sys.stdout.write("\nTest series for exp\n")
ipi = Complex(0.0, math.pi)
sys.stdout.write("e^ipi: " +
str(cexp(ipi))+'\n')
sys.stdout.write("\nTest Python's exp\n")
pij = 0+cmath.pi*1j
sys.stdout.write("cmath.exp(0+cmath.pi*1j):"
+ str(cmath.exp(pij)) + '\n')
|
This Complex class would have allowed a good project similar to
the Fraction class that we did. The Swiss mathematician
Leonhard Euler
discovered the formula: ,
as well as the infinite series for calculating the exponential
function:
This series converges for all x, in particular for
x = 0 + πj (in Python's notation). I would have had you calculate the sum of
the first 25 terms of this series as complex numbers. The program
above carries out this computation, and the series does indeed converge
to 1+0j. Notice that we are not using
Python's built-in complex numbers.
Output of
the program above |
% python test_complex.py
Test 8 mults
(1+0j)
(1+1j) False
(0+2j) False
(-2+2j) False
(-4+0j) False
(-4+-4j) False
(0+-8j) False
(8+-8j) False
(16+0j) True
Test 8 divisions
(1+0j)
(0.5+-0.5j) False
(0+-0.5j) False
(-0.25+-0.25j) False
(-0.25+0j) False
(-0.125+0.125j) False
(0+0.125j) False
(0.0625+0.0625j) False
(0.0625+0j) True
Test 8 mults to 1.0
(1+0j)
(0.70710678+0.70710678j)
(0+1j)
(-0.70710678+0.70710678j)
(-1+0j)
(-0.70710678+-0.70710678j)
(0+-1j)
(0.70710678+-0.70710678j)
(1+0j)
|
Test series for exp
ipi: (0+3.141592653589793j)
1, (1+3.141592653589793j)
2, (-3.934802200544679+ 3.141592653589793j)
3, (-3.934802200544679+ -2.026120126460176j)
4, ( 0.1239099258720886+-2.026120126460176j)
5, ( 0.1239099258720886+ 0.5240439134171688j)
6, (-1.211352842982501+ 0.5240439134171688j)
7, (-1.211352842982501+ -0.07522061590362306j)
8, (-0.9760222126236076+-0.07522061590362306j)
9, (-0.9760222126236076+ 0.006925270707505135j)
10, (-1.001829104013622+ 0.006925270707505135j)
11, (-1.001829104013622+ -0.000445160238209211j)
12, (-0.9998995297042177+-0.000445160238209211j)
13, (-0.9998995297042177+ 2.114256755840119e-05j)
14, (-1.000004167809142+ 2.114256755840119e-05j)
15, (-1.000004167809142+ -7.727858894290057e-07j)
16, (-0.9999998647395555+-7.727858894290057e-07j)
17, (-0.9999998647395555+ 2.241951071854486e-08j)
18, (-1.00000000352908+ 2.241951071854486e-08j)
19, (-1.00000000352908+ -5.289182787249905e-10j)
20, (-0.9999999999243493+-5.289182787249905e-10j)
21, (-0.9999999999243493+ 1.034818753582179e-11j)
22, (-1.000000000001356+ 1.034818753582179e-11j)
23, (-1.000000000001356+ -1.702841811102599e-13j)
24, (-0.9999999999999796+-1.702841811102599e-13j)
25, (-0.9999999999999796+ 2.737743473350955e-15j)
26, (-1 + 2.737743473350955e-15j)
27, (-1 + 3.051822933575691e-16j)
e^iπ:(-1 + 3.051822933575691e-16j)
cmath.exp(0+cmath.pi*1j): (-1+1.22460635382e-16j)
|
The computation above is converging to -1 plus
an extremely small number times j. The last line on the right above
shows the same calculation using Python's built-in complex
numbers. The two extremely small multiples of
j are different in the two computations, but this is of
no significance (a pun).
Finally the numbers below show the output of the same
computaton, carried to high precision, using the library mpmath.
Arbitrary Precision
Complex Numbers in Python, using mpmath library |
% python complex_mpmath.py
(0.0 + 3.141592653589793238462643383279502884197j)
5: ( 0.1239099258720889087677683620913040677485 + 0.5240439134171686530727684557912337378288j)
10: (-1.001829104013621442578255362236712553235 + 0.006925270707504804983831437768892036621276j)
15: (-1.000004167809142365237470033401378834922 - 0.0000007727858897634448344079259855673249670672j)
20: (-0.9999999999243491569656829463991101443086 - 0.0000000005289186131634290398091717071376516464896j)
25: (-0.9999999999999793635436587388265668563339 + 2.403305035355747041912550309652197855868e-15j)
30: (-1.000000000000000000030536341988518572233 + 3.108707909073023766021815358391073476033e-19j)
35: (-1.000000000000000000000002107755552885457 - 1.79029219970697302499873293037084385004e-25j)
40: (-0.9999999999999999999999999999994625280236 - 7.183693811264135340256222537220808666116e-30j)
45: (-0.9999999999999999999999999999999999866208 + 8.944603736934812058552449165710983774179e-37j)
50: (-1.0 + 2.242118931431719972951701196207462110809e-41j)
|
(Revision date: 2014-12-19.
Please use ISO 8601,
the International Standard.)
|