Migration Guide

This comprehensive guide covers migration to NLSQ from SciPy and between NLSQ versions.


Migrating from SciPy

Quick Start

Minimal changes required to migrate from ``scipy.optimize.curve_fit``:

Before (SciPy):

from scipy.optimize import curve_fit
import numpy as np


def exponential(x, a, b):
    return a * np.exp(-b * x)


x = np.linspace(0, 5, 1000)
y = 2.5 * np.exp(-1.3 * x) + 0.01 * np.random.randn(1000)

popt, pcov = curve_fit(exponential, x, y, p0=[2, 1])

After (NLSQ):

from nlsq import curve_fit
import jax.numpy as jnp  # Changed from numpy
import numpy as np  # Keep for data generation


def exponential(x, a, b):
    return a * jnp.exp(-b * x)  # Changed to jnp


x = np.linspace(0, 5, 1000)
y = 2.5 * np.exp(-1.3 * x) + 0.01 * np.random.randn(1000)

popt, pcov = curve_fit(exponential, x, y, p0=[2, 1])

That’s it! The API is nearly identical. Just change np to jnp in your model function.

Key Differences from SciPy

  1. NumPy → JAX NumPy: Model functions must use jax.numpy instead of numpy for GPU acceleration and automatic differentiation.

  2. Method Selection: NLSQ uses only 'trf' (Trust Region Reflective). Remove method='lm' or method='dogbox' parameters.

  3. Automatic Differentiation: Remove manual Jacobian functions. NLSQ uses JAX autodiff which is faster and more accurate.

  4. Double Precision: NLSQ automatically enables float64 precision.

API Compatibility

Parameter

SciPy

NLSQ

f

Yes

Yes*

xdata

Yes

Yes

ydata

Yes

Yes

p0

Yes

Yes

sigma

Yes

Yes

absolute_sigma

Yes

Yes

check_finite

Yes

Yes

bounds

Yes

Yes

method

lm/trf/dogbox

trf only

jac

Yes

Yes**

*Must use jax.numpy in function body

**Autodiff recommended instead

Enhanced Result Object

NLSQ returns a CurveFitResult object with additional features:

# Works like SciPy (tuple unpacking)
popt, pcov = curve_fit(model, x, y)

# NLSQ enhancement: access optimization details
result = curve_fit(model, x, y)
print(f"R² = {result.r_squared:.4f}")
print(f"RMSE = {result.rmse:.4f}")

# Confidence intervals
ci = result.confidence_intervals(alpha=0.95)

# Automatic visualization
result.plot(show_residuals=True)

# Statistical summary
result.summary()

Common Migration Patterns

Conditional Logic (JAX control flow):

# SciPy (works with Python if/else)
def piecewise(x, a, b, c):
    result = np.zeros_like(x)
    mask = x < 5
    result[mask] = a * x[mask] + b
    result[~mask] = c
    return result


# NLSQ (use jnp.where)
def piecewise(x, a, b, c):
    return jnp.where(x < 5, a * x + b, c)

Remove Manual Jacobians:

# SciPy with manual Jacobian
popt, pcov = curve_fit(model, x, y, jac=my_jacobian_func)

# NLSQ (autodiff handles it)
popt, pcov = curve_fit(model, x, y)

Large Datasets:

# NLSQ streaming for large datasets
from nlsq import fit

result = fit(model, x_large, y_large, workflow="streaming", memory_limit_gb=4.0)

When to Migrate

Migrate to NLSQ when:

  • Dataset has > 10,000 points (GPU advantage)

  • Fitting multiple similar datasets (JIT compilation amortized)

  • Working with very large datasets (> 1M points)

Stay with SciPy when:

  • Dataset < 1,000 points (JIT overhead not worth it)

  • Need method='lm' specifically

  • One-off fits in simple scripts

Expected Performance

Dataset Size

SciPy Time

NLSQ Time (GPU)

Speedup

1,000

0.05s

0.43s (first)

0.1x-1.7x

10,000

0.18s

0.04s

4.5x

100,000

2.1s

0.09s

23x

1,000,000

40.5s

0.15s

270x

First call includes JIT compilation. Subsequent calls are much faster.


Version Migration

v0.5.x → v0.6.0

Note

v0.6.0 Deprecation Purge Complete

All deprecated functionality has been completely removed in v0.6.0. There are no deprecation warnings or compatibility shims remaining. The nlsq.compat module has been deleted from the package.

Removed in v0.6.0:

  1. Domain-specific workflow presets - Use core presets instead:

    Removed Preset

    Replacement

    xpcs

    standard

    saxs

    standard

    kinetics

    standard

    dose_response

    quality

    imaging

    streaming

    materials

    standard

    binding

    standard

    synchrotron

    streaming

  2. SloppyModelAnalyzer aliases - Use new names:

    # Before (v0.5.x)
    from nlsq.diagnostics import SloppyModelAnalyzer, SloppyModelReport
    
    # After (v0.6.0)
    from nlsq.diagnostics import ParameterSensitivityAnalyzer, ParameterSensitivityReport
    
  3. IssueCategory.SLOPPY - Use new enum value:

    # Before (v0.5.x)
    if issue.category == IssueCategory.SLOPPY:
        pass  # handle sensitivity issue
    
    # After (v0.6.0)
    if issue.category == IssueCategory.SENSITIVITY:
        pass  # handle sensitivity issue
    
  4. compute_svd_adaptive() - Use new function:

    # Before (v0.5.x)
    from nlsq.stability.svd_fallback import compute_svd_adaptive
    
    # After (v0.6.0)
    from nlsq.stability.svd_fallback import compute_svd_with_fallback
    
  5. nlsq.compat module - Deleted entirely. Import from canonical locations.

v0.4.2 → v0.4.3

Import Path Changes:

# Before (v0.4.x) - deprecated
from nlsq.core._optimize import OptimizeResult, OptimizeWarning

# After (v0.4.3) - recommended
from nlsq.result import OptimizeResult, OptimizeWarning

# Or from package root
from nlsq import OptimizeResult, OptimizeWarning

New Features in v0.4.3:

  • Factory functions: create_optimizer(), configure_curve_fit()

  • Protocol adapters for dependency injection

  • Security hardening for CLI model loading

  • wait_for() utility for reliable test condition waiting


Deprecation Timeline

Item

Deprecated

Removal Version

Replacement

nlsq.core._optimize

v0.4.3

v0.6.0 (removed)

nlsq.result

Domain presets

v0.5.0

v0.6.0 (removed)

Core presets

SloppyModelAnalyzer

v0.5.0

v0.6.0 (removed)

Parameter...

IssueCategory.SLOPPY

v0.5.0

v0.6.0 (removed)

SENSITIVITY

compute_svd_adaptive

v0.3.5

v0.6.0 (removed)

compute_svd_..

nlsq.compat

v0.5.0

v0.6.0 (removed)

Direct imports

result['x'] syntax

v0.5.0

v0.6.0 (removed)

result.x


Finding Deprecated Usage

Run these commands to identify deprecated code in your project:

# Deprecated presets (removed in v0.6.0)
grep -rn "from_preset.*xpcs\|saxs\|kinetics\|dose_response\|imaging\|materials\|binding\|synchrotron" .

# Deprecated class names (removed in v0.6.0)
grep -rn "SloppyModelAnalyzer\|SloppyModelReport" .

# Deprecated enum (removed in v0.6.0)
grep -rn "IssueCategory.SLOPPY" .

# Deprecated SVD function (removed in v0.6.0)
grep -rn "compute_svd_adaptive" .

# Deprecated compat imports (removed in v0.6.0)
grep -rn "from nlsq.compat import" .

# Deprecated dict-style access (removed in v0.6.0)
grep -rn "result\['" --include="*.py" .

Migration Checklist

From SciPy:

  • [ ] Replace from scipy.optimize import curve_fit with from nlsq import curve_fit

  • [ ] Add import jax.numpy as jnp

  • [ ] Change np to jnp in model functions

  • [ ] Remove custom Jacobian functions (use autodiff)

  • [ ] Remove method='lm' or method='dogbox' parameters

  • [ ] Test that results match SciPy (within tolerance)

To v0.6.0:

  • [ ] Replace domain-specific presets with core presets

  • [ ] Replace SloppyModelAnalyzer with ParameterSensitivityAnalyzer

  • [ ] Replace IssueCategory.SLOPPY with IssueCategory.SENSITIVITY

  • [ ] Replace compute_svd_adaptive with compute_svd_with_fallback

  • [ ] Remove imports from nlsq.compat

  • [ ] Replace result['x'] with result.x throughout codebase

  • [ ] Use result.to_dict() if dictionary conversion needed


Getting Help

If you encounter issues during migration:

  1. Check the API documentation

  2. Search GitHub Issues

  3. Open a new issue with the migration label