Skip to content

Commit

Permalink
Merge pull request #13 from cometbeetle/pint_extensions
Browse files Browse the repository at this point in the history
Add UncertaintyQuantity class for use with Pint
  • Loading branch information
varchasgopalaswamy authored Nov 15, 2024
2 parents 5243a5a + 958542a commit 1d8274a
Show file tree
Hide file tree
Showing 16 changed files with 298 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.6
rev: v0.5.5
hooks:
#Run the formatter.
- id: ruff-format
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ straightforward and automatic using auto-differentiation.

- [x] Scalars
- [x] Arrays, with support for most NumPy ufuncs and functions
- [x] Integration with [Pint](https://pint.readthedocs.io/en/stable/user/defining-quantities.html) Quantity objects
- [ ] Pandas Extension Type (see [here](https://pandas.pydata.org/docs/reference/api/pandas.api.extensions.ExtensionDtype.html))

## Prerequisites
Expand All @@ -29,6 +30,18 @@ To install, simply run:
pip install auto_uncertainties
```

## Build Documentation

To build the documentation locally, clone the repository, create a virtual Python environment
(if desired), and run the following commands within the repository directory:

```bash
pip install auto_uncertainties[docs]
sphinx-build docs/source docs/build
```

Once built, the docs can be found under the `docs/build` subdirectory.

## Basic Usage

* Creating a scalar `Uncertainty` variable is relatively simple:
Expand Down
8 changes: 6 additions & 2 deletions auto_uncertainties/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import annotations
# from __future__ import annotations

__private__ = ["util"]
__protected__ = ["numpy"]
__protected__ = ["numpy", "pint"]

import lazy_loader


Expand All @@ -11,8 +12,10 @@
"DowncastError",
"DowncastWarning",
"NegativeStdDevError",
"SType",
"ScalarDisplay",
"ScalarUncertainty",
"UType",
"Uncertainty",
"UncertaintyArray",
"UncertaintyDtype",
Expand All @@ -23,6 +26,7 @@
"nominal_values",
"numpy",
"pandas",
"pint",
"set_compare_error",
"set_display_rounding",
"set_downcast_error",
Expand Down
6 changes: 6 additions & 0 deletions auto_uncertainties/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ from . import display_format
from . import exceptions
from . import numpy
from . import pandas
from . import pint
from . import uncertainty
from . import util

Expand All @@ -22,7 +23,9 @@ from .pandas import (
unc_dtype,
)
from .uncertainty import (
SType,
ScalarUncertainty,
UType,
Uncertainty,
VectorUncertainty,
nominal_values,
Expand All @@ -36,8 +39,10 @@ __all__ = [
"DowncastError",
"DowncastWarning",
"NegativeStdDevError",
"SType",
"ScalarDisplay",
"ScalarUncertainty",
"UType",
"Uncertainty",
"UncertaintyArray",
"UncertaintyDtype",
Expand All @@ -48,6 +53,7 @@ __all__ = [
"nominal_values",
"numpy",
"pandas",
"pint",
"set_compare_error",
"set_display_rounding",
"set_downcast_error",
Expand Down
11 changes: 11 additions & 0 deletions auto_uncertainties/pint/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import lazy_loader


__getattr__, __dir__, __all__ = lazy_loader.attach_stub(__name__, __file__)

__all__ = [
"UncertaintyQuantity",
"UncertaintyRegistry",
"UncertaintyUnit",
"extensions",
]
14 changes: 14 additions & 0 deletions auto_uncertainties/pint/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from . import extensions

from .extensions import (
UncertaintyQuantity,
UncertaintyRegistry,
UncertaintyUnit,
)

__all__ = [
"UncertaintyQuantity",
"UncertaintyRegistry",
"UncertaintyUnit",
"extensions",
]
147 changes: 147 additions & 0 deletions auto_uncertainties/pint/extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
"""
This module contains classes for use with `pint` registries.
For usage with Pint, see `here <https://pint.readthedocs.io/en/stable/advanced/custom-registry-class.html>`_.
.. important::
The `pint` package must be installed in order to use these extensions.
.. warning::
To use `Uncertainty` objects with `pint` quantities, always make sure to use
the `UncertaintyQuantity` extension. Simply passing an `Uncertainty` object
into `Quantity()` will *not* automatically use `UncertaintyQuantity`, and may cause
problems.
A small exception to this is when the `UncertaintyRegistry` class is used. In that case,
it is supported to pass `Uncertainty` objects directly into `Quantity()`.
See the examples below for clarification.
.. code-block:: python
:caption: Supported use of UncertaintyQuantity
>>> from auto_uncertainties import Uncertainty
>>> from auto_uncertainties.pint import UncertaintyQuantity
# Units will be preserved when accessing the 'value' and 'error' attributes
>>> x = Uncertainty(1.0, 0.5)
>>> q = UncertaintyQuantity(x, 'radian')
>>> q.value
<Quantity(1.0, 'radian')>
>>> q.error
<Quantity(0.5, 'radian')>
.. code-block:: python
:caption: Unsupported use example
>>> from auto_uncertainties import Uncertainty
>>> from pint import Quantity
# Units are NOT automatically preserved when accessing the 'value' and 'error' attributes
>>> x = Uncertainty(1.0, 0.5)
>>> q = Quantity(x, 'radian')
>>> q.value
1.0
>>> q.error
0.5
.. code-block:: python
:caption: Supported use with the custom unit registry
>>> from auto_uncertainties import Uncertainty
>>> from auto_uncertainties.pint import UncertaintyQuantity, UncertaintyRegistry
# UncertaintyRegistry overrides the default Pint Quantity class
>>> reg = UncertaintyRegistry()
>>> x = Uncertainty(1.0, 0.5)
>>> q = reg.Quantity(x, 'radian')
>>> type(q)
<class 'pint.UncertaintyQuantity'>
>>> q.value
<Quantity(1.0, 'radian')>
>>> q.error
<Quantity(0.5, 'radian')>
"""

from __future__ import annotations

from typing import Generic

from auto_uncertainties import Uncertainty

try:
import pint
from pint.facets.plain import MagnitudeT, PlainQuantity
except ImportError as e:
msg = "Failed to load Pint extensions (Pint is not currently installed). Run 'pip install pint' to install it."
raise ImportError(msg) from e


__all__ = ["UncertaintyQuantity", "UncertaintyUnit", "UncertaintyRegistry"]


class UncertaintyQuantity(Generic[MagnitudeT], PlainQuantity[MagnitudeT]):
"""
Extension of `pint.facets.plain.PlainQuantity` to allow the `value` and `error`
attributes for an `Uncertainty` to be returned with their proper units.
"""

@property
def value(self):
"""
The central value of the `Uncertainty` object.
.. seealso:: * `auto_uncertainties.uncertainty.uncertainty_containers.Uncertainty.value`
"""
if isinstance(self._magnitude, Uncertainty):
return self._magnitude.value * self.units
else:
return self._magnitude * self.units

@property
def error(self):
"""
The uncertainty (error) value of the `Uncertainty` object.
.. seealso:: * `auto_uncertainties.uncertainty.uncertainty_containers.Uncertainty.error`
"""
if isinstance(self._magnitude, Uncertainty):
return self._magnitude.error * self.units
else:
return (0 * self._magnitude) * self.units

def plus_minus(self, err):
"""
Add an error to the `Uncertainty` object.
.. seealso:: * `auto_uncertainties.uncertainty.uncertainty_containers.Uncertainty.plus_minus`
"""
from auto_uncertainties import nominal_values, std_devs

my_value = nominal_values(self._magnitude) * self.units
my_err = std_devs(self._magnitude) * self.units

new_err = (my_err**2 + err**2) ** 0.5

return Uncertainty.from_quantities(my_value, new_err)


class UncertaintyUnit(pint.UnitRegistry.Unit):
"""
Unit used for `UncertaintyRegistry`.
"""


class UncertaintyRegistry(
pint.registry.GenericUnitRegistry[UncertaintyQuantity, pint.Unit]
):
"""
Alternative `pint` unit registry where the default `~pint.Quantity` class is
`UncertaintyQuantity`.
"""

Quantity = UncertaintyQuantity
Unit = UncertaintyUnit
2 changes: 2 additions & 0 deletions auto_uncertainties/uncertainty/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
__getattr__, __dir__, __all__ = lazy_loader.attach_stub(__name__, __file__)

__all__ = [
"SType",
"ScalarUncertainty",
"UType",
"Uncertainty",
"VectorUncertainty",
"nominal_values",
Expand Down
4 changes: 4 additions & 0 deletions auto_uncertainties/uncertainty/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from . import uncertainty_containers

from .uncertainty_containers import (
SType,
ScalarUncertainty,
UType,
Uncertainty,
VectorUncertainty,
nominal_values,
Expand All @@ -11,7 +13,9 @@ from .uncertainty_containers import (
)

__all__ = [
"SType",
"ScalarUncertainty",
"UType",
"Uncertainty",
"VectorUncertainty",
"nominal_values",
Expand Down
Loading

0 comments on commit 1d8274a

Please sign in to comment.