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:
- Every index where it disagrees with this course's table.
- Whether that pattern of disagreement looks like a mistake in the code, or like something else.
- 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:
- What is the general rule connecting one of Lovelace's labels to its modern index?
- 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
B7instead, 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:
- Compare
FractionagainstFraction. Do not compare againstfloat, and do not use a tolerance. These values are exact and an approximate comparison would hide precisely the errors you are looking for. - Report the first divergence rather than all of them. A single specific index is a reproducible debugging boundary; do not assume it causes every later mismatch.
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.
cp06-b4-by-hand— the value you derived in Task 1cp06-convention— the disagreement you diagnosed in Task 2cp06-ada-index— the mapping you worked out in Task 3
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.
- Sign lost at the last step. You reach
1/6 + 5·B4 = 0and read off1/30instead of-1/30. The sum equals zero, so the term containingB4is the negative of everything else. This one is easy to make and worth saying aloud. Make the same slip in code and the routine is wrong at every index fromB1on. ItsB1is+1/2, which looks like the other convention. ItsB2gives it away:5/6, not1/6. - Stopping before solving, or at n = 2.
1/6is what the known terms add up to forn = 4before you solve forB4; it is alsoB2, so it can mean the recurrence was run forB2rather thanB4. That points to a misread of whichnto take, rather than an arithmetic error. - Wrong binomial row. Using row 4 (1, 4, 6, 4, 1) instead of row 5. The
identity uses
n + 1on top, notn, and it is easy to substitutenreflexively. - Decimals. Converting to decimals partway through gives
-0.0333…, which is not exact and cannot be compared for equality with-1/30. This is the same error that the course later namesnot-fractionwhen it appears in generated code, and notice that it began here, on paper.
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:
- It names the convention, so a reader can look it up.
- It states the value, so a reader who has never heard of the convention can still comply.
- It is checkable against a single output, which means a test can be written for it directly.
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:
- Iterating in index order is what makes "first" meaningful. Iterating over the claimed dictionary in insertion order would report whichever divergence happened to be inserted first, which is not the same thing.
- Exact comparison.
!=between twoFractionobjects is exact. If you wroteabs(a - b) < 1e-9, that tolerance passes the course's float-based example,float_output.py, at every index up toB23and first fails it atB24, which is the worst possible behaviour: it hides the bug until the bug is expensive. Comparing aFractionagainst afloatin Python does work, and that is part of the hazard — it will silently succeed forFraction(1, 4)and fail forFraction(1, 6). - A missing value is a finding, not a crash. A routine that has no answer at
an index has failed the specification just as surely as one with a wrong
answer. The course's own checker treats this as its
raisesfailure mode.
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:
cp06-b4-by-handcp06-conventioncp06-ada-index
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.