Designing Complex Numbers using Classes in Python


In a previous article, we talked about complex numbers in Python using built-in classes and attributes to perform operations on them. But, imagine if Python didn't come equipped with these conveniences. How would we go about implementing complex operations? We can explore that concept by utilizing built-in mathematical operations to create our own complex class in Python.

To start off, we need to initialize our complex number:

def __init__ (self, real, imag=0.0):
    '''Form complex number'''
    self.real = real
    self.imag = imag

As we've stated previously, complex numbers are created from a real and an imaginary number. The __init__ operator defines the values that form the complex number by initializing the value of self.

You may have noticed that imag (short for imaginary) has been set equal to zero. This is because zero is a purely imaginary number. Therefore, if a user does not pass any value into imag, then this ensures that it will still be imaginary.

We have a class that defines complex numbers by their real and imaginary parts, now we're ready to begin creating operations to perform on complex numbers.

Addition

When you add complex numbers together, you are only able to combine like terms. In other words, we must add together the real and imaginary components of both numbers as separate pieces. Taking a look at the addition class below, we can see that we have defined the first number as self and the second as other.

After initializing our two complex numbers, we can then add them together as seen below the addition class.

def __add__(self, other):
    return Complex(self.real + other.real, self.imag + other.imag)

i = complex(2, 10j)
k = complex(3, 5j)
add = i + k
print(add)
# Output: (5+15j)

Subtraction

Subtraction works very similarly to addition with complex numbers. Once again, we can only combine like terms, therefore, it's important that we keep these separate when performing the subtraction operation.

Using the same variables from the previous example, i and k, let's see the result of the subtraction of these two complex numbers.

def __sub__(self, other):
    return Complex(self.real - other.real, self.imag - other.image)

subtract = i - k
# Output: (-1+5j)

As expected, when you subtract 3 from 2 the result is -1, and 10j - 5j resulted in 5j.

Multiplication

Multiplication is a bit trickier. Unlike addition and subtraction, we will now have to combine coefficients of the imaginary numbers with the real numbers. However, the result must be returned as a complex number in the end.

Before we jump into the code, let's take a look at how the math works out under the hood. As you can see below, we break the problem up into two separate pieces: one that solves for the real number and one that solves for the imaginary. This should look familiar to anyone who knows algebra.

    # The problem written out
    (2 + 10j) * (3 + 5j)
    
    # Solving for the real number
    (2 * 3) - (10j * 5j)
    6 + 50
    --------------------
    56
    # Once we multiply 10j by 5j, j becomes j^2.
    # Which is also equal to -1, causing - to change to +
    
    # Solving for the imaginary number
    (10j * 3) + (2 * 5j)
    30j + 10j
    --------------------
    40j
    /* We are combining the coefficients from the reals
    with the imaginary numbers */
    
    # Final result:
    56 + 40j

Note

The real part of the complex number is solved by subtracting (2 * 3) - (10j * 5j) but this becomes addition because an imaginary number is equivalent to -1. In other words, the real number part of the equation can be seen as:
(2 * 3) + (10 * 5).

Understanding the math involved in solving for the multiplication of two complex numbers is an important first step. We are ready to move forward with the class. As we look below, you'll notice that the math we performed above is laid out very similarly to the code itself.

def __mul__(self, other):
    return Complex(self.real * other.real - self.imag * other.imag, self.imag         * other.real + self.real * other.real)

multiply = i * k
# Output: 

Division

Like multiplication, dividing complex numbers can become messy pretty quickly. Especially if you're not familiar with the fundamental concepts of how to solve for the quotient of two complex numbers.

Continuing with the same complex numbers used throughout this article, we begin with the problem that solves for the real number. Similar to multiplication, division of complex numbers can be broken down into two separate equations for each part of the complex number.

Then we move onto the second half of the problem in which we solve for the imaginary part. This is pretty straight forward as we're just following the same basic division principles that we use with real numbers.

Once we have two pieces for each - the real and imaginary parts of the complex number - then we need to combine them in order to get a final result.

    # Divisor
    (3**2 + 5j**2)
    (9 - 25)
    
    # Solving real number
    (2*3 - 10j*5j) / (9 - 25)
    (6 + 50) / (-16)
    (-44 / -16)
    --------------------------
    -3.5
    
    # Solving imaginary number
    (10j*3 + 2*5j) / (9 - 25)
    (30j + 10j) / (-16)
    40j / (-16)
    --------------------------
    -2.5j

    # Final result:
    (-3.5-2.5j)

