Appendix A
The Python code in this book is, with a small number of carefully marked exceptions, real. You can copy it into a file. You can run it. The functions will do what they say they do, give or take the specifics of your Python installation.
This appendix collects the working code from across the chapters into one place, so that you have, in a single document, the executable parts of the toolkit. I have added a few small utility functions that did not earn a place in any single chapter but that, over the years, have turned out to be useful enough in my own daily life to include here. The whole thing is, in total, well under two hundred lines of Python. You could, if you wanted, paste it into a single file called anxious_toolkit.py and have, by the end of an afternoon, a working library you can import from any project.
A note on what is here and what is not. The functions below are the ones that actually run. The book also contains, in Chapters 10, 11, 18, and 19, code that is deliberately broken or that uses Python syntax as a notation for procedures whose implementation is, on inspection, performed by your brain rather than by an interpreter. That code is not in this appendix, because it would not, on import, do anything useful. If you want it, it is in the chapters where it lives. This appendix is the runnable parts.
The functions are organized roughly in the order they appeared in the book. Each one has a short note pointing at the chapter where the underlying concept was introduced. Read the chapter first, if you have not. The function alone, without the concept, is a tool without a use.
Part one
From the diagnosis.
The brain's threat-detection function, miscalibrated. Returns 'leopard' for every input, because the legacy code was written for an environment that has not existed for a hundred thousand years.
def brain_threat_response(stimulus):
"""
The threat-response function as installed by evolution,
running on the modern nervous system. Notice that it
does not actually examine the input.
"""
return 'leopard'
Part two
From the toolkit.
Returns both the function value and its derivative. The anxious mind, by default, returns only the second and tells you it is the first.
def how_is_life_going(history):
"""
history: a list of numeric values representing your life
at evenly spaced moments. Higher is better.
Returns: a tuple (position, velocity).
"""
if not history:
return (None, None)
position = history[-1]
if len(history) < 2:
return (position, None)
velocity = history[-1] - history[-2]
return (position, velocity)
Roll downhill until you stop. Will find a local minimum and refuse to leave, even if a deeper valley is available somewhere over the next hill.
def gradient_descent(start, landscape, step_size, steps):
position = start
for _ in range(steps):
slope = (landscape(position + 0.001)
- landscape(position - 0.001)) / 0.002
position = position - step_size * slope
if abs(slope) < 1e-6:
break
return position
Closes the gap to the target by a fixed proportion at each step. Gets arbitrarily close. Never arrives. Run it with a learning rate above 1 to simulate the overshooter.
def chase_happiness(target, position, learning_rate, iterations):
history = [position]
for _ in range(iterations):
gap = target - position
position = position + learning_rate * gap
history.append(position)
return history
Given a transformation, find the directions in which it does not rotate. These are the eigenvectors. They are what life cannot turn.
import numpy as np
def find_eigen_directions(transformation):
"""
transformation: a 2x2 numpy array
Returns: a list of (eigenvalue, eigenvector) pairs.
"""
eigenvalues, eigenvectors = np.linalg.eig(transformation)
return list(zip(eigenvalues, eigenvectors.T))
One Bayesian update. Strong priors yield slowly, then all at once. The collapse is mathematically guaranteed if the evidence is real and you keep producing it.
def bayesian_update(prior, likelihood_if_true, likelihood_if_false):
"""
prior: belief in the hypothesis, 0..1
likelihood_if_true: P(evidence | hypothesis_true)
likelihood_if_false: P(evidence | hypothesis_false)
Returns the updated belief after the evidence.
"""
numerator = likelihood_if_true * prior
denominator = (likelihood_if_true * prior
+ likelihood_if_false * (1 - prior))
return numerator / denominator
Generate a sequence of daily moods drawn from a normal distribution. Useful for watching regression to the mean happen in numbers.
import random
def simulate_mood(days, mean=6.0, std=1.5, seed=0):
rng = random.Random(seed)
return [rng.gauss(mean, std) for _ in range(days)]
Greedy: take the biggest coin that fits, repeat. Fast. Often wrong.
def make_change_greedy(target, coins):
coins = sorted(coins, reverse=True)
used = []
for coin in coins:
while target >= coin:
target -= coin
used.append(coin)
return used
Dynamic programming: consider, at each step, what future moves a current choice preserves. Slower. Correct.
def make_change_dp(target, coins):
best = [0] + [None] * target
choice = [None] * (target + 1)
for amount in range(1, target + 1):
for coin in coins:
if coin <= amount and best[amount - coin] is not None:
candidate = best[amount - coin] + 1
if best[amount] is None or candidate < best[amount]:
best[amount] = candidate
choice[amount] = coin
used = []
a = target
while a > 0:
used.append(choice[a])
a -= choice[a]
return used
Watch a small system drift from order to disorder as the second law of thermodynamics does what it does.
def simulate_disorder(n_particles=20, n_steps=500, seed=42):
rng = random.Random(seed)
positions = [1] * n_particles
history = []
for step in range(n_steps):
i = rng.randrange(n_particles)
positions[i] = rng.choice([0, 1])
history.append(sum(positions))
return history
A toy demonstration that observing a sensitive variable changes it. Watch the average drop as the observation frequency rises.
def simulate_observation(duration=100, base=6.0, freq=0.0,
cost=0.4, seed=0):
rng = random.Random(seed)
value = base
history = []
for _ in range(duration):
value += rng.gauss(0, 0.3)
value = max(0, min(10, value))
if rng.random() < freq:
value -= cost
history.append(value)
return sum(history) / len(history)
A heuristic for the traveling salesman problem. Not optimal. Finishes. The two are not the same thing.
import math
def distance(a, b):
return math.hypot(a[0] - b[0], a[1] - b[1])
def tour_length(cities, route):
total = 0
for i in range(len(route)):
total += distance(cities[route[i]],
cities[route[(i + 1) % len(route)]])
return total
def nearest_neighbor_tsp(cities, start=0):
n = len(cities)
unvisited = set(range(n))
route = [start]
unvisited.remove(start)
current = start
while unvisited:
nxt = min(unvisited,
key=lambda c: distance(cities[current], cities[c]))
route.append(nxt)
unvisited.remove(nxt)
current = nxt
return tuple(route), tour_length(cities, route)
Part three
From the constructive practices.
Identify, in a graph of references, what is still reachable from the roots. Everything else is garbage. This function decides by reference tracing, not by emotional valence.
def mark_and_sweep(heap, roots):
live = set()
queue = list(roots)
while queue:
obj_id = queue.pop()
if obj_id in live:
continue
live.add(obj_id)
for ref in heap[obj_id]['refs']:
if ref not in live:
queue.append(ref)
return {obj_id: obj for obj_id, obj in heap.items()
if obj_id in live}
New utilities
Small additions that did not earn their own chapter.
What follows are three small helper functions that draw on the concepts in the book but did not, on their own, justify a chapter. They are the kind of thing that is easier to keep around as code than as a habit, because code, unlike a habit, does not require you to remember it on a Tuesday at three in the morning. You can call the code. You can read the docstring. The code will respond the same way regardless of your mood, which is, on inspection, exactly the property the chapters have been arguing makes mathematics worth keeping around as company.
Given a chain of conditional probabilities representing the steps in a feared cascade, returns the overall probability of the feared outcome. Useful for one-in-three-thousand calculations on the back of a notional envelope.
def expected_value_of_fear(stages):
"""
stages: a list of conditional probabilities in [0, 1],
each one the probability of the next step given
the previous one. Example for a feared career
consequence:
[0.33, # they noticed the awkward email
0.20, # given noticed, they formed bad opinion
0.10, # given bad opinion, they still remember
0.05] # given remembered, it affects something
Returns:
the overall probability of the feared end state.
"""
p = 1.0
for s in stages:
p *= s
return p
Decides, by a simple heuristic, whether the current thought has drifted far enough from its original seed to count as a "but what if" cascade rather than a real piece of reasoning. The implementation is, on inspection, a piece of self-honesty that the function cannot perform for you.
def cascade_check(current_thought, original_seed):
"""
current_thought, original_seed: short strings describing
what your mind is currently chewing on and what you
actually started with.
Returns:
'on topic' if the two appear to be discussing the
same concrete situation, 'cascade' if the current
thought is significantly more abstract than the seed.
Note: the abstraction check is by word overlap, which is
a crude heuristic. The actual diagnosis is the one you
perform when you read the two strings side by side and
notice how much further the current thought has drifted.
"""
seed_words = set(original_seed.lower().split())
thought_words = set(current_thought.lower().split())
overlap = len(seed_words & thought_words)
if overlap >= 2:
return 'on topic'
return 'cascade'
A rough estimate of how strongly you are still holding a particular Bayesian prior, given how long you have held it and how much contradictory evidence you have generated. Not a measurement. A prompt to think about one.
def prior_strength(years_held, contradictory_observations):
"""
years_held: how long you have held the belief, in years
contradictory_observations: how many concrete pieces of
evidence against it you can name without effort
Returns:
a rough estimate of your current posterior, on a
scale of 0 to 1, where 1 means you still hold the
original belief with full confidence.
The estimate is not a measurement. It is a structured
prompt that converts an internal sense into a number
you can argue with.
"""
if years_held <= 0:
return 0.5
base = 1.0 - (1.0 / (1.0 + 0.1 * years_held))
decay_per_observation = 0.1
posterior = base - decay_per_observation * contradictory_observations
return max(0.0, min(1.0, posterior))
This is the toolkit. It is not, in any sense, the entire toolkit. The chapters contain the rest. The chapters are the part you read. This appendix is the part you import.
A small note before the appendix closes. I have, over the years, been asked some version of the following question by people who have read drafts of this material: does writing this stuff down as code, in a Python file, actually help, or is it just a way of feeling productive?
The honest answer is that the helping is, in my experience, real but indirect. Reading the code does not, on its own, calm a panic attack. Running the simulations does not, by itself, lift a depression. The code is not a piece of magic. The code is a small piece of intellectual companionship, available at any hour, in a notation that does not negotiate with the anxious mind on the anxious mind's preferred terms. The code is a friend who does not flinch. The code is a friend who does not get tired. The code is a friend whose answer, on the seven hundredth visit, will be exactly what it was on the first.
For a particular kind of mind, this kind of company has been, in my experience, more useful than I expected. For your kind of mind, it may or may not be. The only way to find out is to copy the code into a file, run it once, and see whether the act of doing so changes anything inside you that nothing else has been able to change. If yes, the appendix has earned its keep. If no, no harm done. The functions will be where they are. You can come back to them later.