Skip to content

Commit

Permalink
Remove setup.cfg (#43)
Browse files Browse the repository at this point in the history
- Remove setup.cfg
- Use ruff instead of flake8
- Fix propagate docstring
- Include examples in propagate docstring into doctest
  • Loading branch information
HDembinski authored Aug 16, 2023
1 parent e5983a7 commit 9a3c774
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 140 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ jobs:
include:
# version number must be string, otherwise 3.10 becomes 3.1
- os: windows-latest
python-version: "3.10"
python-version: "3.11"
- os: macos-latest
python-version: "3.6"
python-version: "3.8"
- os: ubuntu-latest
python-version: "pypy-3.7"
python-version: "pypy-3.8"
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install --upgrade pip
Expand Down
16 changes: 5 additions & 11 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ repos:
- id: file-contents-sorter
- id: trailing-whitespace

# Python linter (Flake8)
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
# Ruff linter, replacement for flake8, isort, pydocstyle
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.0.280'
hooks:
- id: flake8
- id: ruff
args: [--fix, --exit-non-zero-on-fix]

# Python formatting
- repo: https://github.com/psf/black
Expand All @@ -49,10 +50,3 @@ repos:
- id: mypy
args: [src]
pass_filenames: false

# doc string checking
- repo: https://github.com/PyCQA/pydocstyle
rev: 6.3.0
hooks:
- id: pydocstyle
files: src/jacobi/.*\.py
60 changes: 56 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,66 @@
[build-system]
requires = [
"setuptools>=42",
"setuptools_scm[toml]>=3.4",
]
requires = ["setuptools>=42", "setuptools_scm[toml]>=3.4"]
build-backend = "setuptools.build_meta"

[project]
name = "jacobi"
requires-python = ">=3.8"
description = "Compute numerical derivatives"
authors = [{ name = "Hans Dembinski" }, { email = "[email protected]" }]
dynamic = ["version"]
dependencies = ["numpy"]
readme = "README.rst"
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
"Operating System :: POSIX :: Linux",
"Operating System :: MacOS",
"Operating System :: Microsoft :: Windows",
"Topic :: Scientific/Engineering",
]
license = { file = "LICENSE" }

[project.urls]
repository = "https://github.com/hdembinski/jacobi"
documentation = "https://hdembinski.github.io/jacobi/"

[project.optional-dependencies]
test = ["pytest", "pytest-benchmark"]
doc = ["sphinx", "sphinx-rtd-theme", "ipykernel"]
plot = ["numdifftools", "matplotlib"]

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools_scm]
write_to = "src/jacobi/_version.py"

[tool.mypy]
ignore_missing_imports = true
allow_redefinition = true
no_implicit_optional = false

[tool.pytest.ini_options]
minversion = "6.0"
addopts = "--doctest-modules --strict-config --strict-markers -q -ra --ff"
testpaths = ["src/jacobi", "tests"]
filterwarnings = [
"error::numpy.VisibleDeprecationWarning",
"error::DeprecationWarning",
]

[tool.ruff]
select = ["E", "F", "D"]
extend-ignore = ["D203", "D212"]

[tool.ruff.pydocstyle]
convention = "numpy"

[tool.ruff.per-file-ignores]
"test_*.py" = ["B", "D"]
"tests/bench.py" = ["D"]
".ci/*.py" = ["D"]
"bench/*.py" = ["D"]
"doc/*.py" = ["D"]
54 changes: 0 additions & 54 deletions setup.cfg

This file was deleted.

8 changes: 0 additions & 8 deletions setup.py

This file was deleted.