The divisor in the problem above is found by raising the real and imaginary parts of the other value to the second power. Since we are dividing by k those are 3 and 5j.

The j of 5j is eliminated once the number is raised to the second power, turning it into a real number and allowing it to be combined with other.real.

Let's see this written out in Python:

def __truediv__(self, other):
    divisor = (other.real**2 + other.imag**2)
    return Complex((self.real * other.real) -
        (self.imag * other.imag)/divisor,
        (self.imag * other.real) + (self.real * other.imag)/divisor)

divide = i / k
# Output: (-3.5-2.5j)

Note:

You may have noticed that the division operator is named __truediv__ rather than div. This is due to the fact that __truediv__ is a built-in operation in Python 3 that is able to recognize the division operator /. Otherwise, i / k would be considered illegal and would result in an error message.

Absolute Value

On a less math-heavy note, let's try to find the absolute value of a complex number. To do so, we must extract the coefficient from the imaginary part and combine that with the real number. We've already done this with the divisor in the last example.

    # We are solving for the absolute value of i = (2+10j)
    (2**2 + (10j**2)*(-1))
    4 + (-100)*(-1)
    4 + 100
    ----------------------
    104
    sqrt(104)
    
    # Final result: 10.198039027185569

As we can see with i (2+10j), we are raising the real and imaginary numbers to the second power. After we have turned 10j into -100, we must multiply by -1 because we have eliminated j and made the coefficient into a negative number. However, the result will be inaccurate if we subtract 100 from 4 because we cannot solve for the square root of a negative.

import math
from math import sqrt

def __abs__(self):
    new = (self.real**2 + (self.imag**2)*-1)
    return Complex(sqrt(new.real))


abs(i)
# Output: 10.198039027185569
abs(k)
# Output: 5.830951894845301

You may be wondering why it is even necessary to raise the numbers to the second power if we're just going to solve for the square root, anyway.

As noted earlier, raising the real and imaginary numbers to the second power pulls them out of the complex number by eliminating all imaginary parts (j squared is -1). This allows us to combine the two numbers in order to find their absolute value.

All Together

So far we've discussed addition, subtraction, multiplication, division, and absolute value with complex numbers. You can contribute even more operations to your complex class on your own. But, first, let's clean up the code and see how it works all together.

import math
from math import sqrt

class Complex(object):

    def __init__(self, real, imag=0.0):
        self.real = real
        self.imag = imag
        # Formats our results
        print(self.real + self.imag)

    def __add__(self, other):
        print('\nSum:')
        return Complex(self.real + other.real, self.imag + other.imag)

    def __sub__(self, other):
        print('\nDifference:')
        return Complex(self.real - other.real, self.imag - other.imag)
    
    def __mul__(self, other):
        print('\nProduct:')
        return Complex((self.real * other.real) - (self.imag * other.imag),
            (self.imag * other.real) + (self.real * other.imag))

    def __truediv__(self, other):
        print('\nQuotient:')
        r = (other.real**2 + other.imag**2)
        return Complex((self.real*other.real - self.imag*other.imag)/r,
            (self.imag*other.real + self.real*other.imag)/r)

    def __abs__(self):
        print('\nAbsolute Value:')
        new = (self.real**2 + (self.imag**2)*-1)
        return Complex(sqrt(new.real))


i = Complex(2, 10j)
k = Complex(3, 5j)

# Add
i + k
# Subtract
i - k
# Multiply
i * k
# Divide
i / k
# Absolute value
abs(i)
abs(k)

# Output:
(2+10j)
(3+5j)

Sum:
(5+15j)

Difference:
(-1+5j)

Product:
(56+40j)

Quotient:
(-3.5-2.5j)

Absolute Value:
10.198039027185569

Absolute Value:
5.830951894845301

As you see from our resulting output, we can add a new line (\n) and label the operation so that our answers are returned in a neater and more readable format for the user. There is much more to expand upon this complex class, explore these concepts on your own to create a deeper understanding of Python and its operators.

With this article at OpenGenus, you must have the complete idea of implementing and designing Complex Numbers using classes in Python. Enjoy.