FIX: index fsolve result in un_insure r_error so brentq gets a scalar#327
Conversation
In `r_error`, `sp.optimize.fsolve(...)` returns a length-1 array, so the
function returned a shape-(1,) array. The outer `brentq` then coerces that
return value to a Python float, which raises under numpy 2.x / scipy 1.17:
TypeError: only 0-dimensional arrays can be converted to Python scalars
Older numpy silently accepted 1-element arrays, so this only surfaced on a
fresh execution with the current quantecon-build image (numpy 2.4.4,
scipy 1.17.1); cache-warmed builds masked it. Index `[0]` to return a
scalar, matching the existing `Vu_aut = fsolve(...)[0]` call just below.
Verified by running the full un_insure notebook inside
ghcr.io/quantecon/quantecon-build:latest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for lustrous-melomakarona-3ee73e ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Fixes execution of the un_insure.md lecture notebook under newer NumPy/SciPy builds by ensuring the brentq objective function returns a scalar (not a length-1 ndarray), preventing TypeError: only 0-dimensional arrays can be converted to Python scalars.
Changes:
- Indexes the
fsolvereturn value inr_error([0]) sor_errorreturns a scalar compatible withscipy.optimize.brentq.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@HumphreyYang would you mind to cast your eye over this. I've done some validation checks and it looks right. But it would be nice to have confirmation. |
|
Many thanks @mmcky! The edit is good. There is another warning that I think I know how to fix. Where should I push my commit? |
|
Thanks @HumphreyYang — yes, resolving that (For context on the close-then-reopen: the mirror PR in |
|
Thanks @mmcky, so the PRs are now synced! I have just pushed the fix and the warning is gone. I also made some PEP8 formatting. |
Thanks @HumphreyYang greatly appreciated. The PRs syncing issue was unintentional and a side effect have the word "fix" in the lecture-dp PR(!) yikes. Added a rule in my global CLAUDE.md to avoid this scenario. |
mmcky
left a comment
There was a problem hiding this comment.
Reviewed and verified inside the actual build container (ghcr.io/quantecon/quantecon-build:latest, python 3.13.13 / numpy 2.4.4 / scipy 1.17.1).
Results match the live page to ~10 significant figures and the rendered figure is byte-identical:
| Check | Live page | This PR | |
|---|---|---|---|
Calibrated r |
0.0003431409393866701 |
0.00034314093941788904 |
✅ |
Check p at r |
0.1000000000000214 |
0.10000000000502518 |
✅ |
| All 5 iteration errors | match to 10+ sig figs | ✅ | |
| Figure (PNG SHA256) | 07de52ef…6578e16 |
identical | ✅ |
| RuntimeWarning | present | gone | ✅ |
The tiny last-digit drift in the iteration trace is just max(0, …) cutting off a small infeasible region during fsolve. Converged policy functions and the rendered plot are identical at display precision.
One small non-blocking suggestion on the PR body: the title/description reads "update fix and PEP8 check", but max(0, invp_prime(...)) inside Vu_error is actually a substantive numerical-behavior change (not pure refactor). It brings Vu_error into line with the clamp calc_a already had — so both functions now consistently enforce a ≥ 0, which is what eliminates the iteration not making good progress warning. Worth a one-line note in the PR body, so future readers see the substantive vs. cosmetic split clearly.
Nice catch on the warning and clean PEP8 sweep 🙏
🤖 Generated with Claude Code
|
@HumphreyYang please merge when you're ready. Just did a few quick updates to PR descriptions and ran a test. |
|
Many thanks @mmcky, merging! |
Brings lecture-dp's un_insure.md back into byte-identical alignment with its primary source (see #7) after upstream PR #327 merged. The minimal [0] index fix that landed earlier in lecture-dp #17 was a first-aid measure to unblock CI. This SYNC pulls in the full upstream version, which adds: * the same [0] index on fsolve(...) so r_error returns a scalar (the original CI fix from #17); * args=(r,) tuple correctness on both fsolve calls; * max(0, invp_prime(...)) inside Vu_error so search effort is clamped to non-negative, matching what calc_a already did. This eliminates a "RuntimeWarning: iteration not making good progress" from fsolve; * PEP8 sweep across the file (spaces around operators, no spaces around = in kwargs, two blank lines between top-level defs, comment spacing, long-line wrapping); * two spelling fixes (utiliy -> utility, consumpton -> consumption). Verified in ghcr.io/quantecon/quantecon-build:latest (numpy 2.4.4 / scipy 1.17.1): runs clean with no warnings, calibration r matches the published live page to ~10 significant figures, and the rendered figure is byte-identical (same PNG SHA256). Mirrors upstream change in QuantEcon/lecture-python-advanced.myst#327 (authored by @HumphreyYang). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Problem
un_insure.mdfailed to execute on a fresh build with the currentquantecon-buildimage (numpy 2.4.4, scipy 1.17.1):It also emitted a
RuntimeWarning: The iteration is not making good progressfromfsolve.Cause
In the calibration cell,
r_errorwas:fsolvereturns a length-1 array, sor_errorreturned a shape-(1,)array. The outerbrentqthen coerces that to a Python float; under numpy 2.x this raises (older numpy silently accepted 1-element arrays). Cache-warmed CI builds masked it because the notebook wasn't re-executed.Vu_errorcomputeda = invp_prime(...)without enforcinga ≥ 0, so duringfsolve's inner search the implied effortacould go negative — feeding back into the error and causing the not making good progress warning. (calc_aalready hada = max(0, ...); the two functions were inconsistent.)Fix
Three changes:
Scalar return from
r_error— index[0]so thebrentqobjective returns a Python float (same pattern asVu_aut = fsolve(...)[0]just below;args=(r,)also corrects the missing tuple comma):Enforce
a ≥ 0insideVu_error— small numerical-behavior change that bringsVu_errorinto line with the clampcalc_aalready had, so both functions consistently enforce the economic constraint that search effort is non-negative:This eliminates the iteration not making good progress
RuntimeWarning.PEP8 / style sweep across the file (spaces around operators, no spaces around
=in kwargs, two blank lines between top-level defs, comment spacing, long-line wrapping) and two spelling fixes (utiliy→utility,consumpton→consumption).Verification
Ran the full
un_insure.mdnotebook insideghcr.io/quantecon/quantecon-build:latest(numpy 2.4.4 / scipy 1.17.1):r = 0.00034314093941788904— matches the published live page (0.0003431409393866701) to ~10 significant figures.07de52ef…6578e16).Tiny last-digit drift in the iteration trace comes from
max(0, …)cutting off a small infeasible region duringfsolve; converged policy functions and the plot are identical at display precision.Context
Surfaced while syncing this lecture into
lecture-dp; a matching minimal fix landed there as QuantEcon/lecture-dp#17. A follow-up SYNC will bring the warning fix + PEP8 cleanup downstream too.Warning fix,
max(0, …)clamp, and PEP8 sweep contributed by @HumphreyYang.🤖 Generated with Claude Code