Skip to content

Sums, Products, and Set Notation

Ask someone which part of a machine learning paper they find hardest and they will name the model. Watch them read one and the place they actually slow down is a double sum with a subscript on the inner index. Sigma notation is not conceptually hard — it is a for loop with an accumulator — but manipulating it is a skill, and it is the skill every derivation in this module assumes.

By the end of this page, n=1Nd=1Dxndwd\sum_{n=1}^{N}\sum_{d=1}^{D} x_{nd}w_d should read as fluently as a nested loop, and you should know when you are allowed to swap the two sums.

  • How to read Σ\Sigma and Π\Pi as loops, and how to translate either direction.
  • The three rules that let you move things in and out of a sum, and why the third one fails for products.
  • When a double sum can be swapped, and the one case where it cannot.
  • The telescoping trick, which turns a long sum into two terms.
  • Set-builder notation, membership, and the operations Chapter 2 uses on subspaces.
  • The indicator function, which is how a condition becomes arithmetic.

Intuition: a sum is a loop with an accumulator

Section titled “Intuition: a sum is a loop with an accumulator”
i=1nai  =  a1+a2++an\sum_{i=1}^{n} a_i \;=\; a_1 + a_2 + \cdots + a_n

Four things to read off, every time:

  • the indexii, the loop variable,
  • the lower limit — where it starts,
  • the upper limit — where it stops, inclusive,
  • the bodyaia_i, what gets added.
diagram Diagram mermaid

The inclusive upper limit is the one thing that trips programmers, because range(1, n) in Python stops at n1n-1. The mathematical sum stops at nn. In NumPy you almost never write the loop anyway — the whole sum is a.sum() — but when you are checking a derivation against code, the off-by-one lives here.

A shopping basket has nn items. Item ii costs pip_i and you buy qiq_i of it. The bill is

total  =  i=1npiqi.\text{total} \;=\; \sum_{i=1}^{n} p_i q_i .

That is a dot product, and it is also the only formula in this section. Every “weighted sum” in machine learning — a linear model’s prediction, an expectation, a loss over a dataset — is this same shape: multiply pairwise, then add up.

i=1ncai  =  ci=1nai\sum_{i=1}^{n} c\,a_i \;=\; c\sum_{i=1}^{n} a_i

Because cc does not depend on ii, it is the same cc in every term, so it factors out. This is the rule that lets 1/N1/N move to the front of an average, and it is used on nearly every page of Chapter 8.

The test for “does it depend on the index?” is mechanical: does the symbol carry the index as a subscript, or is it a function of it? cc does not, so it comes out. aia_i does, so it cannot.

i=1n(ai+bi)  =  i=1nai  +  i=1nbi\sum_{i=1}^{n} (a_i + b_i) \;=\; \sum_{i=1}^{n} a_i \;+\; \sum_{i=1}^{n} b_i

Adding a list of pairs is the same as adding the two lists separately. Combined with Rule 1 this makes Σ\Sigma linear, which is why gradients pass through sums (§5.2) and why expectations do (§6.4). Those two facts are the same fact.

i=1nc  =  nc\sum_{i=1}^{n} c \;=\; n\,c

The body does not mention ii at all, so the loop just runs nn times. Small, and constantly useful: it is the step that turns i=1Nμ\sum_{i=1}^{N}\mu into NμN\mu when you compute the mean of a Gaussian likelihood.

Reindexing: the limits and the body move together

Section titled “Reindexing: the limits and the body move together”
i=1nai  =  j=0n1aj+1\sum_{i=1}^{n} a_i \;=\; \sum_{j=0}^{n-1} a_{j+1}

The name of the index is arbitrary — it is a bound variable, exactly like a loop variable in code. Shifting it by one means shifting both limits and the subscript in the body by one, in opposite directions. Getting this wrong is the most common error in a hand derivation, and the fix is always to check one endpoint: at j=0j = 0 the right-hand side contributes a1a_1, which is what i=1i = 1 contributes on the left. Endpoints agree, so the shift is right.

Products: the same shape, one rule different

Section titled “Products: the same shape, one rule different”
i=1nai  =  a1×a2××an\prod_{i=1}^{n} a_i \;=\; a_1 \times a_2 \times \cdots \times a_n

Everything above transfers with multiplication in place of addition, except Rule 1:

i=1ncai  =  cni=1nai\prod_{i=1}^{n} c\,a_i \;=\; c^{\,n} \prod_{i=1}^{n} a_i

The constant comes out raised to the power nn, because it appeared in every one of the nn factors. This exact step is why the Gaussian likelihood of NN independent points carries a (2πσ2)N/2(2\pi\sigma^2)^{-N/2} out front (§6.5), and forgetting the exponent is a classic way to derive a wrong normalising constant.

i=1mj=1naij\sum_{i=1}^{m}\sum_{j=1}^{n} a_{ij}

Two nested loops. The inner sum runs to completion for each value of the outer index. When the limits are constants — neither depends on the other index — you may swap them freely:

i=1mj=1naij  =  j=1ni=1maij\sum_{i=1}^{m}\sum_{j=1}^{n} a_{ij} \;=\; \sum_{j=1}^{n}\sum_{i=1}^{m} a_{ij}

Both sides add up the same mnmn numbers; only the order changes, and addition does not care about order. Thinking of aija_{ij} as a grid makes it obvious: one side sums the rows and then adds the row totals, the other sums the columns and adds the column totals.

If the body is a difference of consecutive terms, almost everything cancels:

i=1n(bibi1)  =  bnb0\sum_{i=1}^{n} (b_i - b_{i-1}) \;=\; b_n - b_0

Write it out and every interior term appears once with a plus and once with a minus. What survives is the two endpoints. This is the discrete version of the Fundamental Theorem of Calculus, which the calculus refresher makes explicit, and it is the trick behind the monotone-improvement proof of EM in Chapter 11.

Take n=5n = 5 with ai=ia_i = i and bi=2b_i = 2, and check every rule against arithmetic.

ruleexpressionexpansionvalue
plain sumi=15i\sum_{i=1}^{5} i1+2+3+4+51+2+3+4+51515
Rule 1i=153i\sum_{i=1}^{5} 3i3(1+2+3+4+5)3(1+2+3+4+5)4545
Rule 2i=15(i+2)\sum_{i=1}^{5}(i+2)15+5215 + 5\cdot 22525
Rule 3i=152\sum_{i=1}^{5} 22+2+2+2+22+2+2+2+21010
reindexedj=04(j+1)\sum_{j=0}^{4}(j+1)1+2+3+4+51+2+3+4+51515
producti=15i\prod_{i=1}^{5} i123451\cdot2\cdot3\cdot4\cdot5120120
product, Rule 1i=153i\prod_{i=1}^{5} 3i351203^5 \cdot 1202916029160
telescoping, bi=i2b_i = i^2i=15(i2(i1)2)\sum_{i=1}^{5}(i^2-(i-1)^2)25025 - 02525

The last row is worth pausing on. The body expands to 2i12i - 1, so the sum is 1+3+5+7+9=251+3+5+7+9 = 25 — the sum of the first five odd numbers is 525^2. Telescoping gave the answer without adding anything.

Now the double sum. With aij=ija_{ij} = i\cdot j over i=1,2,3i = 1,2,3 and j=1,2j = 1,2:

aija_{ij}j=1j=1j=2j=2row total
i=1i=1112233
i=2i=2224466
i=3i=3336699
column total66121218\mathbf{18}

Rows first: 3+6+9=183 + 6 + 9 = 18. Columns first: 6+12=186 + 12 = 18. Same answer, which is the swap rule made concrete. And note 18=(1+2+3)(1+2)=6×318 = (1+2+3)(1+2) = 6 \times 3 — when the body factorises as aij=uivja_{ij} = u_i v_j, the double sum factorises into a product of two single sums. That observation turns up whenever an expectation splits over independent variables (§6.4).

Drag the limits. The highlighted cells are the terms the sum currently collects, and the running total is what a loop would accumulate.

sketch Which terms does this sum actually collect? p5.js
Drag the lower and upper limit knobs and watch which terms the sum picks up. The second knob row switches to a triangular inner limit, where the collected set becomes a triangle rather than a block.