4 changes: 2 additions & 2 deletions src/jacobi/_jacobi.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ def jacobi(
Whether to compute central (0), forward (1) or backward derivatives (-1).
The default (None) uses auto-detection.
mask : array or None, optional
If `x` is an array and `mask` is not None, compute the Jacobi matrix only for the
part of the array selected by the mask.
If `x` is an array and `mask` is not None, compute the Jacobi matrix only for
the part of the array selected by the mask.
rtol : float, optional
Relative tolerance for the derivative. The algorithm stops when this relative
tolerance is reached. If 0 (the default), the algorithm iterates until the
Expand Down
100 changes: 44 additions & 56 deletions src/jacobi/_propagate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ def propagate(
Parameters
----------
fn: callable
Function with the signature `fn(x, *args)`, where `x` is a number or a sequence
of numbers and `*args` are optional auxiliary arguments. The function must
return a number or a sequence of numbers (ideally as a numpy array). The
length of `x` can differ from the output sequence. Error propagation is only
performed with respect to `x`, the auxiliary arguments are ignored.
Function with the signature `fn(x, [y, ...])`, where `x` is a number or a
sequence of numbers, likewise if other arguments are present they must have the
same format. The function must return a number or a sequence of numbers (ideally
as a numpy array). The length of `x` can differ from the output sequence. The
function should accept more than one argument only if there are no
correlations between these arguments. See example below for use cases.
x: float or array-like with shape (N,)
Input vector. An array-like is converted before passing it to the callable.
cov: float or array-like with shape (N,) or shape(N, N)
Expand All @@ -53,63 +54,50 @@ def propagate(
-----
For callables `fn` which perform only element-wise computation, the jacobian is
a diagonal matrix. This special case is detected and the computation optimised,
although can further speed up the computation by passing the argumet `diagonal=True`.
although can further speed up the computation by passing the argument
`diagonal=True`.
In this special case, error propagation works correctly even if the output of `fn`
is NaN for some inputs.
Examples
--------
General error propagation maps input vectors to output vectors::
def fn(x):
return x ** 2 + 1
x = [1, 2]
xcov = [[3, 1],
[1, 4]]
y, ycov = propagate(fn, x, xcov)
In the previous example, the function y = fn(x) treats all x values independently,
so the Jacobian computed from fn(x) has zero off-diagonal entries. In this case,
one can speed up the calculation significantly with a special keyword::
# same result as before, but faster and uses much less memory
y, ycov = propagate(fn, x, xcov, diagonal=True)
If the function accepts several arguments, their uncertainties are treated as
uncorrelated::
def fn(x, y):
return x + y
x = 1
y = 2
xcov = 2
ycov = 3
z, zcov = propagate(fn, x, xcov, y, ycov)
Functions that accept several correlated arguments must be wrapped::
def fn(x, y):
return x + y
x = 1
y = 2
sigma_x = 3
sigma_y = 4
rho_xy = 0.5
r = [x, y]
cov_xy = rho_xy * sigma_x * sigma_y
rcov = [[sigma_x ** 2, cov_xy], [cov_xy, sigma_y ** 2]]
def fn_wrapped(r):
return fn(r[0], r[1])
z, zcov = propagate(fn_wrapped, r, rcov)
General error propagation maps input vectors to output vectors.
>>> def fn(x):
... return x ** 2 + 1
>>> x = [1, 2]
>>> xcov = [[3, 1],
... [1, 4]]
>>> y, ycov = propagate(fn, x, xcov)
In the previous example, the function ``y = fn(x)`` treats all x values
independently and the Jacobian computed from ``fn(x)`` has zero off-diagonal
entries. In this case, one can speed up the calculation significantly with a special
keyword.
>>> y, ycov = propagate(fn, x, xcov, diagonal=True)
This produces the same result, but is faster and uses less memory. If the function
accepts several arguments, their uncertainties are treated as uncorrelated.
>>> def fn(x, y):
... return x + y
>>> x = 1
>>> y = 2
>>> xcov = 2
>>> ycov = 3
>>> z, zcov = propagate(fn, x, xcov, y, ycov)
Functions that accept several correlated arguments must be wrapped.
>>> def fn(x, y):
... return x + y
>>> rho_xy = 0.5
>>> cov_xy = rho_xy * (xcov * ycov) ** 0.5
>>> r = [x, y]
>>> rcov = [[xcov, cov_xy], [cov_xy, ycov]]
>>> z, zcov = propagate(lambda r: fn(r[0], r[1]), r, rcov)
See Also
--------
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit 9a3c774

Please sign in to comment.