Skip to content

Use DEFAULT_CALLBACK const in SciPy/Sophia/NLopt/Metaheuristics __init - #1412

Draft
ChrisRackauckas-Claude wants to merge 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:opt-default-callback-const-rest
Draft

ChrisRackauckas-Claude wants to merge 1 commit into
SciML:masterfrom
ChrisRackauckas-Claude:opt-default-callback-const-rest

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow-up to #1407 for the remaining four wrappers that still defaulted callback to a fresh anonymous (args...) -> (false) in __init. Through Julia's keyword sorter that default is re-evaluated with a different type, so typeof(init(prob, alg)) != typeof(init(prob, alg; maxiters=100)) and a second solve that adds a kwarg recompiles the chain. Point each default at the exported public OptimizationBase.DEFAULT_CALLBACK (NullCallback; same form as MadNLP/ODE/PRIMA and #1407). Callback call sites already use callback(opt_state, x...), which NullCallback accepts.

Measurements (amdci2, process CPU via /proc/self/stat utime+stime, OPENBLAS_NUM_THREADS=1, JULIA_NUM_THREADS=1, taskset -c 2, 5 alternating master/PR fresh processes)

OptimizationNLopt Rosenbrock + NLopt.LN_NELDERMEAD + ForwardDiff:

master (n=5) PR (n=5)
TTFS LN_NELDERMEAD (first solve, no kwargs) median 2.09 s (2.07–2.46) median 2.07 s (2.05–2.09)
2nd solve with maxiters=100 median 0.43 s (0.42–0.44) median 0.14 s (0.14–0.15)

OptimizationMetaheuristics Rosenbrock + Metaheuristics.ECA:

master (n=5) PR (n=5)
TTFS ECA (first solve, no kwargs) median 2.54 s (2.53–2.55) median 2.54 s (2.51–2.85)
2nd solve with maxiters=100 median 0.53 s (0.52–0.54) median 0.14 s (0.13–0.15)

First-solve: no clear regression. Second-solve with kwargs: clear win for both (~0.43→0.14 s NLopt; ~0.53→0.14 s Metaheuristics).

OptimizationSophia / OptimizationSciPy: same source change; SciPy could not be loaded locally (PythonCall/CondaPkg pixi install failed in the test temp env despite system scipy 1.15.3). Sophia init type-stability regression test passes inside its Core suite.

Regression test

typeof(init(...)) == typeof(init(...; maxiters=100)) in each sublib's test/core_tests.jl.

  • Before (src stashed): FAIL — distinct var"#…#…" callback types (before.txt for NLopt + Metaheuristics)
  • After: PASS (after.txt); also PASS on Julia 1.10.12 for NLopt + Metaheuristics

Test-group tails

  • OptimizationNLopt Core: Core | 43 Pass; tests passed
  • OptimizationMetaheuristics Core: Core | 51 Pass; tests passed
  • OptimizationNLopt QA: Quality Assurance | 27 Pass; tests passed
  • OptimizationMetaheuristics QA: Quality Assurance | 68 Pass, 1 Broken; tests passed
  • OptimizationSophia QA: Quality Assurance | 20 Pass, 1 Broken; tests passed
  • Root GROUP=QA (via test/qa): Quality Assurance | 21 Pass
  • OptimizationSophia Core: suite reports 3 Pass, 1 Fail — failure is pre-existing neural-ODE 10 * res1.objective < l1 at core_tests.jl:72, not the new DEFAULT_CALLBACK test (which Passes). Not attributed to this change.
  • OptimizationSciPy Core/QA: not runnable locally (CondaPkg/pixi)

Not verified

  • SciPy load / TTFS / Core (CondaPkg pixi install failed)
  • Sophia TTFS (heavy Zygote/Lux Core path; only type-stability + QA checked)
  • Runtime @benchmark (compile-time / type-stability fix; numbers are process CPU)
  • Downstream packages

