Unit 6 activity: Meet the Bernoulli numbers

Release candidate. The unit videos are added in the first release.

Time: about 20 minutes. Watch the video or read the reading first, at least as far as the worked example for B2.

Two routes run through this activity: table-based verification and Python with exact rationals. They cover the same ground and end at the same checkpoints. Pick whichever suits how you work. If you want to do both, do the paper working first — the code route is more useful as a check on your own arithmetic than as a substitute for it.

Keep your working. You will refer back to it in Unit 8, and, on a route that includes Unit 17, it belongs in your evidence pack there.


Task 1 — Work out B4 by hand (about 8 minutes)

This is the central task of the unit. Do not look it up, do not compute it with a library, and do not ask an AI system. The value of this exercise is entirely in having produced the number yourself, because in the next unit you will need a reference that did not come from a machine.

The recurrence, again, for n >= 1 with B0 = 1:

sum_{j=0}^{n} C(n+1, j) * B_j = 0

Take n = 4.

Step 1. Write out the identity with all five terms, from j = 0 to j = 4. Every binomial coefficient uses n + 1 = 5 on top.

C(5,0)·B0 + C(5,1)·B1 + C(5,2)·B2 + C(5,3)·B3 + C(5,4)·B4 = 0

Step 2. Fill in the five binomial coefficients. Row 5 of Pascal's triangle is 1, 5, 10, 10, 5, 1 — you need the first five of those. Write them into your copy of the identity.

Step 3. Substitute the values you already have for B0, B1, B2 and B3. All four are in the table in the reading, and B2 is the one you watched being derived. B3 has an odd index above 1, so you know it is zero without looking.

Step 4. Add up everything that does not contain B4. Keep it as a single fraction — do not convert to decimals at any point. You should end up with one small fraction plus a whole-number multiple of B4, and the two together equal zero.

Step 5. Solve for B4. Watch the sign: the whole sum is zero, so the term containing B4 must be the negative of everything else.

Write the result as a fully reduced fraction. Keep every line of working — the intermediate steps are what you will check, not just the final number.

If you are taking the Python route

Do steps 1 to 5 on paper first. Then use Python to check your own arithmetic line by line, rather than to produce the answer.

from fractions import Fraction
from math import comb

# Step 2 — the coefficients you wrote down
print([comb(5, j) for j in range(5)])

# Step 3 and 4 — the part of the sum that does not contain B4.
# Fill in the values you substituted; do not import the oracle.
b0 = Fraction(1)
b1 = Fraction(-1, 2)
b2 = Fraction(1, 6)
b3 = Fraction(0)

known = comb(5, 0) * b0 + comb(5, 1) * b1 + comb(5, 2) * b2 + comb(5, 3) * b3
print(known)          # compare against your Step 4 fraction

If the printed value matches the fraction you got by hand, your substitution and addition are sound and any remaining error is in Step 5. If it does not match, you have found your slip without being told the answer, which is the more useful outcome.

Do not finish this task by calling reference_bernoulli.bernoulli(4). That routine is the thing you are learning to be able to check. Using it here would invert the whole exercise.

If you are taking the table-based route

Your working on paper is the deliverable. Before moving on, do one extra pass: re-derive Step 4 by putting all four known terms over a common denominator of 6, independently of how you did it the first time. Two ways of reaching the same intermediate value is a much stronger check than reading your own arithmetic twice.


Continue after attempting Task 1

Task 2 — Diagnose a disagreement (about 5 minutes)

A colleague sends you a Python file. Its output begins:

B0 = 1
B1 = 1/2
B2 = 1/6
B3 = 0
B4 = -1/30
B5 = 0
B6 = 1/42
B10 = 5/66
B12 = -691/2730

Compare it against the table in the reading, index by index, and write down:

  1. Every index where it disagrees with this course's table.
  2. Whether that pattern of disagreement looks like a mistake in the code, or like something else.
  3. The one sentence you would add to a specification so that this disagreement could not arise again. Write it as a requirement, not as a complaint — something a reader could test against.

Keep sentence 3. You will compare it with the failure-mode name it corresponds to in Unit 8 and, if your route includes Unit 10, with the clause in that unit's precise prompt.


Task 3 — Read an index from Note G (about 4 minutes)

Lovelace's labels start at the modern B2. From there she numbers only the values that are not zero, using the odd numbers. The zeros never receive a label at all.

Fill in this table from that rule alone. Work out the modern index for each of her labels rather than copying the mapping from the reading, then check your answers against it.

Ada's label modern index value
B1
B3
B5
B7

Then answer in one sentence each:

  1. What is the general rule connecting one of Lovelace's labels to its modern index?
  2. Note G's worked example computes the number Lovelace calls B7. If a modern routine were asked to reproduce Note G's result and returned its own B7 instead, what would go wrong, and would the returned value be a genuine Bernoulli number?

Question 2 is the one to sit with. A routine can return real Bernoulli numbers, correctly computed, and still be wrong — because the values are filed under the wrong labels. That is a failure that survives being read carefully, and it is why Unit 8 compares index by index against a reference instead of asking whether output "looks like" Bernoulli numbers.


Task 4 — Optional CS extension (about 5 minutes, outside the core budget)

Clearly optional. Nothing later in the course depends on it.

Write a short function that takes a list of claimed values and reports the first index at which it disagrees with the table in the reading — not every disagreement, just the first, with the index, the claimed value and the expected one.

from fractions import Fraction

TABLE = {
    0: Fraction(1),
    1: Fraction(-1, 2),
    2: Fraction(1, 6),
    3: Fraction(0),
    # ... complete this from the reading, as fractions, not floats
}

def first_divergence(claimed: dict[int, Fraction]) -> str:
    """Describe the first index where claimed differs from TABLE."""
    ...

Two constraints worth honouring, because they are the same ones the course's own checker works under:

You have now written, in miniature, the tool that Unit 8 hands you.


Now check yourself

This unit has three checkpoints. Attempt them from your own working, not from the reading.

Either route:

python3 selfcheck.py run cp06-b4-by-hand  # or in the browser

Run this from the course/lab folder (setup: The verification lab and self-check).

To attempt all three at once:

python3 selfcheck.py run --unit 6

The browser route, at the foot of this unit's page, has the same three checkpoints, the same answers and the same feedback, and needs nothing installed.

A wrong answer on cp06-b4-by-hand will not reveal the correct value. It will name the specific slip your answer resembles and point you at the step to redo. Deriving the number is the objective, so being given it would defeat the exercise. If you are stuck, ask for the hint and go back to Step 2.

Marking guidance — open this once you have done the activity, to check your own work

Read this after attempting the activity. Working through it before Task 1 removes the only part of the course that has to happen before an AI system is involved.

Checkpoint answers are not restated here. Each checkpoint is defined once, and the browser self-check and the terminal both read that one definition and grade against it. This page explains the reasoning; the checkpoints do the marking.


Task 1 — B4 by hand

The full working

With n = 4, the identity has five terms and every binomial coefficient is taken from row 5:

C(5,0)·B0 + C(5,1)·B1 + C(5,2)·B2 + C(5,3)·B3 + C(5,4)·B4 = 0

The coefficients are 1, 5, 10, 10, 5. Substituting B0 = 1, B1 = -1/2, B2 = 1/6 and B3 = 0:

(1)(1) + (5)(-1/2) + (10)(1/6) + (10)(0) + (5)(B4) = 0

Term by term:

1 - 5/2 + 5/3 + 0 + 5·B4 = 0

Putting the known part over a denominator of 6:

6/6 - 15/6 + 10/6 = 1/6

So:

1/6 + 5·B4 = 0
      5·B4 = -1/6
        B4 = -1/30

Which agrees with the table in the reading, and with course/lab/reference_bernoulli.py.

Where the working can go wrong

Four possible slips are worth recognising rather than merely correcting.

Why this task exists

The value matters less than its provenance. In Unit 7 an AI system will produce a Bernoulli routine, and you will need a value that did not come from that system to judge it by. A number you derived is such a value. A number you looked up in the same conversation is not.

The course site withholds the numerical value in the initial reading and animations, then places the later tasks, this guidance, and the revealing mapping table in labelled sections you open yourself. This is a request, not a lock: you can still open the source or the section. The instruction is to attempt the derivation before you do.


Task 2 — Diagnosing the disagreement

1. Where it disagrees. At exactly one index: B1. The file gives +1/2 where this course's table gives -1/2. Every other value shown — B0, B2, B3, B4, B5, B6, B10, B12 — agrees exactly.

2. What that pattern means. A single-index disagreement, with everything around it untouched, is consistent with the second Bernoulli convention, B1 = +1/2; this course uses the first, B1 = -1/2. Both are standard, both are published, and they differ in exactly this one value. If that is what the file does, the colleague's file is not broken: the specification that both of you were working to was incomplete.

The output alone does not settle it. A sign error inside a recurrence would usually spread from B1 into later values, so the pattern rules that kind of bug out, but a sign error that touched only B1 would print exactly these values. To tell a convention from a bug, read the specification, the documentation or the code.

This is the failure mode the checker names b1-sign. It is worth noticing that the name describes what the output looks like, not a diagnosis of intent — the same output would result from a genuine sign error, and neither the checker nor you can tell those apart from the output alone.

3. The specification sentence. Any wording is acceptable provided it is testable and unambiguous. A reasonable version:

Use the first Bernoulli convention, in which B1 = -1/2.

The properties that make it a good requirement:

A version such as "use the correct Bernoulli numbers" fails all three: there is no such thing as the correct one here, and nothing can be tested against it. If your route includes Unit 10, compare your sentence with the corresponding clause in that unit's precise prompt. If yours is close, you have independently reinvented a piece of requirements engineering, which is what specification work mostly is.


Task 3 — Reading an index from Note G

The completed table, which is the locked mapping used throughout the course:

Ada's label modern index value
B1 B2 1/6
B3 B4 -1/30
B5 B6 1/42
B7 B8 -1/30

1. The general rule. Lovelace labels only the non-zero Bernoulli numbers, starting from the modern B2. The modern scheme spends an index on every term, including the odd ones that are zero, so her label numbers are one smaller: her Bk is the modern B(k+1), for odd k. Her B7 is the modern B8.

Two ways of getting this wrong are worth naming. Treating her labels as modern labels gives B7 = 0, since the modern B7 is an odd index above 1. Doubling — guessing that her B7 is the modern B14 — comes from noticing that only even indices are involved, but the rule is a shift by one, not a scaling. Writing out all four pairs makes the pattern visible immediately, which is why the task asks for the whole table rather than a single row.

2. Note G's B7. Note G's worked example computes Ada's B7, which is the modern B8. Its value is -1/30.

A modern routine that returned its own B7 would return 0, since every odd modern index above 1 is exactly zero — a plainly visible mismatch. The more dangerous version of this error is the one where nothing looks wrong at all: a routine that uses Lovelace's own labels returns 1/6 for B1, -1/30 for B3, 1/42 for B5 — her numbering runs B1, B3, B5, B7 for the modern B2, B4, B6, B8, and she has no B2 at all. Every value it returns is a genuine Bernoulli number, correctly computed, exactly represented. Every one is filed under Note G's index, not the modern one.

That combination — real values, wrong labels — is why reading the code carefully does not catch it. There is nothing in the code to catch. The mismatch only exists relative to a specification, and it only becomes visible when you compare against a reference index by index. The course names this failure mode ada-indexing, and you will meet a working example of it in Unit 8.

A note on the history: Note G's published table contains printed errors — the inverted division at operation 4 is established from a facsimile, and others in operations 24 and 21 rest on a self-published reconstruction — while their attribution (who introduced them) remains contested. This unit does not depend on the error at all, and Unit 16 handles it properly. See Historical Notes before repeating any account of it.


Task 4 — Optional CS extension

No single correct implementation. A reasonable one:

from fractions import Fraction

def first_divergence(claimed: dict[int, Fraction]) -> str:
    for n in sorted(TABLE):
        if n not in claimed:
            return f"B{n}: no value supplied"
        if claimed[n] != TABLE[n]:
            return f"B{n}: claimed {claimed[n]}, expected {TABLE[n]}"
    return f"no divergence up to B{max(TABLE)}"

Points to notice:

To go further, add the property check that every odd index above 1 is exactly zero. That check needs no table at all — it tests a property rather than a value — and it catches the ada-indexing failure mode immediately, because a routine using Lovelace's labels puts its values on the odd indices: its B3 is her B3, the modern B4 = -1/30, where the property demands zero.


Checkpoints

Attempt these from your own working:

python3 selfcheck.py run cp06-b4-by-hand  # or in the browser

Both routes ask the same questions and give the same feedback. Answers and diagnostics live in the checkpoint data below, which both routes read.

Your private activity record

Browser storage is not a permanent copy

Progress is kept only in this browser, profile and device. Private browsing, clearing site data, removing the profile, a browser reset, storage eviction or device loss can erase it. Keep important answers and contributions separately.

These notes stay in this browser unless you download a backup or activity log.