Skip to content

FIX: index fsolve result in un_insure r_error so brentq gets a scalar#327

Merged
HumphreyYang merged 3 commits into
mainfrom
fix-un_insure-fsolve-scalar-return
May 28, 2026
Merged

FIX: index fsolve result in un_insure r_error so brentq gets a scalar#327
HumphreyYang merged 3 commits into
mainfrom
fix-un_insure-fsolve-scalar-return

Conversation

@mmcky
Copy link
Copy Markdown
Contributor

@mmcky mmcky commented May 28, 2026

Problem

un_insure.md failed to execute on a fresh build with the current quantecon-build image (numpy 2.4.4, scipy 1.17.1):

TypeError: only 0-dimensional arrays can be converted to Python scalars

It also emitted a RuntimeWarning: The iteration is not making good progress from fsolve.

Cause

In the calibration cell, r_error was:

Vu_star = sp.optimize.fsolve(Vu_error_Λ, 15000, args=(r))
a_star  = invp_prime(1/(β*(Ve-Vu_star)), r)
return  p(a_star, r) - 0.1
  • fsolve returns a length-1 array, so r_error returned a shape-(1,) array. The outer brentq then 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.
  • Separately, Vu_error computed a = invp_prime(...) without enforcing a ≥ 0, so during fsolve's inner search the implied effort a could go negative — feeding back into the error and causing the not making good progress warning. (calc_a already had a = max(0, ...); the two functions were inconsistent.)

Fix

Three changes:

  1. Scalar return from r_error — index [0] so the brentq objective returns a Python float (same pattern as Vu_aut = fsolve(...)[0] just below; args=(r,) also corrects the missing tuple comma):

    -    Vu_star = sp.optimize.fsolve(Vu_error_Λ,15000,args = (r))
    +    Vu_star = sp.optimize.fsolve(Vu_error_Λ, 15000, args=(r,))[0]
  2. Enforce a ≥ 0 inside Vu_error — small numerical-behavior change that brings Vu_error into line with the clamp calc_a already had, so both functions consistently enforce the economic constraint that search effort is non-negative:

    -    a = invp_prime(1/(β*(Ve-Vu)),r)
    +    a = max(0, invp_prime(1 / (β * (Ve - Vu)), r))

    This eliminates the iteration not making good progress RuntimeWarning.

  3. 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 (utiliyutility, consumptonconsumption).

Verification

Ran the full un_insure.md notebook inside ghcr.io/quantecon/quantecon-build:latest (numpy 2.4.4 / scipy 1.17.1):

  • ✅ Completes end-to-end with no warnings.
  • ✅ Calibrated r = 0.00034314093941788904 — matches the published live page (0.0003431409393866701) to ~10 significant figures.
  • ✅ All iteration error values match the live page to 10+ significant figures.
  • ✅ Rendered figure is byte-identical to the live page (same PNG SHA256, 07de52ef…6578e16).

Tiny last-digit drift in the iteration trace comes from max(0, …) cutting off a small infeasible region during fsolve; 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

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>
Copilot AI review requested due to automatic review settings May 28, 2026 01:22
@netlify
Copy link
Copy Markdown

netlify Bot commented May 28, 2026

Deploy Preview for lustrous-melomakarona-3ee73e ready!

Name Link
🔨 Latest commit b6e0be4
🔍 Latest deploy log https://app.netlify.com/projects/lustrous-melomakarona-3ee73e/deploys/6a17b0b68daeec00084086f6
😎 Deploy Preview https://deploy-preview-327--lustrous-melomakarona-3ee73e.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 fsolve return value in r_error ([0]) so r_error returns a scalar compatible with scipy.optimize.brentq.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 28, 2026

@github-actions github-actions Bot temporarily deployed to pull request May 28, 2026 01:32 Inactive
@mmcky mmcky requested a review from HumphreyYang May 28, 2026 01:52
@mmcky
Copy link
Copy Markdown
Contributor Author

mmcky commented May 28, 2026

@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.

@HumphreyYang
Copy link
Copy Markdown
Member

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?

/tmp/ipykernel_10718/2412693371.py:6: RuntimeWarning: The iteration is not making good progress, as measured by the 
 improvement from the last five Jacobian evaluations.
  Vu_star = sp.optimize.fsolve(Vu_error_Λ,15000,args = (r))

@mmcky mmcky reopened this May 28, 2026
@mmcky
Copy link
Copy Markdown
Contributor Author

mmcky commented May 28, 2026

Thanks @HumphreyYang — yes, resolving that RuntimeWarning from fsolve is a good idea. Please push directly to this branch (fix-un_insure-fsolve-scalar-return); I've granted maintainer-edit on the PR so you should be able to push.

(For context on the close-then-reopen: the mirror PR in lecture-dp was merged with a commit message containing "Mirrors upstream fix QuantEcon/lecture-python-advanced.myst**#327**", and GitHub's closing-keyword auto-linker treated fix <cross-repo>#N as a closing reference, so it auto-closed this PR. Reopened now.)

@github-actions github-actions Bot temporarily deployed to pull request May 28, 2026 02:23 Inactive
@github-actions github-actions Bot temporarily deployed to pull request May 28, 2026 02:38 Inactive
@HumphreyYang
Copy link
Copy Markdown
Member

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.

@mmcky
Copy link
Copy Markdown
Contributor Author

mmcky commented May 28, 2026

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.

Copy link
Copy Markdown
Contributor Author

@mmcky mmcky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@mmcky
Copy link
Copy Markdown
Contributor Author

mmcky commented May 28, 2026

@HumphreyYang please merge when you're ready. Just did a few quick updates to PR descriptions and ran a test.

@HumphreyYang
Copy link
Copy Markdown
Member

Many thanks @mmcky, merging!

@HumphreyYang HumphreyYang merged commit 6320d71 into main May 28, 2026
9 checks passed
@HumphreyYang HumphreyYang deleted the fix-un_insure-fsolve-scalar-return branch May 28, 2026 05:28
mmcky added a commit to QuantEcon/lecture-dp that referenced this pull request May 28, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants