Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

name: tests

on:
push:
branches:
- main
pull_request:


jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [
ubuntu-latest,
windows-latest,
macOS-latest,
]
python-version: ["3.10", "3.11", "3.12", "3.13"]
name: ${{ matrix.os }}, Python ${{ matrix.python-version }} lint and test
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install package
run: |
python -m pip install --upgrade pip
pip install setuptools wheel
pip install -e .[test]
- name: Test with pytest
run: |
pip install pytest pytest-cov
pytest --cov=ndoptimize --cov-report=xml
- name: Test with Numba disabled
env:
NUMBA_DISABLE_JIT: 1
run: |
pytest --cov=ndoptimize --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.xml
flags: unittests
env_vars: OS,PYTHON
name: codecov-umbrella
fail_ci_if_error: true
26 changes: 26 additions & 0 deletions .github/workflows/typing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: typing

on:
push:
branches:
- main
pull_request:

jobs:
pyright:
name: Check type annotations with pyright
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install package
run: |
python -m pip install --upgrade pip
pip install -e .[test]
- name: Check types with pyright
uses: jakebailey/pyright-action@v2
with:
version: 1.1.411
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
__pycache__/
*.py[cod]

/build/
/dist/
*.egg-info/

docs/_autosummary/
docs/_build/
docs/jupyter_execute/

/venv/
.idea/

.coverage
coverage.xml
21 changes: 21 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
cff-version: 1.2.0
message: "If you use ndoptimize in your research, please cite it using these metadata."
type: software
title: ndoptimize
abstract: >-
Numba-accelerated minimization of objective functions for many independent
problems at once, including batched Levenberg-Marquardt least squares with
correlated noise, priors and bounds.
authors:
- family-names: Smart
given-names: Roy T.
orcid: "https://orcid.org/0000-0002-9997-5515"
repository-code: "https://github.com/sun-data/ndoptimize"
url: "https://ndoptimize.readthedocs.io"
license: BSD-3-Clause
keywords:
- nonlinear least squares
- Levenberg-Marquardt
- curve fitting
- spectral line fitting
- Numba
28 changes: 28 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
BSD 3-Clause License

Copyright (c) 2024-2026, Roy T. Smart

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,73 @@
# ndoptimize

