diff --git a/README.md b/README.md index 53f08fd..4aaf609 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ 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 @@ -21,7 +20,6 @@ For array support: * `jaxlib` * `numpy` - ## Installation To install, simply run: diff --git a/auto_uncertainties/__init__.py b/auto_uncertainties/__init__.py index 67c0f0e..2a03352 100644 --- a/auto_uncertainties/__init__.py +++ b/auto_uncertainties/__init__.py @@ -1,8 +1,7 @@ -# from __future__ import annotations +from __future__ import annotations __private__ = ["util"] -__protected__ = ["numpy", "pint"] - +__protected__ = ["numpy"] import lazy_loader @@ -26,7 +25,6 @@ "nominal_values", "numpy", "pandas", - "pint", "set_compare_error", "set_display_rounding", "set_downcast_error", diff --git a/auto_uncertainties/__init__.pyi b/auto_uncertainties/__init__.pyi index 847e180..674a191 100644 --- a/auto_uncertainties/__init__.pyi +++ b/auto_uncertainties/__init__.pyi @@ -2,7 +2,6 @@ from . import display_format from . import exceptions from . import numpy from . import pandas -from . import pint from . import uncertainty from . import util @@ -53,7 +52,6 @@ __all__ = [ "nominal_values", "numpy", "pandas", - "pint", "set_compare_error", "set_display_rounding", "set_downcast_error", diff --git a/auto_uncertainties/pint/__init__.py b/auto_uncertainties/pint/__init__.py deleted file mode 100644 index a3e3f1f..0000000 --- a/auto_uncertainties/pint/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -import lazy_loader - - -__getattr__, __dir__, __all__ = lazy_loader.attach_stub(__name__, __file__) - -__all__ = [ - "UncertaintyQuantity", - "UncertaintyRegistry", - "UncertaintyUnit", - "extensions", -] diff --git a/auto_uncertainties/pint/__init__.pyi b/auto_uncertainties/pint/__init__.pyi deleted file mode 100644 index 5ce803d..0000000 --- a/auto_uncertainties/pint/__init__.pyi +++ /dev/null @@ -1,14 +0,0 @@ -from . import extensions - -from .extensions import ( - UncertaintyQuantity, - UncertaintyRegistry, - UncertaintyUnit, -) - -__all__ = [ - "UncertaintyQuantity", - "UncertaintyRegistry", - "UncertaintyUnit", - "extensions", -] diff --git a/auto_uncertainties/pint/extensions.py b/auto_uncertainties/pint/extensions.py deleted file mode 100644 index ef05c3f..0000000 --- a/auto_uncertainties/pint/extensions.py +++ /dev/null @@ -1,147 +0,0 @@ -""" -This module contains classes for use with `pint` registries. - -For usage with Pint, see `here `_. - -.. 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 - - >>> q.error - - -.. 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) - - >>> q.value - - >>> q.error - - -""" - -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 diff --git a/auto_uncertainties/uncertainty/uncertainty_containers.py b/auto_uncertainties/uncertainty/uncertainty_containers.py index c7d69d2..4500b8c 100644 --- a/auto_uncertainties/uncertainty/uncertainty_containers.py +++ b/auto_uncertainties/uncertainty/uncertainty_containers.py @@ -1,11 +1,12 @@ # Based heavily on the implementation of pint's Quantity object from __future__ import annotations +from collections.abc import Sequence import copy import locale import math import operator -from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar +from typing import Any, Generic, Literal, TypeVar import warnings import joblib @@ -23,15 +24,6 @@ ignore_runtime_warnings, ) -if TYPE_CHECKING: - from collections.abc import Sequence - - from pint import Unit - from pint.facets.plain import PlainQuantity as Quantity - - from auto_uncertainties.pint.extensions import UncertaintyQuantity - - ERROR_ON_DOWNCAST = False COMPARE_RTOL = 1e-9 @@ -43,15 +35,15 @@ "set_compare_error", "nominal_values", "std_devs", - "UType", "SType", + "UType", ] -UType: type(TypeVar) = TypeVar("UType", np.ndarray, float, int) +UType = TypeVar("UType", np.ndarray, float, int) """`TypeVar` specifying the supported underlying types wrapped by `Uncertainty` objects.""" -SType: type(TypeVar) = TypeVar("SType", float, int) +SType = TypeVar("SType", float, int) """`TypeVar` specifying the scalar types used by `ScalarUncertainty` objects.""" @@ -264,65 +256,35 @@ def from_string(cls, string: str) -> Uncertainty: return cls(float(u1), float(u2)) @classmethod - def from_quantities( - cls, value: Quantity[UType] | UType, err: Quantity[UType] | UType - ) -> UncertaintyQuantity: + def from_quantities(cls, value, err) -> Uncertainty: """ - Create an `Uncertainty` object from one or more `pint.Quantity` objects. + Create an `Quantity` object with uncertainty from one or more `pint.Quantity` objects. - .. important:: The `pint` package must be installed for this to work. - - :param value: The central value of the `Uncertainty` object - :param err: The uncertainty value of the `Uncertainty` object + :param value: The central value(s) of the `Uncertainty` object + :param err: The uncertainty value(s) of the `Uncertainty` object .. note:: - * If **neither** argument is a `~pint.Quantity`, returns an + * If **neither** argument is a `~pint.Quantity`, returns a regular `Uncertainty` object. - * If **both** arguments are `~pint.Quantity` objects, returns an - `UncertaintyQuantity` with the same units as ``value`` (attempts - to convert ``err`` to ``value.units``). + * If **both** arguments are `~pint.Quantity` objects, returns a + `~pint.Quantity` (wrapped `Uncertainty`) with the same units as + ``value`` (attempts to convert ``err`` to ``value.units``). * If **only the** ``value`` argument is a `~pint.Quantity`, returns - an `UncertaintyQuantity` object with the same units as ``value``. + a `~pint.Quantity` (wrapped `Uncertainty`) object with the same units as ``value``. * If **only the** ``err`` argument is a `~pint.Quantity`, returns - an `UncertaintyQuantity` object with the same units as ``err``. + a `~pint.Quantity` (wrapped `Uncertainty`) object with the same units as ``err``. """ value_, err_, units = _check_units(value, err) inst = cls(value_, err_) - - from auto_uncertainties.pint.extensions import UncertaintyQuantity - if units is not None: - inst = UncertaintyQuantity(inst, units) - + inst *= units return inst - def as_quantity(self, unit: str | Unit | None = None) -> UncertaintyQuantity: - """ - Returns the current object as an `UncertaintyQuantity`. - - This is an alternative to calling `UncertaintyQuantity()` directly. - - .. attention:: - - This will **not** create a copy of the underlying `Uncertainty` object. - It simply returns the current object wrapped in `UncertaintyQuantity`. - Any changes to the underlying object (such as to the `numpy` arrays of a - `VectorUncertainty`) will be reflected in the `UncertaintyQuantity`, and vice versa. - - .. important:: The `pint` package must be installed for this to work. - - :param unit: The Pint unit to apply. Can be a string, or a `pint.Unit` object. (Optional) - """ - - from auto_uncertainties.pint.extensions import UncertaintyQuantity - - return UncertaintyQuantity(self, unit) - @classmethod def from_list(cls, u_list: Sequence[Uncertainty]): # pragma: no cover """ @@ -951,65 +913,49 @@ def _check_units(value, err) -> tuple[Any, Any, Any]: def nominal_values(x) -> UType: """Return the central value of an `Uncertainty` object if it is one, otherwise returns the object.""" - if hasattr(x, "units"): - x_used = x.m - x_units = x.units - else: - x_used = x - x_units = 1 # Is an Uncertainty - if hasattr(x_used, "_nom"): - ret_val = x_used.value + if hasattr(x, "_nom"): + return x.value else: - if np.ndim(x_used) > 0: + if np.ndim(x) > 0: try: - x2 = Uncertainty.from_sequence(x_used) + x2 = Uncertainty.from_sequence(x) except Exception: - ret_val = x_used + return x else: - ret_val = x2.value + return x2.value else: try: - x2 = Uncertainty(x_used) + x2 = Uncertainty(x) except Exception: - ret_val = x_used + return x else: if isinstance(x2, float): - ret_val = x2 + return x2 else: - ret_val = x2.value + return x2.value - return ret_val * x_units def std_devs(x) -> UType: """Return the uncertainty of an `Uncertainty` object if it is one, otherwise returns zero.""" # Is an Uncertainty - if hasattr(x, "units"): - x_used = x.m - x_units = x.units + if hasattr(x, "_err"): + return x.error else: - x_used = x - x_units = 1 - # Is an Uncertainty - if hasattr(x_used, "_nom"): - ret_val = x_used.error - else: - if np.ndim(x_used) > 0: + if np.ndim(x) > 0: try: - x2 = Uncertainty.from_sequence(x_used) + x2 = Uncertainty.from_sequence(x) except Exception: - ret_val = np.zeros_like(x_used) + return np.zeros_like(x) else: - ret_val = x2.error + return x2.error else: try: - x2 = Uncertainty(x_used) + x2 = Uncertainty(x) except Exception: - ret_val = 0 + return 0 else: if isinstance(x2, float): - ret_val = 0 + return 0 else: - ret_val = x2.error - - return ret_val * x_units + return x2.error diff --git a/docs/source/_autoapi_templates/python/data.rst b/docs/source/_autoapi_templates/python/data.rst index d0ab537..bd386bb 100644 --- a/docs/source/_autoapi_templates/python/data.rst +++ b/docs/source/_autoapi_templates/python/data.rst @@ -5,7 +5,7 @@ {% endif %} {% if obj.annotation and 'TypeAlias' in obj.annotation %}{% extends "python/typealias.rst" %} -{% elif obj.annotation and 'type(TypeVar)' in obj.annotation %}{% extends "python/typevar.rst" %}{% else %} +{% elif obj.annotation and 'TypeVar' in obj.annotation %}{% extends "python/typevar.rst" %}{% else %} .. py:{{ obj.type }}:: {% if is_own_page %}{{ obj.id }}{% else %}{{ obj.name }}{% endif %} {% if obj.annotation is not none %} diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 4429fd2..0760b86 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -15,12 +15,6 @@ Getting Started pip install auto_uncertainties -* The Pint extensions can be installed and enabled alongside AutoUncertainties using: - -.. code:: bash - - pip install auto_uncertainties[pint] - * The integration with `pandas` (still WIP) can be enabled by installing `pandas`, either separately or via: .. code:: bash diff --git a/docs/source/index.rst b/docs/source/index.rst index 051a662..86ce9f3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -33,10 +33,6 @@ Supported Features -
- - -
@@ -47,7 +43,6 @@ Usage ----- * See :doc:`basic_usage` -* See :doc:`Pint extensions ` Quick Reference diff --git a/docs/source/jinja_formatters.py b/docs/source/jinja_formatters.py index 7e3b38c..2d21de6 100644 --- a/docs/source/jinja_formatters.py +++ b/docs/source/jinja_formatters.py @@ -139,7 +139,7 @@ def _format_typevar(str_in: str) -> str: # Adjust this as desired if necessary replacements = (("np.", "~numpy."), ("npt.", "~numpy.typing.")) - # Import the TypeVar get the TypeVar constraints + # Import the TypeVar and get the TypeVar constraints mod, var = str_in.rsplit(".", 1) module = importlib.import_module(mod) constraints = getattr(module, var).__constraints__ diff --git a/pyproject.toml b/pyproject.toml index df48b32..df8866d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,6 @@ dynamic = ["version"] [project.optional-dependencies] CI = ["pytest", "pytest-cov", "hypothesis", "pylint", "pint"] pandas = ["pandas >= 1.5.1"] -pint = ["pint >= 0.24.4"] docs = [ "sphinx >= 4.1.2", "sphinx_rtd_theme >= 1.0.0", diff --git a/tests/test_uncertainty.py b/tests/test_uncertainty.py index 70f8380..5994ffd 100644 --- a/tests/test_uncertainty.py +++ b/tests/test_uncertainty.py @@ -26,7 +26,6 @@ set_downcast_error, ) from auto_uncertainties.numpy import HANDLED_FUNCTIONS, HANDLED_UFUNCS -from auto_uncertainties.pint import UncertaintyQuantity from auto_uncertainties.uncertainty.uncertainty_containers import ( ScalarUncertainty, Uncertainty, @@ -429,7 +428,7 @@ def test_init(): # Check proper handling of Quantity inputs # (further tests for from_quantities are handled in a separate function) from_quant = Uncertainty(Quantity(2, "radian"), Quantity(1, "radian")) - assert isinstance(from_quant, UncertaintyQuantity) + assert isinstance(from_quant, Quantity) assert isinstance(from_quant.m, Uncertainty) assert from_quant.units == Unit("radian")