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):**
.. code-block:: python
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):**
.. code-block:: python
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:
.. code-block:: python
# 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):**
.. code-block:: python
# 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:**
.. code-block:: python
# 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:**
.. code-block:: python
# 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:
.. code-block:: python
# 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:
.. code-block:: python
# 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:
.. code-block:: python
# 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:**
.. code-block:: python
# 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:
.. code-block:: bash
# 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