TKTK — dedicate this book to whom you choose. One or two lines is conventional. Delete this comment and the placeholder once written.
This is not a finished book. It is a scaffold — a complete, internally consistent skeleton for a book-length technical work, with every structural element in place and every author-specific passage left as a marked stub for you to fill.
The whole book is data-driven from a single source of
truth, config.yaml. That
file declares the parts, the chapters inside each part, the front
matter, the appendices, and the labs and question banks. The table of
contents, chapter numbering, figure numbering, and the
manuscript-integrity tests all read from it. You grow the book by
editing config.yaml and then materialising any missing
files — you never hand-number a chapter, figure, equation, or table.
What is already provided for you:
labs/ and questions/.src/ — the worked equations are real, tested Python
functions (textbook.models), and figures are generated
deterministically. Chapter prose calls these functions rather
than retyping the mathematics.tests/test_manuscript_integrity.py plus
scripts/audit_textbook_quality.py) that checks the
structural contract holds as you write.Everywhere author-specific content belongs, you will find a stub
marker: <!-- STUB -->, TODO:, or
TKTK. The quality audit counts these, so your progress
toward a finished book is measurable. See Appendix A — Authoring
Guide and AGENTS.md for the
full filling workflow.
The book is organised into four parts that build on one another. A first-time reader should move through them in order; an instructor can assign parts independently.
Each chapter ends with a Practice section pointing to its lab (a guided, hands-on exercise) and its question bank (self-check questions). Work the lab after reading; use the question bank to confirm you can recall and apply the material. New terms are linked to the Master Glossary the first time they appear.
This manuscript is rendered, not typeset by hand. The pipeline reads
config.yaml, runs the analysis
scripts that produce figures and diagrams, assembles the Markdown
sections in declared order, and renders a PDF through Pandoc with
pandoc-crossref resolving every cross-reference and
citation.
To build the book from the repository root:
uv run python scripts/02_run_analysis.py --project templates/template_textbook
uv run python scripts/03_render_pdf.py --project templates/template_textbookor run the full pipeline with ./run.sh. See README.md for the manuscript directory
layout and SYNTAX.md for the exact
authoring syntax.
TKTK — state, in two or three paragraphs, the problem this book solves and why it is worth a reader’s time. What gap in the existing literature does it fill? What will a reader be able to do after finishing it that they could not do before?
This template is domain-neutral by design. Wherever the sample chapters speak of “systems”, “dynamics”, or “regulation”, substitute the concepts of your own field. The structure — orientation, fundamentals, core systems, applications — generalises across most technical subjects; the content is yours to supply.
TKTK — describe the intended reader. Assumed background? A prerequisite course or a specific level of mathematical maturity? Whether the book suits self-study, a one-semester course, or a reference shelf.
The book assumes the quantitative foundations laid out in Part 0 and nothing more. A reader comfortable with that material can follow every chapter.
Each chapter is paired with two companion documents:
labs/) — a guided, hands-on exercise that
puts the chapter’s worked formalism to work. Labs are meant to be
done, not just read: run the tested functions in
textbook.models, vary the parameters, and observe how the
predictions change. Work the lab immediately after reading the chapter,
while the ideas are fresh.questions/) — self-check questions
that confirm you can recall and apply the chapter’s load-bearing claims.
Use it as a diagnostic: a question you cannot answer points you back to
a specific section.Instructors can assign the lab as homework and draw exam or quiz
items from the question bank. Because both are generated from the same
config.yaml as the chapters, they stay in lockstep with the
manuscript as it grows.
Every equation in this book is implemented as a tested function and
every figure is generated deterministically from code. Nothing in the
prose is computed by hand. If you build the book yourself you will get
byte-identical figures and numbers — see README.md for the build commands.
— The Author, TKTK (place), TKTK (date)
This part covers orientation and methods. It contains the following chapters:
How to use this part.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Orientation to the Field. Foundational treatments include [kim2020data; brown2017principles]. Key terms such as regulation, boundary, state are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 1:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(1)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 1.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 1 and revisit the objectives above as you read. Further evidence: [kim2020data; brown2017principles].
feedback, gradient, threshold, network.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Core Methods and Tools. Foundational treatments include [kim2020data; brown2017principles]. Key terms such as model, parameter, variable are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 2:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(2)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 2.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 2 and revisit the objectives above as you read. Further evidence: [kim2020data; brown2017principles].
emergence, regulation, boundary, state.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Quantitative Foundations. Foundational treatments include [smith2020foundations; doe2019methods]. Key terms such as feedback, gradient, threshold are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 3:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(3)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 3.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 3 and revisit the objectives above as you read. Further evidence: [smith2020foundations; doe2019methods].
observable, system, model, parameter.
This part covers fundamentals. It contains the following chapters:
How to use this part.
Level 1/3 · 25 min read · 40 min lecture · Prerequisites: none
This chapter is a worked reference. It is filled to completion to show what a finished chapter looks like. The other chapters in this template ship as structurally-complete stubs (marked with stub comments and TODO notes); fill them the same way.
By the end of this chapter you should be able to:
textbook.models.logistic_growth rather than re-deriving
arithmetic by hand.textbook.models.logistic_growth.
Opening Vignette: Counting before you explain.
A population biologist watches a colony double, then double again, then — unexpectedly — slow. A start-up tracks users that grow the same way before the market saturates. A chemist measures a reaction that races, then crawls as substrate runs low. Three unrelated fields, one shape. The job of a first model is not to capture everything; it is to capture that shape with as few moving parts as possible.
A model is a deliberate simplification. We choose a small number of state variables — here, a single quantity \(N(t)\) — and write a rule for how the state changes. The art is leaving things out: a first model that fits on one line teaches more than a faithful one that fills a page.
The variables are what change; the parameters are what we hold fixed while we reason. In the growth model below, \(N\) is the variable, while the rate \(r\) and the carrying capacity \(K\) are parameters. Confusing the two is the most common beginner error: if you find yourself “solving for \(K\) over time,” you have mislabelled a parameter as a variable.
Unbounded (exponential) growth assumes nothing ever pushes back. Real systems saturate. The simplest law that grows fast when small and levels off when large is the logistic equation, written as a rate of change in eq. 4 and in closed form in eq. 5:
\[ \frac{dN}{dt} = rN\left(1 - \frac{N}{K}\right) \qquad{(4)}\]
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(5)}\]
Read eq. 4 aloud: the rate of change is proportional to how much there is (\(rN\)) times how much room remains (\(1 - N/K\)). When \(N\) is small the second factor is near \(1\) and growth is nearly exponential; as \(N\) approaches \(K\) the factor approaches \(0\) and growth stalls. The parameters are collected in tbl. 4.
| Symbol | Name | Role | Example value |
|---|---|---|---|
| \(N(t)\) | quantity | variable | computed |
| \(r\) | intrinsic rate | parameter | \(0.8\ \mathrm{s^{-1}}\) |
| \(K\) | carrying capacity | parameter | \(100\) units |
| \(N_0\) | initial value | parameter | \(5\) units |
Take \(r = 0.8\), \(K = 100\), and \(N_0 = 5\). Rather than evaluate the exponential by hand, call the tested backbone:
import numpy as np
from textbook import models
t = np.array([0.0, 2.0, 5.0, 10.0])
N = models.logistic_growth(t, r=0.8, carrying_capacity=100.0, initial=5.0)
# N -> [ 5.00, 20.68, 74.18, 99.37 ]The trajectory starts at \(N(0) = 5.00\), reaches \(N(2) = 20.68\), passes the steep middle near \(t = 5\) with \(N(5) = 74.18\), and by \(t = 10\) has all but arrived at \(N(10) = 99.37\) — within one part in a hundred of the carrying capacity. This is the S-curve plotted in fig. 4: an early near-exponential rise, an inflection, and a long approach to the asymptote.
The long-run behaviour is exact, not approximate. Because \(r > 0\), the term \(e^{-rt} \to 0\), so \(N(t) \to K\). The proof is one line and is recorded as Theorem 1 in sec. 46; the same fact is asserted numerically by the test suite, so the prose and the code cannot silently disagree.
graph TD
A[Choose state variables] --> B[Write a rate rule]
B --> C[Solve or simulate]
C --> D[Predict long-run behaviour]
D -->|compare to data| A
This loop — choose, write, solve, predict, compare — is the first principle the rest of the book elaborates. Foundational treatments of model-building include [brown2017principles] and [patel2018models].
A model trades completeness for clarity: a few state variables and one rule. The
logistic law adds a single idea — finite room — to exponential growth,
and that idea changes the long-run behaviour from unbounded increase to
a stable equilibrium at
the carrying capacity \(K\). Compute
with the tested logistic_growth function so your worked
numbers are reproducible and correct by construction.
system, model, state, parameter, equilibrium.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Building Blocks. Foundational treatments include [taylor2019theory; smith2020foundations]. Key terms such as equilibrium, feedback, gradient are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 6:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(6)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 5.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 5 and revisit the objectives above as you read. Further evidence: [taylor2019theory; smith2020foundations].
state, observable, system, model.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Structure and Form. Foundational treatments include [lee2021systems; garcia2022dynamics]. Key terms such as threshold, network, dynamics are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 7:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(7)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 6.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 6 and revisit the objectives above as you read. Further evidence: [lee2021systems; garcia2022dynamics].
model, parameter, variable, equilibrium.
This part covers core systems. It contains the following chapters:
How to use this part.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Systems Overview. Foundational treatments include [patel2018models; nguyen2023synthesis]. Key terms such as equilibrium, feedback, gradient are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 8:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(8)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 7.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 7 and revisit the objectives above as you read. Further evidence: [patel2018models; nguyen2023synthesis].
state, observable, system, model.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Dynamics and Change. Foundational treatments include [brown2017principles; wilson2021analysis]. Key terms such as parameter, variable, equilibrium are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 9:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(9)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 8.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 8 and revisit the objectives above as you read. Further evidence: [brown2017principles; wilson2021analysis].
regulation, boundary, state, observable.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Regulation and Control. Foundational treatments include [wilson2021analysis; taylor2019theory]. Key terms such as network, dynamics, emergence are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 10:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(10)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 9.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 9 and revisit the objectives above as you read. Further evidence: [wilson2021analysis; taylor2019theory].
parameter, variable, equilibrium, feedback.
This part covers applications and synthesis. It contains the following chapters:
How to use this part.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Applied Models. Foundational treatments include [patel2018models; nguyen2023synthesis]. Key terms such as equilibrium, feedback, gradient are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 11:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(11)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 10.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 10 and revisit the objectives above as you read. Further evidence: [patel2018models; nguyen2023synthesis].
state, observable, system, model.
Level 2/3 · 30 min read · 45 min lecture · Prerequisites: First Principles
This chapter is a worked reference in a different style from sec. 8: where that chapter derives a model, this one applies one to a small dataset. The remaining chapters ship as stubs.
By the end of this chapter you should be able to:
textbook.models.linear_fit and state what its slope and
\(R^2\) mean.textbook.models.linear_fit,
descriptive_statistics.
Opening Vignette: From a spreadsheet to a decision.
A team runs a small pilot: a control condition and two treatment levels, two replicates each. The numbers land in a spreadsheet. The question is not “are they different?” — they obviously are — but “by how much, how confidently, and what should we predict next?” A case study is the discipline of answering those three questions without overreaching.
The pilot produced six measurements across three conditions, recorded
in assets/data/sample_dataset.csv and reproduced in
tbl. 11.
| Condition | Replicate | Measurement | Standard error |
|---|---|---|---|
| control | 1 | 2.10 | 0.20 |
| control | 2 | 2.30 | 0.18 |
| treatment (low) | 1 | 3.60 | 0.25 |
| treatment (low) | 2 | 3.40 | 0.22 |
| treatment (high) | 1 | 4.80 | 0.35 |
| treatment (high) | 2 | 5.10 | 0.30 |
Grouping and averaging — the first move in almost every case study —
gives means of \(2.20\) (control),
\(3.50\) (low), and \(4.95\) (high). Across all six points the
mean is \(3.55\) with a standard
deviation of \(1.13\)
(textbook.models.descriptive_statistics). The error-bar
view is fig. 11.
Encode the dose as \(0, 1, 2\) for
control, low, and high, and fit a line with
textbook.models.linear_fit:
import numpy as np
from textbook import models
dose = np.array([0.0, 1.0, 2.0])
response = np.array([2.20, 3.50, 4.95]) # condition means
fit = models.linear_fit(dose, response)
# fit.slope ≈ 1.375, fit.intercept ≈ 2.175, fit.r_squared ≈ 0.999The fitted relationship in eq. 12 is
\[ \widehat{y}(d) = 1.375\,d + 2.175 \qquad{(12)}\]
with \(R^2 = 0.999\). The slope says each dose step adds about \(1.4\) units of response; the intercept, \(2.175\), is close to the measured control mean of \(2.20\), a reassuring internal check.
Using eq. 12 to estimate the response at an intermediate dose is reasonable: the model interpolates within the range it was fit on. Using it to predict a much higher dose — say \(d = 3\), giving \(\widehat{y} = 6.3\) — is an extrapolation the data cannot support. Real dose–response curves usually saturate (recall the threshold behaviour of the saturating response in sec. 8); a straight line will overpredict once the system approaches its ceiling.
graph LR
A[Raw measurements] --> B[Group + average]
B --> C[Fit a trend]
C --> D{Within data range?}
D -->|yes| E[Interpolate: defensible]
D -->|no| F[Extrapolate: flag the risk]
Warning. A linear fit through three averaged points reports \(R^2 = 0.999\), but that number describes how well the line passes through three dots — not how well it predicts the next experiment. Treat it as a reason to collect more data, not as a conclusion. Foundational guidance on this trap appears in [kim2020data] and [wilson2021analysis].
A case study moves from raw observables to a summary, a
fitted trend, and — crucially — an honest boundary around what the trend
can claim. Here, averaging and a linear_fit recovered a
clean dose–response slope of about \(1.4\) units per step with an intercept that
matched the control, but the same fit must not be pushed beyond the
measured range. The reusable move is: summarise, fit, then state the
limits.
observable, gradient, threshold, model.
Level 1/3 · 30 min read · 45 min lecture · Prerequisites: none
By the end of this chapter you should be able to:
textbook.models. Opening Vignette: TKTK — a motivating story
This section introduces the central ideas of Frontiers and Open Problems. Foundational treatments include [wilson2021analysis; taylor2019theory]. Key terms such as state, observable, system are defined in the glossary.
The recurring quantitative model for this chapter is shown in eq. 13:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(13)}\]
It is implemented and tested in
textbook.models.logistic_growth; never retype the maths in
prose or scripts — call the tested function. The parameters appear in
tbl. 12.
| Symbol | Meaning | Units |
|---|---|---|
| \(r\) | intrinsic rate | 1/time |
| \(K\) | carrying capacity | quantity |
| \(N_0\) | initial value | quantity |
A concept map of how the pieces fit together:
graph TD
A[Inputs / assumptions] --> B[Model]
B --> C[Predictions]
C --> D[Comparison with data]
D -->|revise| A
Note
See the overview in fig. 12 and revisit the objectives above as you read. Further evidence: [wilson2021analysis; taylor2019theory].
threshold, network, dynamics, emergence.
Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 4.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 5.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 6.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: a computer with the project installed (
uv sync), or graph paper and a calculator
After this lab you will be able to (1) generate a logistic trajectory for chosen parameters, (2) read its carrying capacity and half-rise time from a plot, and (3) recover the growth rate from data with a simple fit.
This lab makes the chapter concrete: see sec. 8 for the logistic law and the meaning of \(r\), \(K\), and \(N_0\). You will produce the same S-curve shown in fig. 4 and then work backwards from numbers to parameters.
uv sync. Confirm the tests pass:
uv run --extra dev python -m pytest tests/test_models.py -q.Summarise each trajectory with
textbook.models.descriptive_statistics(N) and tabulate the
maximum value and the time-to-half-capacity for each \(r\). Describe, in one sentence, how
increasing \(r\) changes the curve
without changing where it ends.
import numpy as np
from textbook import models
t = np.arange(0, 13, 1.0)
for r in (0.4, 0.8, 1.2):
N = models.logistic_growth(t, r=r, carrying_capacity=100.0, initial=5.0)
t_half = t[np.argmax(N > 50)]
print(f"r={r}: N(12)={N[-1]:.1f}, first t with N>50 is t={t_half:.0f}")
print(" stats:", models.descriptive_statistics(N))Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 9.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 10.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 12.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 13.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 14.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 16.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Lab · 75 min · Materials: the project installed (
uv sync),assets/data/sample_dataset.csv
After this lab you will be able to (1) load grouped data, (2) compute per-group means and an overall summary, (3) fit a dose–response trend, and (4) state where the fit may and may not be trusted.
This lab operationalises sec. 17. You will reproduce the chapter’s numbers (group means \(2.20\), \(3.50\), \(4.95\); fitted slope \(\approx 1.375\)) and then probe the fit’s limits by adding a condition.
assets/data/sample_dataset.csv into three groups
(control, low, high).textbook.models.descriptive_statistics.textbook.models.linear_fit. Confirm slope \(\approx 1.375\), intercept \(\approx 2.175\), \(R^2 \approx 0.999\).Tabulate the slope, intercept, and \(R^2\) for the three-point and four-point fits. In two sentences, explain why the four-point fit’s slope falls and what that implies about extrapolating the original line.
import csv
import numpy as np
from textbook import models
rows = list(csv.DictReader(open("manuscript/assets/data/sample_dataset.csv")))
groups: dict[str, list[float]] = {}
for r in rows:
groups.setdefault(r["condition"], []).append(float(r["measurement"]))
means = {k: float(np.mean(v)) for k, v in groups.items()}
dose = np.array([0.0, 1.0, 2.0])
response = np.array([means["control"], means["treatment_low"], means["treatment_high"]])
print(models.linear_fit(dose, response))Lab · 60 min · Materials: notebook, calculator (or
textbook.models)
Linked chapter: sec. 18.
Summarise results with
textbook.models.descriptive_statistics.
from textbook.models import logistic_growth
# TODO: parameterise and plot the model for this lab's scenario.Linked chapter: sec. 4.
Linked chapter: sec. 5.
Linked chapter: sec. 6.
Linked chapter: sec. 8. Answers follow each question in italics; in a print build, move them to an answer key if you prefer.
Linked chapter: sec. 9.
Linked chapter: sec. 10.
Linked chapter: sec. 12.
Linked chapter: sec. 13.
Linked chapter: sec. 14.
Linked chapter: sec. 16.
Linked chapter: sec. 17. Answers follow each question in italics.
textbook.models.descriptive_statistics, which returns mean,
std, min, max, and count.)Linked chapter: sec. 18.
Reference appendix · For authors and contributors · Read this first.
This is the most important file in the template. It explains how to turn the empty scaffold into a finished book by filling stubs and growing structure from a single source of truth. Read it once end-to-end, then keep it open while you work.
The book is data-driven from config.yaml. The list of
parts, chapters, labs, question banks, and reference appendices lives
there and nowhere else. The Python engine in src/textbook/
reads that file and the scaffolding scripts in scripts/
materialise the matching markdown files. You never hand-number a
chapter, figure, equation, or section — pandoc-crossref does that at
render time from the labels you write.
The flow is always the same:
edit config.yaml -> scaffold_chapter.py -> fill <!-- STUB --> blocks
-> add figures / glossary terms / references -> audit + tests
config.yamlOpen config.yaml. To add or
rename a chapter, edit the parts: tree (each chapter has a
stem and a title); to add a lab or question
bank, add an entry under appendices.labs /
appendices.questions for the matching part. The stem drives
every downstream name:
manuscript/<part>/<NN>_<stem>.md, label
{#sec:<part>_<stem>}manuscript/labs/<part>/lab_<stem>.md, label
{#sec:lab_<part>_<stem>}manuscript/questions/<part>/q_<stem>.md, label
{#sec:q_<part>_<stem>}Keep stems short, lowercase, and snake_case. Do not
invent a numbering scheme; the table of contents and labels are derived
in src/textbook/toc.py.
Run the scaffolder. It only creates files that are missing, so it is safe to re-run after every config change:
uv run python scripts/scaffold_chapter.pyThis writes a chapter (or lab, or question bank) pre-populated with
every required structural element as <!-- STUB -->
markers. Your job is to replace the stubs with real prose, never to
delete the structure.
Every chapter must carry these elements (enforced by
validate_chapter in src/textbook/content.py
and by the integrity tests). Fill each, keep the markers’ surrounding
structure:
# Title {#sec:<part>_<stem>}<!-- chapter-metadata-badge --><!-- curriculum-scaffold-start -->{#fig:...}{#eq:...} plus a
parameter table {#tbl:...}```mermaid diagramCross-reference syntax is pandoc-crossref: [@fig:..],
[@tbl:..], [@eq:..], [@sec:..].
Stub markers the audit counts are <!-- STUB -->,
TODO:, and TKTK — drive these to zero as the
chapter matures.
Figures are deterministic matplotlib output. Each chapter expects a
placeholder named <part_id>_<stem>.png. To make
it real, add or edit a function in src/visualization/plots.py
(the four worked figures show the pattern), then regenerate:
uv run python scripts/generate_figures.pyFor diagrams, edit src/mermaid/diagram_specs.yaml and
run scripts/generate_diagrams.py (PNG, or .mmd
fallback). Reference the figure in prose with
[@fig:<part>_<stem>] and give it alt text.
Glossary anchors are a closed contract. To add a term you must update both:
GLOSSARY_ANCHORS in src/textbook/constants.pyglossary.mdLink a term in prose as [**term**](#gl:<anchor>).
The current anchors are
system, model, parameter, variable, equilibrium, feedback, gradient, threshold, network, dynamics, emergence, regulation, boundary, state, observable.
Citations are [key] and must resolve in references.bib. Add a BibTeX
entry, then cite it. Keep CITATION_KEYS in
constants.py in sync if you add a key that the structural
contract should track.
Two gates decide whether the book is healthy:
# Quality gate: counts stubs, checks the content contract per file
uv run python scripts/audit_textbook_quality.py
# Full test suite (engine + manuscript integrity)
uv run --extra dev python -m pytesttests/test_manuscript_integrity.py verifies that every
chapter satisfies the content contract, that labels are unique, that
citations resolve, and that glossary links point at real anchors. Green
tests with remaining stubs mean the structure is
correct but the content is unfinished — that is the
normal state of a freshly scaffolded chapter.
To add a whole new chapter end-to-end:
stem/title under the right part in
config.yaml;appendices;scripts/scaffold_chapter.py;See also: Appendix B — Notation, Appendix C — Mathematical Review, and the appendices README.
Reference appendix · Symbol glossary for the worked models.
This appendix lists the symbols used by the worked formalisms in src/textbook/models.py.
Keep it in sync with the parameter tables ({#tbl:...}) in
the chapters: every symbol that appears in an equation
{#eq:...} should have a row here.
0 denotes an initial value (e.g. N₀
at t = 0).| Symbol | Name | Appears in | Units | Notes |
|---|---|---|---|---|
| t | time / independent variable | all dynamic models | s (or chapter unit) | |
| N | state quantity / population | logistic_growth |
dimensionless | |
| N₀ | initial state | logistic_growth, exponential_decay |
dimensionless | |
| r | intrinsic growth rate | logistic_growth |
1/time | |
| K | carrying capacity | logistic_growth |
dimensionless | |
| λ (lambda) | decay constant | exponential_decay, half_life |
1/time | |
| t½ | half-life | half_life |
time | |
| Vmax | maximum response | saturating_response |
response unit | |
| Kₘ | half-saturation constant | saturating_response |
input unit | |
| x, y | paired observations | linear_fit |
data unit | |
| m, b | slope, intercept | linear_fit |
derived | |
| μ, σ | mean, standard deviation | descriptive_statistics |
data unit |
See also: Appendix C — Mathematical Review.
Reference appendix · Just-enough mathematics for the worked models.
A brief refresher on the mathematics the book relies on. Each section
ties directly to a function in src/textbook/models.py
so you can move from formula to tested code without a gap. Replace the
stubs with the depth your audience needs.
A function maps an input to an output; here state quantities depend
on a variable
such as time, tuned by parameters. See
normalize_unit_interval for rescaling inputs to [0, 1].
Logistic growth (logistic_growth) rises then saturates
at the carrying capacity K; exponential decay
(exponential_decay) falls by a fixed fraction per unit
time, summarised by the half-life t½ (half_life).
These are the canonical examples of dynamics.
The saturating (Michaelis–Menten-style) response
(saturating_response) climbs toward Vmax with
half-maximum at Kₘ — a recurring shape whenever a resource or
signal becomes limiting near a threshold.
Least-squares fitting (linear_fit) returns a slope
m and intercept b for paired data, the simplest way to
summarise a trend before reaching for a richer model.
Descriptive statistics (descriptive_statistics) report
the mean μ and standard deviation σ — the first
numbers to compute on any observable
before inference.
See also: Appendix B — Notation and the Authoring Guide.
A worked example of every formal element a technical
book uses: definitions, theorems with proofs, lemmas, algorithms in
pseudocode, step-by-step derivations, systems of numbered equations, and
dimensioned quantities. Each maps to a tested function in
src/textbook/models.py, so the prose and the code stay in
agreement.
Convention. Theorem-like environments below use a portable bold-label block-quote form that renders in every target. If your render profile loads
amsthm(the preamble does), you may instead use native LaTeXtheorem,lemma,definition, andproofenvironments.
Definition 1 (Equilibrium). A state \(N^{*}\) of a dynamical system \(\dot N = f(N)\) is an equilibrium if \(f(N^{*}) = 0\). See equilibrium.
Definition 2 (Carrying capacity). For logistic dynamics, the carrying capacity \(K\) is the non-zero equilibrium toward which trajectories converge.
Theorem 1 (Logistic limit). For the logistic model in eq. 14 with \(r > 0\) and \(0 < N_0 \le K\), the trajectory satisfies \(\lim_{t \to \infty} N(t) = K\).
\[ N(t) = \frac{K}{1 + A e^{-rt}}, \qquad A = \frac{K - N_0}{N_0} . \qquad{(14)}\]
Proof. Because \(r > 0\), the term \(e^{-rt} \to 0\) as \(t \to \infty\). Hence the denominator \(1 + A e^{-rt} \to 1\), and therefore \(N(t) \to K/1 = K\). The constant \(A \ge 0\) follows from \(0 < N_0 \le K\), so \(N(t)\) is increasing and the limit is approached from below. \(\qquad\blacksquare\)
This is exactly the asymptotic behaviour the test
tests/test_models.py::test_logistic_growth_starts_at_initial_and_approaches_capacity
verifies numerically — the proof and the test assert the same fact.
Lemma 1 (Half-life). For exponential decay \(y(t) = y_0 e^{-\lambda t}\) with \(\lambda > 0\), the time at which \(y\) falls to half its initial value is \(t_{1/2} = \ln 2 / \lambda\), independent of \(y_0\).
Proof. Set \(y(t_{1/2}) = y_0/2\). Then \(e^{-\lambda t_{1/2}} = 1/2\), so \(-\lambda t_{1/2} = -\ln 2\), giving \(t_{1/2} = \ln 2 / \lambda\). The result does not depend on \(y_0\). \(\qquad\blacksquare\)
Implemented as textbook.models.half_life.
Where the preamble provides an algorithm package, use it; otherwise this fenced form renders everywhere.
Algorithm 1: Ordinary least-squares line fit
Input : points (x_i, y_i), i = 1..n, n >= 2
Output: slope m, intercept b, coefficient of determination R^2
1 x_bar <- mean(x); y_bar <- mean(y)
2 m <- sum((x_i - x_bar)(y_i - y_bar)) / sum((x_i - x_bar)^2)
3 b <- y_bar - m * x_bar
4 SS_res <- sum((y_i - (m x_i + b))^2)
5 SS_tot <- sum((y_i - y_bar)^2)
6 R^2 <- 1 - SS_res / SS_tot (define R^2 = 1 when SS_tot = 0)
7 return (m, b, R^2)
This is textbook.models.linear_fit; Step 6’s degenerate
case is covered by
tests/test_models.py::test_linear_fit_constant_y_gives_r_squared_one.
Starting from the logistic differential equation and separating variables:
\[ \begin{aligned} \frac{dN}{dt} &= rN\left(1 - \frac{N}{K}\right) \\ \int \frac{dN}{N(1 - N/K)} &= \int r \, dt \\ \ln\!\left(\frac{N}{K - N}\right) &= rt + C \\ N(t) &= \frac{K}{1 + A e^{-rt}}, \qquad A = e^{-C}, \end{aligned} \]
which recovers eq. 14. Each line is one algebraic move; show your work at this granularity so readers can follow without gaps.
A simple predator–prey system, with each equation individually referenceable (eq. 15, eq. 16):
\[ \frac{dx}{dt} = \alpha x - \beta x y \qquad{(15)}\]
\[ \frac{dy}{dt} = \delta x y - \gamma y \qquad{(16)}\]
The vector-field figure style for such systems is demonstrated by the quiver plot in the format gallery (sec. 47).
State parameters with units explicitly, in math mode so they render in every target:
| Symbol | Quantity | Example value |
|---|---|---|
| \(r\) | intrinsic rate | \(0.8\ \mathrm{s^{-1}}\) |
| \(\lambda\) | decay constant | \(0.5\ \mathrm{s^{-1}}\) |
| \(t_{1/2}\) | half-life | \(1.386\ \mathrm{s}\) |
| \(K\) | carrying capacity | \(100\) individuals |
Symbols used throughout are collected in the notation appendix (sec. 44); glossary definitions for narrative terms such as gradient and threshold are in the master glossary.
This appendix is a kitchen-sink demonstration: a
working example of every content primitive this template supports. Copy
any block into a chapter and adapt it. Each example is real and renders
through the standard pipeline; figures are produced deterministically by
src/visualization/ and embedded from
../figures/.
How to read this appendix. Headings group primitives by kind: text, lists, callouts, tables, math, figures, diagrams, code, cross-references, media, and pedagogy blocks. The Markdown source is the example — view it next to the rendered output.
Plain paragraph text wraps and flows normally. Inline styles:
bold, italic, bold
italic, inline code,
strikethrough, H2O with a subscript, E =
mc2 with a superscript, and a footnote.1
You can hard-break a line
with two trailing spaces, or separate paragraphs with a blank line.
Escape literal Markdown with a backslash: *not italic*.
Unordered, with nesting:
Ordered:
Task list (renders as checkboxes in many targets):
Definition list:
A plain block quote:
“Form follows function.” Use quotes for epigraphs and primary-source extracts.
Portable callouts (a bold label inside a block quote — renders in every target):
Note. A neutral aside that adds context.
Tip. A practical suggestion the reader can act on.
Warning. A caveat, common error, or safety note.
Example. A short worked illustration inline in the text.
Definition. A precise statement of a term, often paired with a glossary entry such as equilibrium.
Pandoc fenced-div callout (richer styling where supported; falls back gracefully):
This is a Pandoc fenced div. If your render profile
styles .callout-note, it appears as a boxed admonition;
otherwise it renders as a normal block.
A simple table with column alignment and a cross-referencable caption (tbl. 14):
| Left | Centre | Right |
|---|---|---|
| alpha | 1 | 10.0 |
| beta | 22 | 2.5 |
| gamma | 333 | 0.125 |
A multi-line / grid table (cells may contain longer wrapped text):
| Symbol | Meaning | Typical range |
|---|---|---|
| \(r\) | intrinsic rate of change | 0.1 – 2.0 |
| \(K\) | carrying capacity / saturation level the system approaches | problem- dependent |
Inline math: the half-life is \(t_{1/2} = \ln 2 / \lambda\).
A numbered display equation, cross-referenced as eq. 17:
\[ N(t) = \frac{K}{1 + \left(\dfrac{K - N_0}{N_0}\right) e^{-rt}} \qquad{(17)}\]
Multi-line aligned derivation:
\[ \begin{aligned} \frac{dN}{dt} &= rN\left(1 - \frac{N}{K}\right) \\ &= rN - \frac{r}{K}N^2 . \end{aligned} \]
A matrix and a piecewise definition:
\[ \mathbf{A} = \begin{bmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{bmatrix}, \qquad f(x) = \begin{cases} 0 & x < 0 \\ 1 & x \ge 0 . \end{cases} \]
Physical quantities with units, written in math mode so they render
in every target (PDF, HTML, slides): a rate of \(0.5\ \mathrm{s^{-1}}\), a length of \(2.0\ \mathrm{m}\), and a concentration of
\(1.5\ \mathrm{mol\,L^{-1}}\). (For
PDF-only builds you may instead use siunitx macros such as
\SI{0.5}{\per\second}, which the preamble loads — but
math-mode units are the portable choice.)
A single figure with caption, label, and alt text, cross-referenced as fig. 13:
Two figures side by side (Pandoc fenced div; falls back to stacked):
A multi-panel composite (fig. 16):
The full plot-type gallery lives in ../figures/gallery/
and includes: line, scatter-with-fit, bar, grouped bar, horizontal bar,
histogram, box, violin, heatmap, contour, quiver field, step, stacked
area, error bars, log-log, pie, annotated, and multi-panel.
The pipeline renders fenced mermaid blocks to figures
(and falls back to the .mmd source if the Mermaid CLI is
absent). One worked example of each kind the builders in
src/mermaid/diagrams.py support:
Flowchart:
graph TD
A[Inputs] --> B[Model]
B --> C[Predictions]
C -->|revise| A
Sequence:
sequenceDiagram
participant Author
participant Engine
Author ->> Engine: edit config.yaml
Engine -->> Author: rendered PDF
State:
stateDiagram-v2
[*] --> Stub
Stub --> Drafted: fill
Drafted --> Reviewed: review
Reviewed --> [*]: publish
Class:
classDiagram
class ChapterRef {
+str part_id
+str file
+stem() str
}
ChapterRef --> TocEntry : numbered as
Entity-relationship:
erDiagram
PART ||--o{ CHAPTER : contains
CHAPTER ||--|| LAB : has
Pie, Gantt, mindmap, timeline, quadrant, and user-journey diagrams
are also supported — see src/mermaid/diagram_specs.yaml for
a worked spec of each.
Inline code: call
textbook.models.logistic_growth(t, r=..., ...).
A fenced code block with a language (syntax-highlighted) and a caption ([lst:gallery_code]):
Listing 1: Calling the tested computational backbone.
from textbook import models
import numpy as np
t = np.linspace(0, 10, 100)
n = models.logistic_growth(t, r=0.8, carrying_capacity=100.0, initial=5.0)
print(n[-1]) # -> approaches the carrying capacityA shell example:
uv run python scripts/generate_figures.py
uv run --extra dev python -m pytest tests/ --cov=srcCross-references resolve by label: figure fig. 13, table tbl. 14, equation eq. 17, and section sec. 46. Never hand-number — Pandoc fills these in.
Citations resolve against references.bib: a single
source [smith2020foundations], multiple sources [doe2019methods;
lee2021systems], and an in-text form — [@garcia2022dynamics] showed the
effect first. A locator narrows the reference [patel2018models].
Embedded raster image (any PNG/JPG works the same way as a figure):
Audio and video embed in HTML targets (PDF shows the caption + link). Syntax:

{width=70%}A downloadable data file lives at assets/data/sample_dataset.csv;
its contents as a table:
| condition | replicate | measurement | standard_error |
|---|---|---|---|
| control | 1 | 2.10 | 0.20 |
| control | 2 | 2.30 | 0.18 |
| treatment_low | 1 | 3.60 | 0.25 |
| treatment_high | 1 | 4.80 | 0.35 |
The error-bar figure fig. 18 visualises this kind of data:
These are the reusable teaching elements chapters draw on.
Learning objective. After this section a reader can identify which Markdown primitive to use for a given purpose.
Worked example. Given \(r = 0.8\), \(K = 100\), \(N_0 = 5\), evaluate \(N(10)\) via eq. 17 using
textbook.models.logistic_growth. The result approaches \(K\).
Try it. Change \(r\) to \(1.5\) and predict, then check, how the curve shifts.
Summary. This appendix demonstrated text, lists, callouts, tables, math and units, figures, diagrams, code, cross-references, media, and pedagogy blocks — the complete primitive set.
A horizontal rule separates major shifts in topic (three or more dashes):
Raw inline HTML is supported only inside
<details>, <aside>, or
<callout> per project style; everything else uses
Markdown. Unicode renders directly: α, β, γ, Δ, ∑, ∞, ≈, →. For PDF
math, prefer LaTeX ($\alpha$) over raw Unicode in
equations.
The interface separating a system from its environment.
How the state of a system changes over time.
System-level behaviour not present in the parts taken alone.
A state in which opposing influences balance and net change is zero.
A loop in which a system’s output influences its own input.
A spatial or quantitative difference that drives flow or change.
A simplified, often quantitative, representation of a system.
A set of elements (nodes) connected by relationships (edges).
A quantity that can be measured or recorded.
A fixed quantity that configures a model’s behaviour.
The control of a system variable toward a target range.
The configuration of a system at a moment in time.
A set of interacting parts forming an integrated whole.
A critical value at which a qualitative change occurs.
A quantity that can take different values across states or observations.
Reference appendix · Generated index — do not hand-maintain.
This index is intended to be generated at build
time, not edited by hand. A future indexing pass will scan the chapters
for glossary links ([**term**](#gl:<anchor>)) and
crossref labels ({#sec:...}, {#fig:...},
{#tbl:...}, {#eq:...}) and collate
page/section references from the rendered PDF. Until that pass runs, the
entries below are placeholders.
The authoritative term definitions live in Appendix D — Glossary; the closed list of
anchors is GLOSSARY_ANCHORS in src/textbook/constants.py.
See also: Appendix D — Glossary.
Footnotes collect at the end of the document (or page, in PDF). Use them for asides that would interrupt the sentence.↩︎