The exponential and trigonometric functions in Python
# The exponential and trigonometric functions
# in Python also accept complex arguments when
# we use the 'cmath' library.
#
# One of the most important relationships
# involving complex numbers is Euler's formula:
#
# exp(beta*j) = cos(beta) + j*sin(beta),
#
# where beta is a real number measured in
# radians.
import cmath
import math
beta = math.pi/3
print( "\nexp(beta*j):" )
print( cmath.exp(beta*1j) )
print( "\ncos(beta) + j*sin(beta):" )
print( math.cos(beta) + 1j*math.sin(beta) )
# Apart from possible floating-point rounding,
# these two values are the same.
#
# Notice that the magnitude of
#
# exp(beta*j)
#
# is always 1. Changing beta simply changes its
# argument.
#
# Thus, as beta varies,
#
# exp(beta*j)
#
# moves around the unit circle.
# Euler's formula also allows us to express the
# trigonometric functions using exponential
# functions.
#
# Starting with
#
# exp(beta*j) = cos(beta) + j*sin(beta),
#
# take the complex conjugate of both sides:
#
# conj(exp(beta*j))
#
# = cos(beta) - j*sin(beta).
#
# Since
#
# conj(exp(beta*j)) = exp(-beta*j),
#
# we have
#
# exp(-beta*j) = cos(beta) - j*sin(beta).
# Adding these two equations gives
#
# exp(beta*j) + exp(-beta*j)
#
# = 2*cos(beta).
#
# Therefore,
#
# exp(beta*j) + exp(-beta*j)
# cos(beta) = -----------------------------.
# 2
print( "\ncos(beta):" )
print( math.cos(beta) )
print( "\nCalculated using exponentials:" )
print(
(cmath.exp(beta*1j)
+ cmath.exp(-beta*1j))/2
)
# Subtracting the two equations instead gives
#
# exp(beta*j) - exp(-beta*j)
#
# = 2*j*sin(beta).
#
# Therefore,
#
# exp(beta*j) - exp(-beta*j)
# sin(beta) = -----------------------------.
# 2*j
print( "\nsin(beta):" )
print( math.sin(beta) )
print( "\nCalculated using exponentials:" )
print(
(cmath.exp(beta*1j)
- cmath.exp(-beta*1j))/(2j)
)
# These identities are often written as
#
# exp(j*beta) + exp(-j*beta)
# cos(beta) = -----------------------------
# 2
#
# and
#
# exp(j*beta) - exp(-j*beta)
# sin(beta) = -----------------------------.
# 2*j
#
# These are not merely computational tricks.
# They show that the exponential and
# trigonometric functions are closely connected
# through complex numbers.
# Python also allows the arguments of the
# trigonometric functions themselves to be
# complex when we use the 'cmath' library.
#
# For example,
z = 0.4 + 0.7j
print( "\nA complex value z:" )
print( z )
print( "\nexp(z):" )
print( cmath.exp(z) )
print( "\ncos(z):" )
print( cmath.cos(z) )
print( "\nsin(z):" )
print( cmath.sin(z) )
# The exponential definitions of sine and
# cosine continue to work even when z itself is
# complex:
#
# exp(j*z) + exp(-j*z)
# cos(z) = ---------------------
# 2
#
# and
#
# exp(j*z) - exp(-j*z)
# sin(z) = ---------------------.
# 2*j
print( "\ncos(z) using exponentials:" )
print(
(cmath.exp(1j*z)
+ cmath.exp(-1j*z))/2
)
print( "\nsin(z) using exponentials:" )
print(
(cmath.exp(1j*z)
- cmath.exp(-1j*z))/(2j)
)
# The exponential function is also closely
# related to powers and logarithms.
#
# Recall that, for positive real numbers,
#
# w
# z = exp(w*ln(z)).
#
# In Python, as in most programming languages,
# the function
#
# log(...)
#
# means the NATURAL LOGARITHM.
#
# That is,
#
# log(z)
#
# means
#
# ln(z),
#
# the logarithm with base e.
#
# It does NOT mean the base-10 logarithm.
#
# For real numbers, the 'math' library provides
#
# math.log(...)
#
# for the natural logarithm and
#
# math.log10(...)
#
# for the base-10 logarithm.
print( "\nNatural logarithm of 10:" )
print( math.log(10) )
print( "\nBase-10 logarithm of 10:" )
print( math.log10(10) )
# The 'cmath' library extends the natural
# logarithm to complex numbers.
#
# Thus,
#
# cmath.log(z)
#
# is the natural logarithm of a complex number.
#
# Again, 'log' means NATURAL logarithm.
# If
#
# z = r*exp(j*theta),
#
# then a complex logarithm of z is
#
# log(z) = log(r) + j*theta.
#
# Since an argument is not unique, however,
#
# theta + 2*k*pi
#
# represents the same direction for every
# integer k.
#
# Consequently, the complex logarithm is
# naturally multivalued:
#
# log(r) + j*(theta + 2*k*pi).
#
# Python's cmath.log(z) returns the PRINCIPAL
# value, using the principal argument of z.
z = 2 + 3j
print( "\nA complex value z:" )
print( z )
print( "\nThe principal value of log(z):" )
print( cmath.log(z) )
# We can see the connection with magnitude and
# argument directly.
#
# The real component of log(z) is
#
# log(|z|),
#
# while its imaginary component is the
# principal argument of z.
print( "\nlog(|z|):" )
print( math.log(abs(z)) )
print( "\nThe principal argument of z:" )
print( cmath.phase(z) )
# We can reconstruct z by exponentiating its
# logarithm.
print( "\nexp(log(z)):" )
print( cmath.exp(cmath.log(z)) )
# This gives us a very powerful way to define
# complex powers.
#
# For complex numbers z and w, with z nonzero,
# we can calculate a principal value of
#
# w
# z
#
# using
#
# w
# z = exp(log(z)*w).
#
# Here, log(z) is the principal complex
# logarithm returned by Python.
z = 2 + 3j
w = 0.4 - 0.7j
print( "\nz:" )
print( z )
print( "\nw:" )
print( w )
print( "\nz**w calculated directly:" )
print( z**w )
print( "\nz**w calculated using exp and log:" )
print( cmath.exp(cmath.log(z)*w) )
# Apart from possible floating-point rounding,
# these calculations give the same principal
# value.
#
# Notice that exponentiation in Python uses
#
# **
#
# and NOT
#
# ^.
#
# The operator '^' is the bitwise exclusive-OR
# operator in Python.
# Thus, the exponential and logarithm allow us
# to make sense of powers in which both the
# base and the exponent may be complex:
#
# w
# z = exp(w*log(z)).
#
# This is an important extension of the familiar
# rules for real exponentials and logarithms.
# There is one important mathematical detail.
#
# Because a nonzero complex number has
# infinitely many possible arguments,
#
# theta + 2*k*pi,
#
# it also has infinitely many possible
# logarithms:
#
# log(|z|) + j*(theta + 2*k*pi).
#
# If these different logarithms are used in
#
# exp(w*log(z)),
#
# they may produce different values when w is
# not an integer.
#
# Programming languages must choose one value
# when evaluating functions such as
#
# log(z)
#
# and
#
# z**w.
#
# Python uses the principal complex logarithm
# to select the principal value.
#
# Thus, when working with complex numbers, it
# is important to distinguish between the
# mathematical multivalued logarithm or power
# and the single principal value returned by a
# programming language.