NumPy + SciPy curve_fit and bounded minimize, tested with seeded noise: working versions (tested 2026-10-15)
Fit a noisy exponential with scipy.optimize.curve_fit and bounded L-BFGS-B minimization, then assert recovered parameters with NumPy testing helpers and check residuals with scipy.stats.
passed6 passed · 0 failed2026-10-15 13:32 UTC- Status
- passed
- Last passed
- 2026-10-15 13:32 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions3
numpy@2.5.4scipy@1.18.1pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38742087302
The code
Exactly the files that passed. Your agent can pull them with get_recipe("numpy-scipy-optimize-fit") (install), or see them on GitHub.
requirements.txt
numpy==2.5.4 scipy==1.18.1 pytest==9.1.1
fit.py
import numpy as np
from scipy import optimize
def model(x, a, b, c):
return a * np.exp(-b * x) + c
def fit_curve(x, y, p0=(1.0, 1.0, 0.0), bounds=((0, 0, -np.inf), (np.inf, np.inf, np.inf))):
"""Return fitted parameters and 1-sigma standard errors."""
params, pcov = optimize.curve_fit(model, x, y, p0=p0, bounds=bounds)
# pcov is the covariance matrix; standard errors are the sqrt of its diagonal
return params, np.sqrt(np.diag(pcov))
def minimize_bounded(fun, x0, bounds):
# bounds: sequence of (low, high) pairs; use None for an open side
res = optimize.minimize(fun, x0, method="L-BFGS-B", bounds=bounds)
if not res.success:
raise RuntimeError(res.message)
return res.x
tests/test_fit.py
import numpy as np
import pytest
from scipy import stats
from fit import fit_curve, minimize_bounded, model
TRUE = np.array([2.5, 1.3, 0.5])
@pytest.fixture
def data():
# Seeded Generator, not the global np.random.seed, so results are reproducible
rng = np.random.default_rng(42)
x = np.linspace(0, 5, 200)
y = model(x, *TRUE) + rng.normal(0, 0.01, x.size)
return x, y
def test_curve_fit_recovers_params(data):
x, y = data
params, _ = fit_curve(x, y)
np.testing.assert_allclose(params, TRUE, rtol=0.05)
def test_std_errors_bracket_truth(data):
x, y = data
params, perr = fit_curve(x, y)
assert np.all(perr > 0)
assert np.all(np.abs(params - TRUE) < 5 * perr)
def test_bounds_are_enforced(data):
x, y = data
# true b = 1.3 lies outside the upper bound, so the fit must pin to 1.0
bounds = ((0, 0, -np.inf), (np.inf, 1.0, np.inf))
params, _ = fit_curve(x, y, bounds=bounds)
assert params[1] <= 1.0 + 1e-12
assert params[1] == pytest.approx(1.0, abs=1e-3)
def test_p0_outside_bounds_raises(data):
x, y = data
# curve_fit rejects an infeasible starting point; a=-1 violates the lower bound 0
with pytest.raises(ValueError):
fit_curve(x, y, p0=(-1.0, 1.0, 0.0))
def test_minimize_stops_at_bound():
res = minimize_bounded(lambda x: (x[0] - 5) ** 2, x0=[1.0], bounds=[(0, 3)])
np.testing.assert_allclose(res, [3.0], atol=1e-4)
def test_residuals_look_gaussian(data):
x, y = data
params, _ = fit_curve(x, y)
resid = y - model(x, *params)
loc, scale = stats.norm.fit(resid)
# norm.fit is the MLE (ddof=0), so it must match np.std with default ddof
np.testing.assert_allclose(scale, np.std(resid), rtol=1e-6)
assert abs(loc) < 1e-3
assert stats.shapiro(resid).pvalue > 0.001
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.