The complex conjugate in Python
# The complex conjugate of a complex number is
# obtained by changing the sign of its imaginary
# component.
#
# If
#
#     z = a + bj,
#
# then the complex conjugate of z is
#
#     zc = a - bj.
#
# The complex conjugate is often written
#
#          _
#     zc = z.
#
# In Python, the method 'conjugate()' returns
# the complex conjugate of a complex number.

z = 3.2 - 4.7j

print( "\nThe complex conjugate of z:" )

print( z.conjugate() )

# We can also construct the complex conjugate
# directly from the real and imaginary
# components.

print( z.real - 1j*z.imag )

print( "z       =", z )
print( "conj(z) =", z.conjugate() )


# Taking the complex conjugate twice returns the
# original complex number:
#
#     conj(conj(z)) = z.
#
# Thus, conjugation is its own inverse.

print( "\nThe conjugate of the conjugate:" )

print( z.conjugate().conjugate() )
print( z )

print( z.conjugate().conjugate() == z )


# The conjugate of a real number is the real
# number itself. If
#
#     z = a + 0j,
#
# then
#
#     conj(z) = z.

print( "\nThe conjugate of a real number:" )

print( (3.8 + 0j).conjugate() )

# Consequently, a complex number is real if and
# only if
#
#     conj(z) = z.


# Conjugation changes the sign of the imaginary
# component but does not change the real
# component:
#
#     Re(conj(z)) = Re(z)
#
#     Im(conj(z)) = -Im(z).

print( "\nReal and imaginary components:" )

print( z.conjugate().real )
print( z.real )

print( z.conjugate().imag )
print( -z.imag )


# The conjugate of an additive inverse is the
# additive inverse of the conjugate:
#
#     conj(-z) = -conj(z).

print( "\nConjugation and the additive inverse:" )

print( (-z).conjugate() )
print( -z.conjugate() )


# Now consider two complex numbers.

w = 5.3 + 3.2j
z = 3.2 - 4.7j


# The conjugate of a sum is the sum of the
# conjugates:
#
#     conj(w + z) = conj(w) + conj(z).

print( "\nThe conjugate of a sum:" )

print( (w + z).conjugate() )
print( w.conjugate() + z.conjugate() )


# Similarly, the conjugate of a difference is
# the difference of the conjugates:
#
#     conj(w - z) = conj(w) - conj(z).

print( "\nThe conjugate of a difference:" )

print( (w - z).conjugate() )
print( w.conjugate() - z.conjugate() )


# The conjugate of a product is the product of
# the conjugates:
#
#     conj(w*z) = conj(w)*conj(z).

print( "\nThe conjugate of a product:" )

print( (w*z).conjugate() )
print( w.conjugate()*z.conjugate() )


# A complex number multiplied by its conjugate
# is always a nonnegative real number.
#
# If
#
#     z = a + bj,
#
# then
#
#     z*conj(z)
#
#       = (a + bj)(a - bj)
#
#       = a**2 + b**2
#
#       = |z|**2.
#
# Thus,
#
#     z*conj(z) = |z|**2.

print( "\nA number times its conjugate:" )

print( z*z.conjugate() )
print( abs(z)**2 )


# A complex number and its conjugate have the
# same absolute value:
#
#     |conj(z)| = |z|.
#
# Geometrically, conjugation reflects a point
# across the real axis, which does not change
# its distance from the origin.

print( "\nAbsolute value of the conjugate:" )

print( abs(z.conjugate()) )
print( abs(z) )


# Conjugation reverses the sign of the argument:
#
#     arg(conj(z)) = -arg(z)    (mod 2*pi).
#
# Geometrically, this again corresponds to
# reflecting the complex number across the real
# axis.

import cmath

print( "\nArgument of the conjugate:" )

print( cmath.phase(z.conjugate()) )
print( -cmath.phase(z) )


# The real component of a complex number can be
# obtained by adding the number and its conjugate.
#
# If
#
#     z = a + bj,
#
# then
#
#     z + conj(z)
#
#       = (a + bj) + (a - bj)
#
#       = 2a.
#
# Therefore,
#
#                 z + conj(z)
#     Re(z) =     -------------.
#                       2

print( "\nRecovering the real component:" )

print( z.real )
print( (z + z.conjugate())/2 )


# Similarly, subtracting the conjugate gives
#
#     z - conj(z)
#
#       = (a + bj) - (a - bj)
#
#       = 2bj.
#
# Therefore,
#
#                      z - conj(z)
#     Im(z)j =         -------------.
#                           2
#
# Notice that this gives the imaginary part
# together with its factor j. We have not yet
# introduced division by a complex number.

print( "\nRecovering the imaginary component times j:" )

print( 1j*z.imag )
print( (z - z.conjugate())/2 )


# Consequently,
#
#     z + conj(z)
#
# is always real, while
#
#     z - conj(z)
#
# is always purely imaginary.

print( "\nSum and difference with the conjugate:" )

print( z + z.conjugate() )
print( z - z.conjugate() )


# Conjugation also behaves naturally with
# nonnegative integer powers:
#
#     conj(z**n) = conj(z)**n.
#
# For example:

print( "\nConjugation and powers:" )

print( (z**5).conjugate() )
print( z.conjugate()**5 )


# Finally, conjugation has a simple geometric
# interpretation.
#
# If
#
#     z = a + bj,
#
# then
#
#     conj(z) = a - bj.
#
# Thus, z and conj(z) have:
#
#     - the same real component,
#     - opposite imaginary components,
#     - the same absolute value, and
#     - opposite arguments (modulo 2*pi).
#
# Geometrically, taking the complex conjugate
# reflects a complex number across the real
# axis.