Broadcasting
Add Vector to Rows
A 2-row × 3-column grid and a 3-element vector added together by hand. A nested
loop walks each row, then each column, adding the matching vector element to the
cell. The trace shows new_row building column by column before being appended
to result.
By hand
Outer loop over each row in grid. Inner loop over each column index j:
add row[j] to vec[j] and append to new_row. After the inner loop
finishes, append new_row to result.
naive.py
Replay: real traced execution (multi-file project)
grid = [[1, 2, 3], [4, 5, 6]]
vec = [10, 20, 30]
result = []
for row in grid:
new_row = []
for j in range(len(vec)):
new_row.append(row[j] + vec[j])
result.append(new_row)
print('RESULT:', result)
grid ← [[1, 2, 3], [4, 5, 6]]
1grid = [[1, 2, 3], [4, 5, 6]]2vec = [10, 20, 30]values this step[[1, 2, 3], [4, 5, 6]]gridvec ← [10, 20, 30]
1grid = [[1, 2, 3], [4, 5, 6]]2vec = [10, 20, 30]3result = []values this step[10, 20, 30]vecresult ← []
2vec = [10, 20, 30]3result = []4for row in grid:values this step[]resultrow ← [1, 2, 3]
3result = []4for row in grid:5 new_row = []values this step[1, 2, 3]rownew_row ← []
4for row in grid:5 new_row = []6 for j in range(len(vec)):values this step[]new_rowj ← 0
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])values this step0jnew_row ← [11]
6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])8result.append(new_row)values this step[] → [11]new_rowj ← 1
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])values this step0 → 1jnew_row ← [11, 22]
6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])8result.append(new_row)values this step[11] → [11, 22]new_rowj ← 2
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])values this step1 → 2jnew_row ← [11, 22, 33]
6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])8result.append(new_row)values this step[11, 22] → [11, 22, 33]new_rowfor j in range(len(vec)):
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])result ← [[11, 22, 33]]
7 new_row.append(row[j] + vec[j])8 result.append(new_row)9print('RESULT:', result)values this step[] → [[11, 22, 33]]resultrow ← [4, 5, 6]
3result = []4for row in grid:5 new_row = []values this step[1, 2, 3] → [4, 5, 6]rownew_row ← []
4for row in grid:5 new_row = []6 for j in range(len(vec)):values this step[11, 22, 33] → []new_rowj ← 0
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])values this step2 → 0jnew_row ← [14]
6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])8result.append(new_row)values this step[] → [14]new_rowj ← 1
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])values this step0 → 1jnew_row ← [14, 25]
6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])8result.append(new_row)values this step[14] → [14, 25]new_rowj ← 2
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])values this step1 → 2jnew_row ← [14, 25, 36]
6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])8result.append(new_row)values this step[14, 25] → [14, 25, 36]new_rowfor j in range(len(vec)):
5new_row = []6for j in range(len(vec)):7 new_row.append(row[j] + vec[j])result ← [[11, 22, 33], [14, 25, 36]]
7 new_row.append(row[j] + vec[j])8 result.append(new_row)9print('RESULT:', result)values this step[[11, 22, 33]] → [[11, 22, 33], [14, 25, 36]]resultfor row in grid:
3result = []4for row in grid:5 new_row = []stdout ← RESULT: [[11, 22, 33], [14, 25, 36]]
8 result.append(new_row)9print('RESULT:', result)values this stepRESULT: [[11, 22, 33], [14, 25, 36]]stdout
With NumPy
a + v broadcasts v (shape (3,)) across both rows of a (shape (2, 3)),
adding the same vector to every row in one call.
library.py
import numpy as np
grid = [[1, 2, 3], [4, 5, 6]]
vec = [10, 20, 30]
a = np.array(grid)
v = np.array(vec)
result = a + v
print('shape:', a.shape)
print('dtype:', result.dtype)
print('values:', result.tolist())
print('RESULT:', result.tolist())
shape: (2, 3)
dtype: int64
values: [[11, 22, 33], [14, 25, 36]]
RESULT: [[11, 22, 33], [14, 25, 36]]
Implementation notes
- Broadcasting rule: NumPy aligns shapes from the right.
vis(3,),ais(2, 3)— the trailing dimension matches, sovis virtually replicated along the rows axis without copying memory. - The result dtype is
int64because both inputs are integer arrays. If either werefloat64, the result would promote tofloat64. - Shape, dtype, and values are shown explicitly here because
ndarray.__repr__output varies with NumPy version and print options.