Table of contents
Text Link

Building a Matrix Calculator with Numpy and Tkinter in Python

Matrices, fundamental building blocks in linear algebra, play a crucial role in various fields like engineering, physics, computer science, and data analysis. Performing calculations on these multi-dimensional arrays often involves repetitive tasks and can be prone to errors. To streamline this process, programmers leverage powerful tools like Python libraries.

This article delves into building a user-friendly matrix calculator using two powerful libraries: NumPy and Tkinter. NumPy offers efficient tools for handling multidimensional arrays, enabling seamless matrix operations. Tkinter, on the other hand, provides a framework for creating graphical user interfaces (GUIs), allowing us to interact with the calculator visually.

Throughout this article, we will explore the step-by-step process of building this matrix calculator. We will guide you through:

  • Understanding how to create, manipulate, and perform operations on matrices using NumPy functions.
  • Integrating NumPy and Tkinter to implement various matrix operations.
  • Create a user-friendly GUI for interacting with the calculator.

Implement Matrix Operations

The NumPy library is not a standard Python library. Therefore, you’d need to install it:

pip install numpy

After installation, you can then use NumPy to implement the matrix operation. One of the advantages of using NumPy is its execution speed. NumPy is fast because it vectorizes its operation. Thus, there is no need for explicit looping. Additionally, it is implemented with optimized C libraries. The C code generally compiles directly to machine code, leading to much faster execution than interpreted Python code. This allows NumPy to perform calculations on large datasets efficiently.

Addition and subtraction operations are performed with numpy.add and numpy.subtract respectively. They take parameters x1 and x2, which are the individual matrix items to be added or subtracted:

import numpy as np


# The first matrix
matrix_one = np.array(
    [
        [3, 6, 7],
        [8, 5, 8],
        [2, 3, 11]
    ]
)

# The second matrix
matrix_two = np.array(
    [
        [2, 4, 5],
        [1, 5, 4],
        [5, 0, 1]
    ]
)


# Add the first and second matrix
addition = np.add(matrix_one, matrix_two)
print(addition, end)

print()


# Subtract the second matrix from the first matrix
subtraction = np.subtract(matrix_one, matrix_two)
print(subtraction)
[[ 5 10 12] 
 [ 9 10 12] 
 [ 7  3 12]]

[[ 1  2  2] 
 [ 7  0  4] 
 [-3  3 10]]

You can see how easy it is to add and subtract matrices with the NumPy library. Next, you will learn how to perform multiplication operations with NumPy.

The first multiplication method you will learn is scalar multiplication. This means that you just multiply the matrix with a scalar variable:

# Multiply the first matrix by two and add the second matrix
print(2 * matrix_one + matrix_two, end="\n\n")

# check if the mul dunder method is present
print("__mul__" in dir(matrix_one), end="\n\n")


# check if the add dunder method is present
print("__add__" in dir(matrix_one))
[[ 8 16 19]
 [17 15 20]
 [ 9  6 23]]

True

True

You can see from the code snippet that the first matrix is multiplied by two and the result is added to the second matrix. This time numpy.add was not used. The operation was done as a basic math operation. This worked because the array object in NumPy has the __mul__ and __add__ dunder methods, which are responsible for scalar multiplication and addition, respectively.

 

The next multiplication operation we will be talking about is the matrix multiplication. This operation is performed with numpy.dot. It requires that the inner dimensions of the matrices need to be compatible. For example, the inner dimensions are compatible if the first matrix has a dimension of (m × n) and the second matrix dimension is (n × p). The dimension of the resultant matrix is (m × p):

# Matrix multiplication with numpy.dot
print(np.dot(matrix_one, matrix_two), end="\n\n")

# Matrix multiplication shortcut
print(matrix_one @ matrix_two, end="\n\n")

# This is not matrix multiplication. It is an element-wise multiplication
print(matrix_one * matrix_two, end="\n\n")

# This is also element-wise multiplication with numpy.multiply
print(np.multiply(matrix_one, matrix_two))
[[47 42 46] 
 [61 57 68] 
 [62 23 33]]

[[47 42 46] 
 [61 57 68] 
 [62 23 33]]

[[ 6 24 35] 
 [ 8 25 32] 
 [10  0 11]]

[[ 6 24 35] 
 [ 8 25 32] 
 [10  0 11]]

The @ shortcut works because of the presence of the __matmul__ method in the array object. The __mul__ dunder is responsible for the element-wise operation without numpy.multiply.

 

The next matrix operation you will be looking at is the matrix inverse:

from numpy.linalg import inv
print(inv(matrix_one))
[[-0.12863071  0.18672199 -0.05394191] 
 [ 0.29875519 -0.07883817 -0.13278008] 
 [-0.05809129 -0.01244813  0.13692946]]

You import the inverse, inv, from numpy.linalg. Be aware that singular matrices do not have an inverse and would result in a LinAlgError.

 

The next matrix operations you would be looking at are the determinant of a matrix, the transpose of a matrix, and the 2-norm:

from numpy.linalg import det, norm

# The determinant of matrix one
print(det(matrix_one), end="\n\n")

# The transpose of matrix one
print(np.transpose(matrix_one), end="\n\n")

# Also the transpose of matrix one
print(matrix_one.T, end="\n\n")

# The 2-norm of matrix one
print(norm(matrix_one))
-240.99999999999994

[[ 3  8  2]       
 [ 6  5  3]       
 [ 7  8 11]]      

[[ 3  8  2]       
 [ 6  5  3]       
 [ 7  8 11]]      

19.519221295943137

The Matrix Calculator GUI

You have seen how matrix algebra works. You can make the process an endless loop and more fun with a GUI. Presently, your matrix operations stop after running. However, with Tkinter, it only stops when you quit the program. This article will build the operations fully for matrix addition, you can refer to the code on GitHub for the others.

 

The first is using Tkinter. Fortunately, Tkinker is a standard Python library, so you do not have to install it. The mainframe of the Matrix Calculator code is:

# matrix_calculator.py

import tkinter as tk
from tkinter import ttk
import numpy as np

class MatrixCalculator:

    def __init__(self, root):

        self.root = root
        self.root.title("Matrix Calculator")
        self.root.geometry("350x250")
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)

        self.font_label = ("Rockwell", 12)
        self.font_item = ("Verdana", 10)

        mainframe = tk.Frame(self.root)
        mainframe.grid()

        operations = ["Add", "Subtract", "Scalar Multiply",
                      "Matrix Multiply", "Inverse", "Determinant",
                      "Transpose", "2-Norm"
                      ]
        
        main_label = ttk.Label(mainframe, text="Select Operation:", font=self.font_label)
        main_label.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=1, pady=1)

        self.combobox = ttk.Combobox(mainframe, value=operations, font=self.font_item)
        self.combobox.grid(row=1, column=1, sticky=(tk.N, tk.S, tk.E, tk.W), padx=1, pady=1)

        self.invalid = tk.StringVar()
        invalid_label = ttk.Label(mainframe, textvariable=self.invalid, font=self.font_label, foreground="red")
        invalid_label.grid(row=2, column=1, sticky=tk.W, padx=1, pady=1)

        go_btn = ttk.Button(mainframe, text="Go", command=self.get_combobox_operation)
        go_btn.grid(row=3, column=1, sticky=tk.E, padx=1, pady=1)


        style = ttk.Style()
        style.configure("Custom.TButton", font=self.font_label)
        go_btn.configure(style="Custom.TButton")


    def get_combobox_operation(self):

        selected_operation = self.combobox.get()

        options = {
            "Add": self.get_add, 
            # "Subtract": self.get_subtract, 
            # "Scalar Multiply": self.get_scalar_multiply,
            # "Matrix Multiply": self.get_matrix_multiply, 
            # "Inverse": self.get_inverse, 
            # "Determinant": self.get_determinant,
            # "Transpose": self.get_transpose, 
            # "2-Norm": self.get_2_norm 
        }

        get_function = options.get(selected_operation, None)

        if get_function is None:

            self.invalid.set("invalid")

        else:
            self.invalid.set("")
            get_function(selected_operation)



root = tk.Tk()
MatrixCalculator(root)
root.mainloop()

Run the program:

python .\matrix_calculator.py

You add the methods for the matrix add mathematical operation as follows:

class MatrixCalculator:

    ... 

    def get_add(self, selected_operation):
        
        window = tk.Toplevel(self.root)
        window.title(f"{selected_operation} Operation")
        window.geometry("800x200")
        window.rowconfigure(0, weight=1)
        window.columnconfigure(0, weight=1)
        
        frame = ttk.Frame(window)
        frame.grid()

        self.first_matrix = tk.Text(frame, width=28, height=6, background="lightgray", font=self.font_item)
        self.first_matrix.grid(row=1, column=0, padx=1, pady=1, sticky=(tk.N, tk.S, tk.E, tk.W))

        plus_label = ttk.Label(frame, text="+", font=self.font_label)
        plus_label.grid(row=1, column=2, sticky=(tk.W, tk.E), padx=1, pady=1)

        self.second_matrix = tk.Text(frame, width=28, height=6, background="lightgray", font=self.font_item)
        self.second_matrix.grid(row=1, column=3, padx=1, pady=1, sticky=(tk.N, tk.S, tk.E, tk.W))

        equals_label = ttk.Label(frame, text="=", font=self.font_label)
        equals_label.grid(row=1, column=4, sticky=(tk.W, tk.E), padx=1, pady=1)

        self.answer_matrix = tk.Text(frame, width=28, height=6, background="lightblue", font=self.font_item)
        self.answer_matrix.grid(row=1, column=5, padx=1, pady=1, sticky=(tk.N, tk.S, tk.E, tk.W))
        
        self.invalid_add = tk.StringVar()
        invalid_label = ttk.Label(frame, textvariable=self.invalid_add, font=self.font_label, foreground="red")
        invalid_label.grid(row=2, column=0, sticky=tk.W, padx=1, pady=1)

        self.invalid_add1 = tk.StringVar()
        invalid_label1 = ttk.Label(frame, textvariable=self.invalid_add1, font=self.font_label, foreground="red")
        invalid_label1.grid(row=2, column=3, sticky=tk.W, padx=1, pady=1)

        self.invalid_add2 = tk.StringVar()
        invalid_label2 = ttk.Label(frame, textvariable=self.invalid_add2, font=self.font_label, foreground="red")
        invalid_label2.grid(row=2, column=5, sticky=tk.W, padx=1, pady=1)


        add_btn = ttk.Button(frame, text="Add", command=self.add_matrix)
        add_btn.grid(row=3, column=5, padx=1, pady=1, sticky=tk.E)
        add_btn.configure(style="Custom.TButton")


    def add_matrix(self):
        
        first_matrix_value = self.first_matrix.get("1.0", "end-1c")
        first_matrix_list = [i.split() for i in first_matrix_value.split("\n") if i]
        try:
            nrows1, ncols1 = len(first_matrix_list), len(random.choice(first_matrix_list))
            first_matrix_list = [float(i) for j in first_matrix_list for i in j]
            self.invalid_add.set("")
        except ValueError:
            self.invalid_add.set("There is an invalid entry")

        second_matrix_value = self.second_matrix.get("1.0", "end-1c")
        second_matrix_list = [i.split() for i in second_matrix_value.split("\n") if i]
        
        try:
            nrows2, ncols2 = len(second_matrix_list), len(random.choice(second_matrix_list))
            second_matrix_list = [float(i) for j in second_matrix_list for i in j]
            self.invalid_add1.set("")
        except ValueError:
            self.invalid_add1.set("There is an invalid entry")

        first_matrix = np.array(first_matrix_list).reshape(nrows1, ncols1)
        second_matrix = np.array(second_matrix_list).reshape(nrows2, ncols2)

        if first_matrix.shape != second_matrix.shape:
            self.invalid_add2.set(f"{first_matrix.shape} != {second_matrix.shape}")
        else:
            self.invalid_add2.set("")
            add_matrix = np.add(first_matrix, second_matrix)
            
            ans_str = "\n".join([" ".join(map(str, row)) for row in add_matrix])
            self.answer_matrix.delete("1.0", tk.END)
            self.answer_matrix.insert(tk.END, ans_str)




root = tk.Tk()
MatrixCalculator(root)
root.mainloop()

The ellipsis (…) signifies the code truncated. This is done to avoid repetition. Your program must, however, contain the source code to run without errors.

Run the program:

python .\matrix_calculator.py

You can now perform the same addition operation as before with matrix one and matrix two:

Conclusion

In conclusion, this article has provided a step-by-step guide on building a user-friendly matrix calculator using the powerful combination of NumPy and Tkinter libraries in Python. We explored the functionalities of NumPy packages for a wide range of matrix operations and the creation of a GUI with Tkinter for user interaction. This interactive calculator simplifies complex matrix calculations, making it a valuable tool for anyone working with matrices in various fields. You can find the whole code on GitHub.

Share this article
Get more articles
like this
Thank you! Your submission has been received!
Oops! Something went wrong.

Create a free account to access the full topic

Wide range of learning tracks for beginners and experienced developers
Study at your own pace with your personal study plan
Focus on practice and real-world experience
Andrei Maftei
It has all the necessary theory, lots of practice, and projects of different levels. I haven't skipped any of the 3000+ coding exercises.