Placement Prep

Matrix Addition in Python: Step-by-Step with Code Examples

Four approaches to matrix addition in Python: nested loops, list comprehension, zip, and NumPy. Includes dimension validation and traced outputs.

By FACE Prep Team 5 min read
python matrix numpy list-comprehension placement-prep coding-interview

Matrix addition in Python has four standard implementations, each suited to a different context: nested loops for clarity, list comprehension for conciseness, zip() for idiomatic Python, and NumPy for performance at scale.

All four share the same precondition: both matrices must have identical dimensions. Attempting to add a 2-row matrix to a 3-row matrix produces either silent wrong output or an IndexError, depending on the approach. This guide shows each implementation applied to the same pair of matrices so you can compare them side by side.

The sample matrices used throughout this article:

mat1 = [[1, 2, 3],
        [4, 5, 6]]

mat2 = [[7, 8, 9],
        [10, 11, 12]]

Both are 2 rows by 3 columns. The expected result for every approach below:

[[8, 10, 12],
 [14, 16, 18]]

Row 0 verification: 1+7=8, 2+8=10, 3+9=12. Row 1 verification: 4+10=14, 5+11=16, 6+12=18.

What is Matrix Addition?

Matrix addition adds corresponding elements of two matrices, position by position. The element at row i, column j in the result equals mat1[i][j] + mat2[i][j]. The result matrix has the same dimensions as the inputs.

This operation appears in computer graphics (combining transformation matrices), signal processing (adding noise matrices to signal matrices), and machine learning (adding bias vectors during forward passes). For placement coding rounds, you’ll most often encounter the nested-loop version as the expected solution, with optimisation to NumPy as a follow-up question.

The dimension rule is the one constraint that never bends: you cannot add a 3x2 matrix to a 2x3 matrix, even though both contain six elements. The shapes must match exactly.

Approach 1: Nested Loops

Two for loops walk every row index and every column index, computing each element sum one cell at a time. Straightforward to write and straightforward to explain out loud during a verbal interview.

def add_matrices_nested(mat1, mat2):
    rows = len(mat1)
    cols = len(mat1[0])
    result = [[0] * cols for _ in range(rows)]
    for i in range(rows):
        for j in range(cols):
            result[i][j] = mat1[i][j] + mat2[i][j]
    return result

mat1 = [[1, 2, 3], [4, 5, 6]]
mat2 = [[7, 8, 9], [10, 11, 12]]
output = add_matrices_nested(mat1, mat2)
for row in output:
    print(row)

Tracing the Output

  • rows = 2, cols = 3
  • result initialised as [[0, 0, 0], [0, 0, 0]]
  • i=0, j=0: result[0][0] = 1 + 7 = 8
  • i=0, j=1: result[0][1] = 2 + 8 = 10
  • i=0, j=2: result[0][2] = 3 + 9 = 12
  • i=1, j=0: result[1][0] = 4 + 10 = 14
  • i=1, j=1: result[1][1] = 5 + 11 = 16
  • i=1, j=2: result[1][2] = 6 + 12 = 18

Output:

[8, 10, 12]
[14, 16, 18]

The nested-loop version is the one to use for a verbal walkthrough in a campus recruitment coding test. You can point at each variable and explain exactly what it holds at every step.

Approach 2: List Comprehension

The same nested-loop logic compacted into a two-level list comprehension. The arithmetic is identical; only the syntax changes.

def add_matrices_lc(mat1, mat2):
    return [[mat1[i][j] + mat2[i][j]
             for j in range(len(mat1[0]))]
            for i in range(len(mat1))]

mat1 = [[1, 2, 3], [4, 5, 6]]
mat2 = [[7, 8, 9], [10, 11, 12]]
output = add_matrices_lc(mat1, mat2)
for row in output:
    print(row)

Tracing the Output

The outer comprehension iterates i over 0 and 1 (two rows). The inner comprehension iterates j over 0, 1, and 2 (three columns). Every mat1[i][j] + mat2[i][j] produces the same six values as Approach 1.

Output:

[8, 10, 12]
[14, 16, 18]

List comprehension is a good choice for an online coding test where you write and submit code without explaining it. If a reviewer asks you to trace it verbally, you can still walk through the i and j indices step by step, though Approach 1 reads more naturally when explaining to another person.

Approach 3: zip() One-Liner

Python’s built-in zip() pairs elements from two iterables simultaneously. Nesting two zip() calls, one for rows and one for elements within each row, eliminates all index variables.

According to the Python 3 documentation, zip() returns an iterator of tuples where the i-th tuple contains the i-th element from each of the input iterables.

def add_matrices_zip(mat1, mat2):
    return [[a + b for a, b in zip(row1, row2)]
            for row1, row2 in zip(mat1, mat2)]

mat1 = [[1, 2, 3], [4, 5, 6]]
mat2 = [[7, 8, 9], [10, 11, 12]]
output = add_matrices_zip(mat1, mat2)
for row in output:
    print(row)

Tracing the Output

  • zip(mat1, mat2) produces two pairs: ([1,2,3], [7,8,9]) then ([4,5,6], [10,11,12])
  • First pair: row1=[1,2,3], row2=[7,8,9]
    • zip(row1, row2) gives tuples (1,7), (2,8), (3,9)
    • a+b for each: 8, 10, 12
  • Second pair: row1=[4,5,6], row2=[10,11,12]
    • zip(row1, row2) gives tuples (4,10), (5,11), (6,12)
    • a+b for each: 14, 16, 18

Output:

[8, 10, 12]
[14, 16, 18]

The zip one-liner is the form most production Python code uses for row-wise operations on parallel lists. No range calls, no index bookkeeping. The downside is that zip silently truncates to the shorter list if the two matrices have different row counts, so the dimension check becomes even more important here than in the loop-based versions.

Approach 4: NumPy

NumPy’s ndarray supports the + operator directly for element-wise addition. Under the hood, NumPy executes compiled C, which makes it the correct choice for matrices with large numbers of rows and columns.

The NumPy documentation confirms that adding two arrays of the same shape returns a new array of the same shape with element-wise sums.

import numpy as np

mat1 = np.array([[1, 2, 3], [4, 5, 6]])
mat2 = np.array([[7, 8, 9], [10, 11, 12]])
result = mat1 + mat2
print(result)

Output:

[[ 8 10 12]
 [14 16 18]]

NumPy also handles dimension mismatches gracefully. Attempting to add arrays of different shapes raises a ValueError that includes the exact shape of each array, which is more helpful than the bare IndexError raised by the loop-based approaches.

Input Validation: Checking Dimensions First

All four approaches assume the matrices are the same size. In real code, validate dimensions before attempting the addition.

def add_matrices(mat1, mat2):
    if len(mat1) != len(mat2):
        raise ValueError(
            f"Row counts differ: {len(mat1)} vs {len(mat2)}"
        )
    if len(mat1[0]) != len(mat2[0]):
        raise ValueError(
            f"Column counts differ: {len(mat1[0])} vs {len(mat2[0])}"
        )
    return [[a + b for a, b in zip(row1, row2)]
            for row1, row2 in zip(mat1, mat2)]

Calling add_matrices([[1,2,3],[4,5,6]], [[1,2],[3,4]]) raises:

ValueError: Column counts differ: 3 vs 2

The two checks run in constant time (two integer comparisons), so there is no performance cost. Building this habit into every function that operates on structured data is the kind of defensive coding that placement test reviewers and actual code reviewers both notice.

The same pattern appears across Python basic programs that deal with user-provided inputs, and in a calculator program in Python where you validate the operator before computing the result. Validate first, operate second.

Comparing the Four Approaches

ApproachIndex trackingPerformance on large matricesBest use
Nested loopsExplicit i, jSlowVerbal interviews, teaching
List comprehensionExplicit i, jSame as nested loopsOnline tests, concise submissions
zip one-linerNoneSame as nested loopsIdiomatic Python, code reviews
NumPyNoneFast (C-compiled)Data science, ML, production code

For a campus placement coding round on HackerEarth or similar, the nested-loop and list-comprehension versions both pass within time limits for the matrix sizes typically tested. For a data-science or ML interview where performance matters, reach for NumPy and be ready to explain why.

If you want simpler loop-based practice before tackling two-dimensional structures, start with greatest of two numbers in Python to build comfort with comparison and arithmetic logic first.

The element-wise operations you traced above are the same building blocks at the core of vector arithmetic in LLM and ML frameworks. TinkerLLM (₹299) is where engineering students apply exactly that foundation to ship a working LLM-powered project, not just read about the math behind it.

Primary sources

Frequently asked questions

Can you add matrices of different sizes in Python?

No. Matrix addition requires both matrices to have identical dimensions. Nested loops will produce wrong output or raise an IndexError; NumPy raises a clear ValueError with the shape mismatch details.

What does zip() do in Python matrix addition?

zip() pairs corresponding rows from both matrices. A nested zip() inside a list comprehension then pairs individual elements within each row, giving element-wise addition without any index variables.

Is NumPy faster than nested loops for matrix addition?

Yes, for large matrices. NumPy runs compiled C code internally. For matrices larger than roughly 100x100 elements, NumPy outperforms pure Python loops by a measurable margin.

Which matrix addition approach should I use in a placement coding test?

Use the nested-loop approach for verbal interviews — it shows you understand row and column indexing clearly. Mention NumPy as the production-grade alternative if the interviewer asks for an optimisation.

How do I add three or more matrices in Python?

With NumPy, use mat1 + mat2 + mat3 directly. With vanilla Python, apply the zip-based function twice: add(add(mat1, mat2), mat3), or use functools.reduce over the list of matrices.

Build AI projects

A self-paced playground for building with LLMs.

TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.

Try TinkerLLM (₹299 launch)
Free AI Roadmap PDF