[![tests](https://github.com/sun-data/ndoptimize/actions/workflows/tests.yml/badge.svg)](https://github.com/sun-data/ndoptimize/actions/workflows/tests.yml)
[![codecov](https://codecov.io/gh/sun-data/ndoptimize/graph/badge.svg)](https://codecov.io/gh/sun-data/ndoptimize)
[![typing](https://github.com/sun-data/ndoptimize/actions/workflows/typing.yml/badge.svg)](https://github.com/sun-data/ndoptimize/actions/workflows/typing.yml)
[![Black](https://github.com/sun-data/ndoptimize/actions/workflows/black.yml/badge.svg)](https://github.com/sun-data/ndoptimize/actions/workflows/black.yml)
[![Ruff](https://github.com/sun-data/ndoptimize/actions/workflows/ruff.yml/badge.svg)](https://github.com/sun-data/ndoptimize/actions/workflows/ruff.yml)
[![Documentation Status](https://readthedocs.org/projects/ndoptimize/badge/?version=latest)](https://ndoptimize.readthedocs.io/en/latest/?badge=latest)
[![PyPI version](https://badge.fury.io/py/ndoptimize.svg)](https://badge.fury.io/py/ndoptimize)

Numba-accelerated minimization of objective functions.
`ndoptimize` minimizes objective functions for many independent problems at
once, similar to
[`scipy.optimize`](https://docs.scipy.org/doc/scipy/reference/optimize.html),
but compiled and parallelized using
[Numba](https://numba.readthedocs.io/en/stable/).

Its main function, `ndoptimize.least_squares()`, fits a model to every
spectrum of a spectral cube, or any other batch of small datasets, using the
Levenberg-Marquardt method. Compared to calling
`scipy.optimize.least_squares()` in a loop, it offers:

- **Speed.** The fits run in parallel on every CPU core, roughly half a
million fits of a five-parameter model to 39 points per second on a
48-thread machine.
- **Correlated noise.** The noise of neighboring data points may be
correlated, as it is in resampled data, which makes the fit generalized
least squares, with parameter uncertainties to match.
- **Priors and bounds.** Each parameter of each fit may have a Gaussian prior
and bounds, to keep faint fits stable.
- **Missing data.** Data points which are NaN are left out of their fit.
- **Broadcasting.** The data, noise, initial guess, bounds and priors are
broadcast against each other, so each fit can have its own.

## Installation

```bash
pip install ndoptimize
```

## Example

```python
import numpy as np
import ndoptimize

velocity = np.linspace(-100, 100, 41)
data = ... # spectra, shape (..., 41)
variance = ... # their noise variance, the same shape

result = ndoptimize.least_squares(
model=ndoptimize.models.gaussian_linear,
x=velocity[:, np.newaxis],
data=data,
variance=variance,
guess=[5, 0, 15, 0, 1], # amplitude, center, width, slope, intercept
)

result.parameters # shape (..., 5)
result.covariance # shape (..., 5, 5)

# the best-fit model at each point of each spectrum, shape (..., 41)
fit = ndoptimize.evaluate(
model=ndoptimize.models.gaussian_linear,
x=velocity[:, np.newaxis],
parameters=result.parameters,
)
```

Models are short Numba functions which compute the model and its Jacobian,
compiled with the `ndoptimize.model` decorator, which also names their
parameters.
See the [documentation](https://ndoptimize.readthedocs.io/en/latest) for
details.
5 changes: 4 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# -- Project information -----------------------------------------------------

project = 'ndoptimize'
copyright = '2024, Roy T. Smart'
copyright = '2024-2026, Roy T. Smart'
author = 'Roy T. Smart'

# -- General configuration ---------------------------------------------------
Expand All @@ -39,6 +39,9 @@
]
autosummary_generate = True # Turn on sphinx.ext.autosummary
autosummary_imported_members = True
# the stubs of `ndoptimize.Model` and `ndoptimize.model` would have the same
# name on a case-insensitive file system
autosummary_filename_map = {'ndoptimize.model': 'ndoptimize.model-decorator'}
# autoclass_content = 'both'
autodoc_typehints = "description"

Expand Down
23 changes: 20 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
Introduction
============

:mod:`ndoptimize` is an experimental library designed to parallelize and
accelerate objective function minimization, similar to the :mod:`scipy.optimize`
package, using the `Numba <https://numba.readthedocs.io/en/stable/>`_
:mod:`ndoptimize` minimizes objective functions for many independent
problems at once, similar to the :mod:`scipy.optimize` package, but compiled
and parallelized using the `Numba <https://numba.readthedocs.io/en/stable/>`_
just-in-time compiler.

Its main function, :func:`ndoptimize.least_squares`, fits a model to every
spectrum of a spectral cube, or any other batch of small datasets, using the
Levenberg-Marquardt method.
Each fit has its own independent variable, data, noise, initial guess,
bounds and Gaussian priors, and the noise of neighboring data points may be
correlated, as it is in resampled data, which makes the fit generalized least
squares.
The fits run in parallel on every CPU core, roughly half a million small
fits per second on a 48-thread machine.

Models are short Numba functions which compute the model and its Jacobian,
compiled with the :func:`ndoptimize.model` decorator, which also names their
parameters.
Some common ones are provided in :mod:`ndoptimize.models`, and
:func:`ndoptimize.evaluate` evaluates a model for many sets of parameters at
once, such as the best-fit ones.


Installation
============
Expand Down
14 changes: 14 additions & 0 deletions ndoptimize/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
"""
Numba-accelerated minimization of objective functions.
"""

from ._model import Model, model
from . import models
from ._least_squares import LeastSquaresResult, least_squares
from ._evaluate import evaluate

__all__ = [
"Model",
"model",
"models",
"LeastSquaresResult",
"least_squares",
"evaluate",
]
Loading
Loading