Switching between rectangular and polar representations in Python
# A complex number can be represented in either
# rectangular or polar form.
#
# In rectangular form, we write
#
#     z = a + bj,
#
# where
#
#     a = Re(z)
#
# and
#
#     b = Im(z).
#
# In polar form, we write
#
#     z = r phase theta,
#
# where
#
#     r = |z|
#
# is the magnitude and theta is the argument,
# or phase, of z.
#
# We will see how to mechanically convert
# between these two representations.

import cmath
import math


# First, suppose that a complex number is given
# in rectangular form:
#
#     z = a + bj.

z = 3.2 - 4.7j

print( "\nThe complex number z:" )
print( z )


# Extract the real and imaginary components.

a = z.real
b = z.imag

print( "\nThe real component a:" )
print( a )

print( "\nThe imaginary component b:" )
print( b )


# To convert from rectangular form to polar
# form, first calculate the magnitude.
#
# From the Pythagorean theorem,
#
#                  __________
#                 /  2     2
#     r = |z| =  /  a  +  b .
#               V

r = math.sqrt(a**2 + b**2)

print( "\nThe magnitude calculated manually:" )
print( r )


# Python can calculate the same value directly
# using abs(...).

print( "\nThe magnitude using abs(z):" )
print( abs(z) )


# Next, calculate the argument theta.
#
# Geometrically,
#
#             b
#     tan(theta) = -.
#             a
#
# It may therefore be tempting to calculate
#
#     atan(b/a).
#
# This is not reliable because the quotient b/a
# does not contain enough information to
# determine the quadrant.
#
# Instead, use
#
#     math.atan2(b, a).
#
# The function atan2(...) considers the signs
# of both a and b and therefore determines the
# correct quadrant.

theta = math.atan2(b, a)

print( "\nThe argument calculated using atan2:" )
print( theta )


# Python can calculate the principal argument
# of a complex number directly using
# cmath.phase(...).

print( "\nThe argument using cmath.phase(z):" )
print( cmath.phase(z) )


# These angles are measured in radians.
#
# We can convert the argument to degrees using
# math.degrees(...).

print( "\nThe argument in degrees:" )
print( math.degrees(theta) )


# We have therefore mechanically converted
#
#     z = a + bj
#
# into
#
#     z = r phase theta.
#
# The conversion formulas are
#
#                  __________
#                 /  2     2
#     r = |z| =  /  a  +  b
#               V
#
# and
#
#     theta = atan2(b, a).
#
# In Python, these can be calculated more
# directly as
#
#     r     = abs(z)
#
#     theta = cmath.phase(z).


# Now suppose instead that we are given a
# complex number in polar form:
#
#     z = r phase theta.
#
# For example, suppose

r = 5
theta = math.pi/3

print( "\nMagnitude r:" )
print( r )

print( "\nArgument theta in radians:" )
print( theta )

print( "\nArgument theta in degrees:" )
print( math.degrees(theta) )


# To convert from polar form to rectangular
# form, use
#
#     a = r*cos(theta)
#
# and
#
#     b = r*sin(theta).

a = r*math.cos(theta)
b = r*math.sin(theta)

print( "\nThe real component a:" )
print( a )

print( "\nThe imaginary component b:" )
print( b )


# The rectangular representation is therefore
#
#     a + bj.

z = a + 1j*b

print( "\nThe rectangular representation:" )
print( z )


# Thus, the mechanical conversion from polar to
# rectangular form is
#
#     a = r*cos(theta)
#
#     b = r*sin(theta)
#
# followed by
#
#     z = a + bj.
#
# Equivalently, we can write this directly as
#
#     z = r*cos(theta) + j*r*sin(theta)
#
#       = r*(cos(theta) + j*sin(theta)).


z_trig = r*(
    math.cos(theta) + 1j*math.sin(theta)
)

print( "\nUsing the trigonometric form:" )
print( z_trig )


# Recall Euler's formula:
#
#     exp(j*theta)
#
#         = cos(theta) + j*sin(theta).
#
# Therefore,
#
#     r*(cos(theta) + j*sin(theta))
#
#         = r*exp(j*theta).
#
# Consequently, our notation
#
#     r phase theta
#
# represents exactly the same complex number as
#
#     r*exp(j*theta).
#
# Thus,
#
#     r phase theta
#
#       = r*(cos(theta) + j*sin(theta))
#
#       = r*exp(j*theta).


z_exp = r*cmath.exp(1j*theta)

print( "\nUsing r*exp(j*theta):" )
print( z_exp )


# The rectangular, trigonometric, exponential,
# and phase representations all describe the
# same complex number:
#
#     a + bj
#
#     r*(cos(theta) + j*sin(theta))
#
#     r*exp(j*theta)
#
#     r phase theta.


# Let us verify that converting from rectangular
# to polar and then back to rectangular returns
# the original complex number.

z = -2.5 + 6.3j

print( "\nOriginal rectangular value:" )
print( z )


# Rectangular to polar:

r = abs(z)
theta = cmath.phase(z)

print( "\nMagnitude:" )
print( r )

print( "\nArgument in radians:" )
print( theta )

print( "\nArgument in degrees:" )
print( math.degrees(theta) )


# Polar back to rectangular:

a = r*math.cos(theta)
b = r*math.sin(theta)

z_reconstructed = a + 1j*b

print( "\nReconstructed rectangular value:" )
print( z_reconstructed )


# We can also reconstruct the number directly
# from the exponential representation.

z_reconstructed_exp = r*cmath.exp(1j*theta)

print( "\nReconstructed using r*exp(j*theta):" )
print( z_reconstructed_exp )


# Apart from possible floating-point rounding,
# both reconstructed values equal the original
# complex number.


# Python's 'cmath' library also provides a
# function that performs the polar-to-
# rectangular conversion directly:
#
#     cmath.rect(r, theta).
#
# It returns
#
#     r*(cos(theta) + j*sin(theta)).

z_rect = cmath.rect(r, theta)

print( "\nUsing cmath.rect(r, theta):" )
print( z_rect )


# Similarly, cmath.polar(z) returns both the
# magnitude and principal argument:
#
#     (r, theta).

polar = cmath.polar(z)

print( "\nUsing cmath.polar(z):" )
print( polar )


# The first value is the magnitude and the
# second is the principal argument.

print( "\nMagnitude from cmath.polar(z):" )
print( polar[0] )

print( "\nArgument from cmath.polar(z):" )
print( polar[1] )


# There is one important special case.
#
# If
#
#     z = 0,
#
# then
#
#     r = |z| = 0.
#
# However, the argument of zero is not
# mathematically defined.
#
# Python returns

print( "\ncmath.phase(0):" )
print( cmath.phase(0) )


# as a computational convention, but
# mathematically
#
#     arg(0)
#
# is undefined because the zero vector has no
# direction.


# Finally, remember that arguments are not
# unique.
#
# If theta is an argument of a nonzero complex
# number, then
#
#     theta + 2*k*pi
#
# is also an argument for every integer k.
#
# This follows because
#
#     exp(j*(theta + 2*k*pi))
#
#       = exp(j*theta)*exp(j*2*k*pi)
#
#       = exp(j*theta).
#
# Python's cmath.phase(z) returns the principal
# argument.
#
# Thus, the central conversion formulas are:
#
# RECTANGULAR TO POLAR
#
#     z = a + bj
#
#                  __________
#                 /  2     2
#     r = |z| =  /  a  +  b
#               V
#
#     theta = atan2(b, a)
#
# or, directly in Python,
#
#     r     = abs(z)
#     theta = cmath.phase(z)
#
# or
#
#     r, theta = cmath.polar(z)
#
#
# POLAR TO RECTANGULAR
#
#     z = r phase theta
#
#     a = r*cos(theta)
#     b = r*sin(theta)
#
#     z = a + bj
#
#       = r*(cos(theta) + j*sin(theta))
#
#       = r*exp(j*theta)
#
# or, directly in Python,
#
#     z = cmath.rect(r, theta).