Reviewer pushback

  • Metaheuristics sets allowscallback = false — is fixing its anonymous default still worth it for init cache type stability alone? (measured second-solve win says yes)
  • Sophia Core already fails an unrelated objective assertion locally — should CI be trusted over this machine for that suite?
  • Should SciPy's identical one-line change wait until CondaPkg can be exercised here?

Please ignore until reviewed by @ChrisRackauckas.

Risk assessment

Independent review: pending

🤖 Generated with Cursor Agent 2026.10.01-14929f9 (model: unknown (Cursor auto)), transcript /home/crackauc/sandbox/goals/performance/jobs/opt/opt-defcb2/log.txt on amdci2; orchestrated by Claude Code (claude-opus-5-5[1m]) https://claude.ai/code/session_01LPHREnnonfLg1VcE1EJovv

Made with Cursor

Independent review: Devin Fusion (fusion-claude-opus-5-5-high-sidekick-swe-2-medium) rated it low, verdict MERGE: #1412 (comment)

Anonymous (args...) -> (false) defaults get a new type through the kwarg
sorter, so init with/without kwargs disagree and a second solve recompiles.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Cursor Agent <noreply@cursor.com>
Agent-Harness: Cursor Agent 2026.10.01-14929f9
Agent-Model: unknown (Cursor auto)
Agent-Session: local session, transcript /home/crackauc/sandbox/goals/performance/jobs/opt/opt-defcb2/log.txt on amdci2
@ChrisRackauckas

Copy link
Copy Markdown
Member

🤖 Automated comment from an AI agent running as @ChrisRackauckas — not written or reviewed by Chris.

Independent review (Devin CLI 3000.11.3, model fusion-claude-opus-5-5-high-sidekick-swe-2-medium): MERGE, risk low.

Full review

VERDICT: MERGE
RISK: low

PR: #1412 (head e266d06). This PR changes the default callback kwarg in SciMLBase.__init from (args...) -> (false) to OptimizationBase.DEFAULT_CALLBACK in four sublibs (NLopt, Metaheuristics, Sophia, SciPy). Each sublib also gets a typeof(init(prob, alg)) == typeof(init(prob, alg; maxiters = 100)) test. The change is narrow and correct, and the regression test does fail without the fix.

Blocking findings

None.

Non-blocking findings

  1. The PR body undersells its own verification. Confirmed by running. The body says SciPy "could not be loaded locally" and that Sophia Core fails locally. Here, SciPy loaded on the first try (CondaPkg/pixi built its env), and my probe passed 5/5 on the PR, including a halting user callback. CI on this PR also shows OptimizationSciPy Core (Julia 1) at 161 Pass, 1 Broken and Sophia Core green on Julia 1 and lts. So the "not verified" caveats are out of date, not a sign of a problem.
  2. OptimizationSciPy [QA] is red on CI. The error is InitError: Could not find Python library for Python executable ".../.CondaPkg/.pixi/envs/default/bin/python", raised at package load inside the QA temp env (job https://github.com/SciML/Optimization.jl/actions/runs/38075924335/job/114287326396). That is an environment/CondaPkg failure. A one-line kwarg default cannot cause it. I did not check whether master fails the same way, because master CI only runs sublib QA when the sublib changes. Suspected from reading.
  3. Sophia ignores a halting callback. This already happens on master; not caused by this PR. Confirmed by running. A callback returning true is called, but the solve does not stop: the probe ran 200 calls with retcode: Success, on both master and the PR. The cause is the break at lib/OptimizationSophia/src/OptimizationSophia.jl:144, which only exits the inner data loop, not the epoch loop. Out of scope here; it deserves its own issue.
  4. The comment line # Default callback must be a stable const ... is repeated in all four test files. That is 4 of 42 added lines (~9.5%), just under the CLAUDE.md ~10% ceiling. Acceptable.
  5. Interface fit with sibling work. Use DEFAULT_CALLBACK const in OptimJL/Optimisers __init #1407 (OPEN) touches only OptimJL and Optimisers. The two PRs change disjoint files. Once both merge, grep "(args...) -> (false)" lib/*/src returns nothing; today it shows exactly OptimJL:178 and Optimisers:47, which Use DEFAULT_CALLBACK const in OptimJL/Optimisers __init #1407 covers. Both PRs use the same pattern as MadNLP, ODE and PRIMA.
  6. Compat. All four sublibs already require OptimizationBase = "5.5.0". DEFAULT_CALLBACK = NullCallback() has existed since OptimizationBase became a sublibrary (commit 74a6266), so no compat bump is needed. No new dependencies, and no non-public API: DEFAULT_CALLBACK is exported from OptimizationBase.
  7. Behaviour is equivalent. NullCallback takes (args...) and returns false, the same contract as the anonymous function it replaces. Both reject keyword arguments. Metaheuristics has allowscallback = false: a user-supplied callback still throws, and passing DEFAULT_CALLBACK still solves (confirmed by the probe).

What I ran

All runs used Julia 1.11.8 on macOS arm64. I wrote scratch/probe.jl and ran it in fresh environments, each with that tree's lib/OptimizationBase and the sublib dev'd, once for the PR tree and once for an origin/master worktree. The probe checks five things:

  • the init type is the same with and without maxiters;
  • cache.callback === DEFAULT_CALLBACK;
  • solve works with and without maxiters;
  • a user callback that returns true on its 3rd call actually halts the solve;
  • for Metaheuristics instead: a user callback throws, and passing DEFAULT_CALLBACK solves.

Probe results:

Sublib master PR
NLopt 3/5. Callback types var"#2#4" vs var"#3#5": the two init calls produce different cache types 5/5, NullCallback
Metaheuristics 4/6 6/6
SciPy 3/5 5/5. The halting callback stops the solve via "Optimization halted by callback"
Sophia 2/5 4/5. The one failure is the halting-callback check from finding 3 (identical on master)

Real test suites, with OPTIMIZATION_TEST_GROUP=Core Pkg.test():

  • On the PR:
    • OptimizationNLopt: Core | 43 Pass ("tests passed").
    • OptimizationMetaheuristics: Core | 51 Pass.
  • Fail-before check: master src with the PR's test/core_tests.jl:
    • NLopt: 42 Pass, 1 Fail.
    • Metaheuristics: 50 Pass, 1 Fail.
    • In both, the only failure is the new DEFAULT_CALLBACK type stability across kwargs testset.

I also read the PR's CI with gh pr checks 1412. All Core jobs for the four sublibs pass on Julia 1, lts and pre, and Runic and typos pass. Red jobs: SciPy QA (finding 2), downgrade-sublibraries / OptimizationReactant, and the MTK downstream job. I did not open the logs of the last two; neither touches the changed code.

Afterwards I deleted the Manifest.toml files the test runs created and removed the master worktree. git status --porcelain in the checkout is empty, and none of my Julia processes are left running.

What I did not verify

  • Any of the PR's TTFS numbers: the second-solve times of 0.43→0.14 s for NLopt and 0.53→0.14 s for Metaheuristics. I verified the type-stability mechanism that would produce them, not the timings.
  • Sophia's full Core suite locally (Lux, SciMLSensitivity, OrdinaryDiffEq). CI runs it green; I only ran the Sophia probe.
  • QA groups locally, and the Julia 1.10 runs claimed in the body. CI covers lts for Core.
  • Whether SciPy QA and the Reactant downgrade failures also happen on master.

🤖 Posted by an AI agent — harness: Devin CLI 3000.11.3 (review), Claude Code 2.1.285 (posting) · model: fusion-claude-opus-5-5-high-sidekick-swe-2-medium
Conversation: local Claude session 3c311569-dda6-4687-bc9f-65febb212c33 (fleet master)

This branch has not been deployed

No deployments
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.

2 participants