Two things to notice. Moving the lower limit up removes terms from the front — the sum does not slide, it shrinks. And on the triangular setting the collected cells form a staircase, not a block: that is the shape the swap rule has to preserve, and it is why the limits change when you swap.

A sum runs over an index. A set is what you run over when there is no natural order.

A={1,3,5,7},B={xR:x>0}\mathcal{A} = \{1, 3, 5, 7\}, \qquad \mathcal{B} = \{x \in \mathbb{R} : x > 0\}

The second is set-builder notation, and it reads left to right as: “the set of xx drawn from R\mathbb{R} such that x>0x > 0”. The colon is “such that”; some authors use a vertical bar instead.

notationmeaning
aAa \in \mathcal{A}aa is an element of A\mathcal{A}
aAa \notin \mathcal{A}aa is not an element
AB\mathcal{A} \subseteq \mathcal{B}every element of A\mathcal{A} is in B\mathcal{B}
AB\mathcal{A} \cup \mathcal{B}union: in A\mathcal{A}, or in B\mathcal{B}, or both
AB\mathcal{A} \cap \mathcal{B}intersection: in both
AB\mathcal{A} \setminus \mathcal{B}in A\mathcal{A} but not in B\mathcal{B}
A×B\mathcal{A} \times \mathcal{B}Cartesian product: all ordered pairs (a,b)(a, b)
\emptysetthe empty set
A\lvert\mathcal{A}\rvertcardinality: how many elements

Three of these do specific jobs later:

  • \subseteq is the definition of a subspace (§2.4): a subset that is itself a vector space.
  • \cap is how the orthogonal complement is characterised (§3.6), and how a feasible region is built from several constraints (§7.2).
  • ×\times is why R2\mathbb{R}^2 is written R×R\mathbb{R}\times\mathbb{R} and why a joint distribution lives on a product space (§6.1).

The bridge from a condition to arithmetic:

1[condition]={1if the condition holds0otherwise\mathbb{1}[\text{condition}] = \begin{cases} 1 & \text{if the condition holds} \\ 0 & \text{otherwise} \end{cases}

It lets a count be written as a sum. The number of correct predictions is

n=1N1[y^n=yn],\sum_{n=1}^{N} \mathbb{1}[\hat{y}_n = y_n],

and dividing by NN gives accuracy. In NumPy the indicator is a boolean array and the sum is (y_hat == y).sum(), because True behaves as 11. That equivalence is not a coincidence of the language — it is the indicator function, implemented.

The zero-one loss of §8.2, the responsibilities of §11.3 and the hinge-loss counting argument of §12.2 are all indicator sums.

sums_and_sets.py
import numpy as np
 
a = np.arange(1, 6)                     # a_i = i for i = 1..5
print("sum:", a.sum())                              # 15
print("Rule 1  sum 3a_i == 3 sum a_i:", (3 * a).sum() == 3 * a.sum())
print("Rule 2  sum(a+b) == sum a + sum b:",
      (a + 2).sum() == a.sum() + np.full(5, 2).sum())
print("Rule 3  sum of a constant:", np.full(5, 2).sum())        # 10
print("product:", a.prod())                                      # 120
print("Rule 1 for products, c^n:", (3 * a).prod(), "==", 3**5 * a.prod())
 
# Telescoping: sum of (i^2 - (i-1)^2) collapses to the endpoints.
b = np.arange(0, 6) ** 2                # b_0..b_5
print("telescoped:", np.diff(b).sum(), "== b_5 - b_0 =", b[-1] - b[0])
 
# Double sums: axis is the index you sum OVER.
A = np.outer(np.arange(1, 4), np.arange(1, 3))    # a_ij = i*j, 3x2
print("rows first:", A.sum(axis=1).sum(), " columns first:", A.sum(axis=0).sum())
print("factorises:", A.sum(), "==", np.arange(1, 4).sum() * np.arange(1, 3).sum())
 
# Triangular sum: the mask IS the inner limit j <= i.
i, j = np.indices((6, 6)) + 1
tri = np.where(j <= i, i * j, 0)
print("lower triangle:", tri.sum())
 
# The indicator function is a boolean array.
y      = np.array([1, 0, 1, 1, 0])
y_hat  = np.array([1, 0, 0, 1, 0])
print("correct:", (y_hat == y).sum(), "of", y.size,
      " accuracy:", (y_hat == y).mean())
 
# Sets: unordered, no duplicates.
print({1, 1, 2} == {2, 1}, "  but tuples keep order:", (1, 2) == (2, 1))
text
sum: 15
Rule 1  sum 3a_i == 3 sum a_i: True
Rule 2  sum(a+b) == sum a + sum b: True
Rule 3  sum of a constant: 10
product: 120
Rule 1 for products, c^n: 29160 == 29160
telescoped: 25 == b_5 - b_0 = 25
rows first: 18  columns first: 18
factorises: 18 == 18
lower triangle: 266
correct: 4 of 5  accuracy: 0.8
True   but tuples keep order: False
you want tosumproduct
pull out a constantcc comes out unchangedcc comes out as cnc^n
split over ++yes, Σ\Sigma is linearno
empty range00, the additive identity11, the multiplicative identity
turn into the otherexp\exp of a sum is a productlog\log of a product is a sum
NumPy.sum(), np.add.reduce.prod(), np.multiply.reduce
numerical riskcatastrophic cancellationunderflow or overflow

The empty-range row is not trivia: an empty sum is 00 and an empty product is 11, and both conventions make edge cases in recursive derivations work without a special case.

pch.quizTag Check yourself
  1. You need to pull the constant three out of a product of n terms. What comes out?

    pch.quizShowAnswer

    B — Three to the power n — The constant appeared in every one of the n factors, so it comes out raised to the n. This is exactly the step that produces the normalising constant in an i.i.d. Gaussian likelihood.

  2. A double sum has an inner limit that runs from one up to the outer index. Can you swap the two sums?

    pch.quizShowAnswer

    B — Yes, but the limits must be rewritten so they describe the same triangle — The same terms are being added either way, so a swap is possible, but the limits describe the region and must be rewritten to describe it column-wise instead of row-wise.

  3. Why does machine learning almost always work with log likelihoods rather than likelihoods?

    pch.quizShowAnswer

    B — A logarithm turns the product into a sum, which avoids floating-point underflow — A product of many small probabilities underflows to zero; the sum of their logarithms does not. And because the logarithm is increasing, the maximum does not move — which is what makes the substitution legitimate.

  4. In NumPy, what does summing with axis equal to zero do to a two-dimensional array?

    pch.quizShowAnswer

    A — Collapses the first index, leaving one value per column — The axis names the index being eliminated. Collapsing the first index leaves one entry per column. Reading it as the index you sum away, rather than as a direction, removes the ambiguity that makes this error so common.

Exercise 2 – The constant comes out squared, cubed, …

Section titled “Exercise 2 – The constant comes out squared, cubed, …”

Exercise 4 – Swapping a triangular double sum

Section titled “Exercise 4 – Swapping a triangular double sum”

Exercise 5 – An indicator sum is an accuracy

Section titled “Exercise 5 – An indicator sum is an accuracy”
  • A sum is a loop with an accumulator — index, inclusive lower limit, inclusive upper limit, body.
  • Sigma is linear: a constant factors out and sums split over addition, which is why gradients and expectations both pass straight through a sum.
  • Pulling a constant out of a product raises it to the power n — the source of the exponent in an i.i.d. likelihood’s normalising constant.
  • Log turns a product into a sum, which is the entire reason machine learning maximises log likelihoods.
  • Double sums with constant limits swap freely; dependent limits swap only after rewriting them to describe the same region the other way round.
  • Telescoping — a sum of consecutive differences collapses to the two endpoints, and it is the discrete Fundamental Theorem of Calculus.
  • An empty sum is zero and an empty product is one, which is what makes recursive edge cases work without special-casing.
  • axis names the index you sum away, not a direction — read axis=0 as eliminating the first index.
  • The indicator function turns a condition into arithmetic, and in NumPy it is just a boolean array being summed.

Next: what a function actually is, and the two properties Chapter 2 will demand of one — Functions, Limits, and Continuity.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading