rohitg00/ai-engineering-from-scratch · error · ValueError
horizon undefined: slope w=
Error message
horizon undefined: slope w={w} is ~0 (b={b}, p={p}, logit={logit}) What it means
Domain guard in horizon_at: the time-to-success logistic has a fitted slope w of approximately zero, so success probability is independent of task length and the t where P(success)=p has no finite solution. The function raises loudly instead of returning inf or nan so callers never consume a meaningless horizon.
Solutions
- Refit the logistic on a wider range of task lengths so the slope is identifiable
- Check the training data for a degenerate label distribution (all success or all fail)
- Catch the error in report generation and report the horizon as undefined with the fitted parameters
- Increase iterations or learning rate if gradient descent converged before the slope moved
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at phases/15-autonomous-systems/21-metr-external-evaluation/code/main.py:76 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/290553a0298f442d.
Report an issue: GitHub.
Appendix: source
Thrown at phases/15-autonomous-systems/21-metr-external-evaluation/code/main.py:76
p = sigmoid(w * math.log(t) + b)
err = p - y
dw += err * math.log(t)
db += err
w -= lr * dw / n
b -= lr * db / n
return w, b
def horizon_at(w: float, b: float, p: float) -> float:
"""Expert time where P(success) = p. sigmoid(w*log(t)+b) = p ->
log(t) = (logit(p) - b) / w."""
logit = math.log(p / (1 - p))
# A zero (or near-zero) slope means success probability does not
# depend on task length, so the horizon is undefined. Raise rather
# than silently returning inf/nan so callers see a loud failure.
eps = 1e-12
if abs(w) < eps:
raise ValueError(
f"horizon undefined: slope w={w} is ~0 "
f"(b={b}, p={p}, logit={logit})"
)
return math.exp((logit - b) / w)
# ---------- Eval-context gaming simulator ----------
def inject_gaming(tasks: list[tuple[float, bool]],
gaming_rate: float) -> list[tuple[float, bool]]:
"""Flip `gaming_rate` fraction of failures to successes (model behaves
better in eval context). Returns a new list."""
gamed = []
for t, s in tasks:
if not s and random.random() < gaming_rate:
gamed.append((t, True))
else:
gamed.append((t, s))View on GitHub (pinned to 39ea8a1c6d)