diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..117d4883 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,17 @@ +codecov: + token: c2f0ce36-17ad-4668-bb1b-f1b72dedf3fc + comment: + after_n_builds: 9 + +coverage: + status: + project: + default: + target: auto + threshold: 2.0% + informational: true + patch: + default: + target: auto + threshold: 20% + informational: true diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..dc8e22cd --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,35 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # You can also specify other tool versions: + # nodejs: "20" + # rust: "1.70" + # golang: "1.20" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs + # builder: "dirhtml" + # Fail on all warnings to avoid broken references + # fail_on_warning: true + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - requirements: docs/requirements.txt diff --git a/CHANGES.rst b/CHANGES.rst index 9d11bd4f..e6dad1ac 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -97,7 +97,7 @@ Main changes: - 1.5.2: ``float_u``, ``array_u`` and ``matrix_u`` renamed ``ufloat``, ``uarray`` and ``umatrix``, for ease of typing. - 1.5: Added functions ``nominal_value`` and ``std_dev``, and - modules ``unumpy`` (additional support for NumPy_ arrays and + modules ``unumpy`` (additional support for NumPy arrays and matrices) and ``unumpy.ulinalg`` (generalization of some functions from ``numpy.linalg``). Memory footprint of arrays of numbers with uncertainties @@ -108,7 +108,7 @@ Main changes: ``unumpy.matrix_u``, with the added benefit of a shorter name. - 1.4.5: Added support for the standard ``pickle`` module. - 1.4.2: Added support for the standard ``copy`` module. -- 1.4: Added utilities for manipulating NumPy_ arrays of numbers with +- 1.4: Added utilities for manipulating NumPy arrays of numbers with uncertainties (``array_u``, ``nominal_values`` and ``std_devs``). - 1.3: Numbers with uncertainties are now constructed with ``num_with_uncert()``, which replaces ``NumberWithUncert()``. This @@ -136,4 +136,3 @@ Main changes: .. _PEP 8: http://www.python.org/dev/peps/pep-0008/ .. _code updater: http://uncertainties-python-package.readthedocs.io/en/latest/index.html#migration-from-version-1-to-version-2 .. _formatting: http://uncertainties-python-package.readthedocs.io/en/latest/user_guide.html#printing - diff --git a/doc/Makefile b/doc/Makefile index 7cf650e2..78567d17 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -10,69 +10,32 @@ PAPER = PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +PDFFILE = uncertainties.pdf -.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest +.PHONY: all html help clean latex pdf -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf _build/* - -pdf: latex - (cd _build/latex; $(MAKE) all-pdf) - -# The HTML needs pdf because it contains a link to the PDF version: -html: pdf +html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) _build/dirhtml - @echo - @echo "Build finished. The HTML pages are in _build/dirhtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: pdf - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp +all: html pdf + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + -cp _build/latex/$(PDFFILE) _build/html/. @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in _build/htmlhelp." + -cd _build && ln -s html uncertainties_doc && zip -pur uncertainties_doc.zip uncertainties_doc/* && mv uncertainties_doc.zip html && rm -f uncertainties_doc + @echo "Build finished. The HTML pages are in _build/html." -epub: pdf - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) _build/epub - @echo - @echo "Build finished; the EPUB documentation is in" \ - "_build/epub." +help: + @echo "Please use \`make ' where is one of" + @echo " all to make standalone HTML files with PDF and zipped HTML" + @echo " html to make standalone HTML files" + @echo " pdf to make PDF" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " clean to clean folders" -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in _build/qthelp, like this:" - @echo "# qcollectiongenerator _build/qthelp/uncertaintiesPythonpackage.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile _build/qthelp/uncertaintiesPythonpackage.qhc" +clean: + -rm -rf _build/* latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @@ -81,18 +44,5 @@ latex: @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes - @echo - @echo "The overview file is in _build/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in _build/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in _build/doctest/output.txt." +pdf: latex + (cd _build/latex; $(MAKE) all-pdf) diff --git a/doc/README.rst b/doc/README.rst deleted file mode 100644 index c2d863ac..00000000 --- a/doc/README.rst +++ /dev/null @@ -1,5 +0,0 @@ -The documentation can be created by running ``make`` (``make html``, ``make -pdf``, etc.). - -Creating the documentation requires `Sphinx -`_. \ No newline at end of file diff --git a/doc/_templates/indexsidebar.html b/doc/_templates/indexsidebar.html new file mode 100644 index 00000000..dfdfcf89 --- /dev/null +++ b/doc/_templates/indexsidebar.html @@ -0,0 +1,20 @@ +
Get uncertainties
+

Current version: {{ release }}

+ +

+ Installation:
+ pip install uncertainties + +

+ Development Version: + Github +

+ + +
Offline Documentation
+ +

+ uncertainties.pdf +
+ Zipped HTML and PDF +

diff --git a/doc/_templates/layout.html b/doc/_templates/layout.html index 97a22886..18644dcd 100644 --- a/doc/_templates/layout.html +++ b/doc/_templates/layout.html @@ -1,93 +1,23 @@ {% extends "!layout.html" %} {% block rootrellink %} - {{ toctree() }} -{% endblock %} - -{% block document %} -{{ super() }} -Fork me on GitHub +
  • [Home
  • +
  • [Installation & Credits
  • +
  • |User Guide
  • +
  • |Formatting
  • +
  • | Numpy Arrays
  • +
  • |Advanced Topics]
  •   +  +    {% endblock %} {% block relbar1 %} - -
    -uncertainties +
    + + + uncertainties logo + +
    {{ super() }} {% endblock %} - - -{#############################################################################} -{# Sidebar customization #) - -{# put the sidebar before the body #} -{% block sidebar1 %} - -{%- macro sidebar() %} - {%- if not embedded %}{% if not theme_nosidebar|tobool %} -
    -
    - {%- block sidebarlogo %} - {%- if logo %} - - {%- endif %} - {%- endblock %} - {%- block sidebartoc %} - {%- block sidebarglobaltoc %} -

    {{ _('Table of contents') }}

    - {{ toctree() }} - {%- endblock %} - {%- endblock %} - {%- block sidebarrel %} - {%- endblock %} - {%- block sidebarsourcelink %} - {%- if show_source and has_source and sourcename %} -

    {{ _('This Page') }}

    - - {%- endif %} - {%- endblock %} - {%- if customsidebar %} - {% include customsidebar %} - {%- endif %} - {%- if display_toc %} -

    {{ _('Section contents') }}

    - {{ toc }} - {%- endif %} - {%- block sidebarsearch %} - {%- if pagename != "search" %} - - - {%- endif %} - {%- endblock %} - {%- block copyright %} -

    Documentation license

    -Creative Commons License - {%- endblock %} -
    -
    - {%- endif %}{% endif %} -{%- endmacro %} - -{{ sidebar() }}{% endblock %} - - -{% block sidebar2 %}{% endblock %} - diff --git a/doc/conf.py b/doc/conf.py index 62b4675a..8eb5daf6 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -27,7 +27,7 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] +extensions = ['sphinx.ext.autodoc', 'sphinx_copybutton'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -39,11 +39,11 @@ #source_encoding = 'utf-8' # The master toctree document. -master_doc = 'index_TOC' +master_doc = 'index' # General information about the project. -project = u'uncertainties Python package' -copyright = u'2010–%d, Eric O. LEBIGOT (EOL)' % date.today().year +project = u'uncertainties' +copyright = f'2010–{date.today().year}, Eric O. LEBIGOT (EOL)' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -96,7 +96,11 @@ # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'sphinxdoc' +# html_theme = 'sphinxdoc' +html_theme = 'bizstyle' +# html_theme = 'cloud' +html_theme = 'python_docs_theme' +# html_theme = "pydata_sphinx_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -108,7 +112,7 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +html_title = "uncertainties" # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None @@ -120,7 +124,7 @@ # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -html_favicon = 'favicon.ico' +html_favicon = '_static/favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -136,7 +140,7 @@ #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -163,7 +167,7 @@ #html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'uncertaintiesdoc' +htmlhelp_basename = 'uncertainties' # -- Options for LaTeX output -------------------------------------------------- @@ -177,7 +181,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index_TOC', 'uncertaintiesPythonPackage.tex', u'uncertainties Python package Documentation', + ('index', 'uncertainties.tex', u'uncertainties Python package Documentation', u'Eric O. LEBIGOT (EOL)', 'manual'), ] diff --git a/doc/formatting.rst b/doc/formatting.rst new file mode 100644 index 00000000..27270ba1 --- /dev/null +++ b/doc/formatting.rst @@ -0,0 +1,224 @@ +.. index:: formatting Variables +.. _formatting guide: + +======================================== +Formatting Variables with uncertainties +======================================== + +.. index:: + printing + formatting + +Printing +======== + +.. Overview: + +Numbers with uncertainties can be printed conveniently: + +>>> print(x) +0.200+/-0.010 + +The resulting form can generally be parsed back with +:func:`ufloat_fromstr` (except for the LaTeX form). + +.. Precision matching: + +The nominal value and the uncertainty always have the **same +precision**: this makes it easier to compare them. + +Standard formats +---------------- + +.. Formatting method: + +More **control over the format** can be obtained (in Python 2.6+) +through the usual :func:`format` method of strings: + +>>> print('Result = {:10.2f}'.format(x)) +Result = 0.20+/- 0.01 + + +.. Legacy formats and base syntax of the format specification: + +**All the float format specifications** are accepted, except those +with the ``n`` format type. In particular, a fill character, an +alignment option, a sign or zero option, a width, or the ``%`` format +type are all supported. + +The usual **float formats with a precision** retain their original +meaning (e.g. ``.2e`` uses two digits after the decimal point): code +that works with floats produces similar results when running with +numbers with uncertainties. + +Precision control +----------------- + +.. Precision control: + +It is possible to **control the number of significant digits of the +uncertainty** by adding the precision modifier ``u`` after the +precision (and before any valid float format type like ``f``, ``e``, +the empty format type, etc.): + +>>> print('1 significant digit on the uncertainty: {:.1u}'.format(x)) +1 significant digit on the uncertainty: 0.20+/-0.01 +>>> print('3 significant digits on the uncertainty: {:.3u}'.format(x)) +3 significant digits on the uncertainty: 0.2000+/-0.0100 +>>> print('1 significant digit, exponent notation: {:.1ue}'.format(x)) +1 significant digit, exponent notation: (2.0+/-0.1)e-01 +>>> print('1 significant digit, percentage: {:.1u%}'.format(x)) +1 significant digit, percentage: (20+/-1)% + +When :mod:`uncertainties` must **choose the number of significant +digits on the uncertainty**, it uses the `Particle +Data Group +`_ rounding +rules (these rules keep the number of digits small, which is +convenient for reading numbers with uncertainties, and at the same +time prevent the uncertainty from being displayed with too few +digits): + +>>> print('Automatic number of digits on the uncertainty: {}'.format(x)) +Automatic number of digits on the uncertainty: 0.200+/-0.010 +>>> print(x) +0.200+/-0.010 + +Custom options +-------------- + +.. Options: + +:mod:`uncertainties` provides even more flexibility through custom +formatting options. They can be added at the end of the format string: + +- ``P`` for **pretty-printing**: + + >>> print('{:.2e}'.format(x)) + (2.00+/-0.10)e-01 + >>> print(u'{:.2eP}'.format(x)) + (2.00±0.10)×10⁻¹ + + The pretty-printing mode thus uses "±", "×" and superscript + exponents. + +- ``S`` for the **shorthand notation**: + + >>> print('{:+.1uS}'.format(x)) # Sign, 1 digit for the uncertainty, shorthand + +0.20(1) + + In this notation, the digits in parentheses represent the + uncertainty on the last digits of the nominal value. + +- ``L`` for a **LaTeX** output: + + >>> print(x*1e7) + (2.00+/-0.10)e+06 + >>> print('{:L}'.format(x*1e7)) # Automatic exponent form, LaTeX + \left(2.00 \pm 0.10\right) \times 10^{6} + +- ``p`` is for requiring that parentheses be always printed around the …±… part + (without enclosing any exponent or trailing "%", etc.). This can for instance + be useful so as to explicitly factor physical units: + + >>> print('{:p} kg'.format(x)) # Adds parentheses + (0.200+/-0.010) kg + >>> print("{:p} kg".format(x*1e7)) # No parentheses added (exponent) + (2.00+/-0.10)e+06 kg + +These custom formatting options **can be combined** (when meaningful). + +Details +------- + +.. Common exponent: + +A **common exponent** is automatically calculated if an exponent is +needed for the larger of the nominal value (in absolute value) and the +uncertainty (the rule is the same as for floats). The exponent is +generally **factored**, for increased legibility: + +>>> print(x*1e7) +(2.00+/-0.10)e+06 + +When a *format width* is used, the common exponent is not factored: + +>>> print('Result = {:10.1e}'.format(x*1e-10)) +Result = 2.0e-11+/- 0.1e-11 + +(Using a (minimal) width of 1 is thus a way of forcing exponents to not +be factored.) Thanks to this feature, each part (nominal value and +standard deviation) is correctly aligned across multiple lines, while the +relative magnitude of the error can still be readily estimated thanks to +the common exponent. + +.. Special cases: + +An uncertainty which is *exactly* **zero** is always formatted as an +integer: + +>>> print(ufloat(3.1415, 0)) +3.1415+/-0 +>>> print(ufloat(3.1415e10, 0)) +(3.1415+/-0)e+10 +>>> print(ufloat(3.1415, 0.0005)) +3.1415+/-0.0005 +>>> print('{:.2f}'.format(ufloat(3.14, 0.001))) +3.14+/-0.00 +>>> print('{:.2f}'.format(ufloat(3.14, 0.00))) +3.14+/-0 + +**All the digits** of a number with uncertainty are given in its +representation: + +>>> y = ufloat(1.23456789012345, 0.123456789) +>>> print(y) +1.23+/-0.12 +>>> print(repr(y)) +1.23456789012345+/-0.123456789 +>>> y +1.23456789012345+/-0.123456789 + + +Global formatting +----------------- + +It is sometimes useful to have a **consistent formatting** across +multiple parts of a program. Python's `string.Formatter class +`_ +allows one to do just that. Here is how it can be used to consistently +use the shorthand notation for numbers with uncertainties: + +.. code-block:: python + + class ShorthandFormatter(string.Formatter): + + def format_field(self, value, format_spec): + if isinstance(value, uncertainties.UFloat): + return value.format(format_spec+'S') # Shorthand option added + # Special formatting for other types can be added here (floats, etc.) + else: + # Usual formatting: + return super(ShorthandFormatter, self).format_field( + value, format_spec) + + frmtr = ShorthandFormatter() + + print(frmtr.format("Result = {0:.1u}", x)) # 1-digit uncertainty + +prints with the shorthand notation: ``Result = 0.20(1)``. + + +Customizing the pretty-print and LaTeX outputs +---------------------------------------------- + +The pretty print and LaTeX outputs themselves can be customized. + +For example, the pretty-print representation of numbers with uncertainty can +display multiplication with a centered dot (⋅) instead of the default symbol +(×), like in ``(2.00±0.10)⋅10⁻¹``; this is easily done through the global +setting ``uncertainties.core.MULT_SYMBOLS["pretty-print"] = "⋅"``. + +Beyond this multiplication symbol, the "±" symbol, the parentheses and the +exponent representations can also be customized globally. The details can be +found in the documentation of :func:`uncertainties.core.format_num`. diff --git a/doc/index.rst b/doc/index.rst index 104fe5cf..496c4d49 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -3,408 +3,59 @@ :keywords: error propagation, uncertainties, error calculations, Python, calculator, library, package -==================================== -Welcome to the uncertainties package -==================================== +Uncertainties +================= -The `uncertainties package`_ is a free, cross-platform program that -**transparently** handles calculations with **numbers with uncertainties** -(like 3.14±0.01). It can also yield the **derivatives** of any -expression. - -The :mod:`uncertainties` package **takes the pain and complexity out** -of uncertainty calculations. Error propagation is not to be feared -anymore! - -Calculations of results with uncertainties, or of derivatives, can be -performed either in an **interactive session** (as with a calculator), -or in **programs** written in the Python_ programming language. -Existing calculation code can **run with little or no change**. - -Whatever the complexity of a calculation, this package returns its -result with an uncertainty as predicted by linear `error propagation -theory`_. It automatically :ref:`calculates derivatives ` -and uses them for calculating uncertainties. Almost all uncertainty -calculations are performed **analytically**. - -**Correlations** between variables are automatically handled, which -sets this module apart from many existing error propagation codes. - -You may want to check the following related uncertainty calculation -Python packages to see if they better suit your needs: soerp_ -(higher-order approximations) and mcerp_ (Monte-Carlo approach). - -.. index:: calculator - -An easy-to-use calculator -========================= - -Calculations involving **numbers with uncertainties** can be performed -even without knowing anything about the Python_ programming language. -After `installing this package`_ and `invoking the Python interpreter`_, -calculations with **automatic error propagation** can be performed -**transparently** (i.e., through the usual syntax for mathematical -formulas): +The `uncertainties package`_ is an open source Python library for doing +calculations on numbers that have uncertainties (like 3.14±0.01) that are +common in many scientific fields. The calculations done with this package will +propagate the uncertainties to the result of mathematical calculations. +The :mod:`uncertainties` package takes the pain and +complexity out of uncertainty calculations and error propagation. Here is a +quick taste of how to use :mod:`uncertainties`: >>> from uncertainties import ufloat ->>> from uncertainties.umath import * # sin(), etc. ->>> x = ufloat(1, 0.1) # x = 1+/-0.1 ->>> print 2*x -2.00+/-0.20 ->>> sin(2*x) # In a Python shell, "print" is optional -0.9092974268256817+/-0.08322936730942848 - -Thus, existing calculation code designed for regular numbers can run -with numbers with uncertainties with :ref:`no or little modification -`. - -.. index:: correlations; simple example - -Another strength of this package is its correct handling of -**correlations**. For instance, the following quantity is exactly -zero even though :data:`x` has an uncertainty: - ->>> x-x -0.0+/-0 - -Many other error propagation codes return the incorrect value 0±0.1414… -because they wrongly assume that the two subtracted quantities are -*independent* random variables. - -**Arrays** of numbers with uncertainties are :ref:`transparently -handled ` too. - - -**Derivatives** are similarly very :ref:`easy to obtain `: - ->>> (2*x+1000).derivatives[x] -2.0 - -They are calculated with a :ref:`fast method `. - -Available documentation -======================= - -The :doc:`user_guide` details many of the features of this package. - -The part :doc:`numpy_guide` describes how arrays of numbers with -uncertainties can be created and used. - -The :doc:`tech_guide` gives advanced technical details. - -.. only:: html - - A :download:`PDF version <_build/latex/uncertaintiesPythonPackage.pdf>` - of the documentation is also available. - -Additional information is available through the pydoc_ command, which -gives access to many of the documentation strings included in the code. - -.. index:: installation - -.. _installing this package: - -Installation and download -========================= - -Supported Python versions -------------------------- - -The :mod:`uncertainties` package supports Python versions 3.8 and higher. -Earlier versions of Python are not tested. :mod:`uncertainties` may still -be compatible with them. Patches to restore compatibility with earlier versions -of Python may be considered if they do not compromise performance or -maintainability for the supported versions of Python. Versions of the -:mod:`uncertainties` package up through 3.1.7 supported Python 2.7 through -Python 3.12. - -Important note --------------- - -The installation commands below should be **run in a DOS or Unix -command shell** (*not* in a Python shell). - -Under Windows (version 7 and earlier), a command shell can be obtained -by running ``cmd.exe`` (through the Run… menu item from the Start -menu). Under Unix (Linux, Mac OS X,…), a Unix shell is available when -opening a terminal (in Mac OS X, the Terminal program is found in the -Utilities folder, which can be accessed through the Go menu in the -Finder). - -Automatic install or upgrade ----------------------------- - -One of the automatic installation or upgrade procedures below might work -on your system, if you have a Python package installer or use certain -Linux distributions. - -Under Unix, it may be necessary to prefix the commands below with -``sudo``, so that the installation program has **sufficient access -rights to the system**. - -If you use the `Anaconda distribution `_, -you can install the latest version with - -.. code-block:: sh - - conda install -c conda-forge uncertainties - -If you have `pip `_, you can try to install -the latest version with - -.. code-block:: sh - - pip install --upgrade uncertainties - -If you have setuptools_, you can try to automatically install or -upgrade this package with - -.. code-block:: sh - - easy_install --upgrade uncertainties - -The :mod:`uncertainties` package is also available for **Windows** -through the `Python(x,y)`_ distribution. It may also be included in -Christoph Gohlke's Base distribution of `scientific Python packages`_. - -**Mac OS X** users who use the `MacPorts package manager -`_ can install :mod:`uncertainties` with -``sudo port install py**-uncertainties``, and upgrade it with ``sudo -port upgrade py**-uncertainties`` where ``**`` represents the desired -Python version (``27``, ``33``, etc.). - -The :mod:`uncertainties` package is also available through the -following **Linux** distributions and software platforms: `Ubuntu -`_, `Fedora -`_, -`openSUSE -`_, -`Debian -`_ -and `Maemo `_. - - -Manual download and install ---------------------------- - -Alternatively, you can simply download_ the package archive from the -Python Package Index (PyPI) and unpack it. The package can then be -installed by **going into the unpacked directory** -(:file:`uncertainties-…`), and running the provided :file:`setup.py` -program with - -.. code-block:: sh - - python setup.py install - -(where the default ``python`` interpreter must generally be replaced -by the version of Python for which the package should be installed: -``python3``, ``python3.3``, etc.). - -For an installation with Python 2.6+ in the *user* Python library -(no additional access rights needed): - -.. code-block:: sh - - python setup.py install --user - -For an installation in a custom directory :file:`my_directory`: - -.. code-block:: sh - - python setup.py install --install-lib my_directory - -If additional access rights are needed (Unix): - -.. code-block:: sh - - sudo python setup.py install - -You can also simply **move** the :file:`uncertainties-py*` directory -that corresponds best to your version of Python to a location that -Python can import from (directory in which scripts using -:mod:`uncertainties` are run, etc.); the chosen -:file:`uncertainties-py*` directory should then be renamed -:file:`uncertainties`. Python 3 users should then run ``2to3 -w .`` -from inside this directory so as to automatically adapt the code to -Python 3. - -Source code ------------ - -The latest, bleeding-edge but working `code -`_ -and `documentation source -`_ are -available `on GitHub `_. -The :mod:`uncertainties` package is written in pure Python and has no -external dependency except for the future_ package -(the `NumPy`_ package is optional). It contains -about 7000 lines of code. 75 % of these lines are documentation -strings and comments. The remaining 25 % are split between unit tests -(15 % of the total) and the calculation code proper (10 % of the -total). :mod:`uncertainties` is thus a **lightweight, portable -package** with abundant documentation and tests. - - -What others say -=============== - -- "*Superb,*" "*wonderful,*" "*It's like magic.*" (`Joaquin Abian - `_) -- "*pretty amazing*" (`John Kitchin `_) -- "*An awesome python package*" (`Jason Moore - `_) -- "*Utterly brilliant*" (`Jeffrey Simpson - `_) -- "*An amazing time saver*" (`Paul Nakroshis - `_) -- "*Seems to be the gold standard for this kind of thing*" (`Peter Williams - `_) -- "*This package has a great interface and makes error propagation - something to stop fearing.*" (`Dr Dawes - `_) -- "*uncertainties makes error propagation dead simple.*" (`enrico - documentation `_) -- "*many inspiring ideas*" (`Abraham Lee - `_) -- "*Those of us working with experimental data or simulation results - will appreciate this.*" (`Konrad Hinsen - `_) -- "*PyPI\'s uncertainties rocks!*" (`Siegfried Gevatter - `_) -- "*A very cool Python module*" (`Ram Rachum - `_) -- "*Holy f\*\*\* this would have saved me so much f\*\*\*ing time last - semester*." (`reddit - `_) - - -Future developments -=================== - -Planned future developments include (starting from the most requested -ones): - -- handling of complex numbers with uncertainties; - -- increased support for `NumPy`_: Fourier Transform with - uncertainties, automatic wrapping of functions that accept or - produce arrays, standard deviation of arrays, more convenient matrix - creation, new linear algebra methods (eigenvalue and QR - decompositions, determinant,…), input of arrays with uncertainties - as strings (like in NumPy),…; - -- `JSON `_ support; - -- addition of :attr:`real` and :attr:`imag` attributes, for increased - compatibility with existing code (Python numbers have these attributes); - -- addition of new functions from the :mod:`math` module; - -- fitting routines that conveniently handle data with uncertainties; - -- a re-correlate function that puts correlations back between data - that was saved in separate files; - -- support for multi-precision numbers with uncertainties. - -**Call for contributions**: I got multiple requests for complex -numbers with uncertainties, Fourier Transform support, and the -automatic wrapping of functions that accept or produce arrays. Please -contact me if you are interested in contributing. Patches are -welcome. They must have a high standard of legibility and quality in -order to be accepted (otherwise it is always possible to create a new -Python package by branching off this one, and I would still be happy -to help with the effort). - -**Please support the continued development of this program** by -`donating $10`_ or more through PayPal (no PayPal account -necessary). I love modern board games, so this will go towards giving -my friends and I some special gaming time! - -.. index:: support - -Contact -======= - -**Feature requests, bug reports, or feedback are much welcome.** They -can be sent_ to the creator of :mod:`uncertainties`, `Eric O. LEBIGOT -(EOL)`_. - -.. figure:: _static/eol.* - :height: 64 - :width: 64 - :target: http://linkedin.com/pub/eric-lebigot/22/293/277 - :align: center - :alt: Eric O. LEBIGOT (EOL) - -How to cite this package -======================== - -If you use this package for a publication (in a journal, on the web, -etc.), please cite it by including as much information as possible -from the following: *Uncertainties: a Python package for calculations -with uncertainties*, Eric O. LEBIGOT. Adding the version -number is optional. - - -Acknowledgments -=============== - -The author wishes to thank all the people who made generous -`donations`_: they help keep this project alive by providing positive -feedback. - -I greatly appreciate having gotten key technical input from Arnaud Delobelle, -Pierre Cladé, and Sebastian Walter. Patches by Pierre Cladé, Tim Head, José -Sabater Montes, Martijn Pieters, Ram Rachum, Christoph Deil, Gabi Davar, Roman -Yurchak and Paul Romano are gratefully acknowledged. - -I would also like to thank users who contributed with feedback and -suggestions, which greatly helped improve this program: Joaquin Abian, -Jason Moore, Martin Lutz, Víctor Terrón, Matt Newville, Matthew Peel, -Don Peterson, Mika Pflueger, Albert Puig, Abraham Lee, Arian Sanusi, -Martin Laloux, Jonathan Whitmore, Federico Vaggi, Marco A. Ferra, -Hernan Grecco, David Zwicker, James Hester, Andrew Nelson, and many others. - -I am grateful to the Anaconda, macOS and Linux distribution maintainers -of this package (Jonathan Stickel, David Paleino, Federico Ceratto, -Roberto Colistete Jr, Filipe Pires Alvarenga Fernandes, and Felix Yan) -and also to Gabi Davar and Pierre Raybaut for including it in -`Python(x,y)`_ and to Christoph Gohlke for including it in his Base -distribution of `scientific Python packages`_ for Windows. - +>>> x = ufloat(2, 0.1) # x = 2+/-0.1 +>>> y = ufloat(3, 0.2) # y = 3+/-0.2 +>>> print(2*x) +4.00+/-0.20 +>>> print(x+y) +5.00+/-0.22 +>>> print(x*y) +6.0+/-0.5 + +The :mod:`uncertainties` library calculates uncertainties using linear `error +propagation theory`_ by automatically :ref:`calculating derivatives +` and analytically propagating these to the results. Correlations +between variables are automatically handled. This library can also yield the +derivatives of any expression with respect to the variables that have uncertain +values. For other approaches, see soerp_ (using higher-order terms) and mcerp_ +(using a Monte-Carlo approach). + +The `source code`_ for the uncertainties package is licensed under the `Revised +BSD License`_. This documentation is licensed under the `CC-SA-3 License`_. + +.. _uncertainties package: https://pypi.python.org/pypi/uncertainties/ +.. _error propagation theory: https://en.wikipedia.org/wiki/Propagation_of_uncertainty +.. _soerp: https://pypi.python.org/pypi/soerp +.. _mcerp: https://pypi.python.org/pypi/mcer +.. _Revised BSD License: https://opensource.org/licenses/BSD-3-Clause +.. _CC-SA-3 License: https://creativecommons.org/licenses/by-sa/3.0 +.. _source code: https://github.com/lmfit/uncertainties/ +.. _version history: https://pypi.python.org/pypi/uncertainties#version-history -.. index:: license +.. _Pint: https://pypi.python.org/pypi/Pint/ +.. _future: https://pypi.org/project/future/ -License -======= -This software is released under a **dual license**; one of the -following options can be chosen: +Table of Contents +================= -1. The `Revised BSD License`_ (© 2010–2021, Eric O. LEBIGOT [EOL]). -2. Any other license, as long as it is obtained from the creator of - this package. +.. toctree:: + :maxdepth: 2 -.. _Python: http://python.org/ -.. _Python(x,y): https://python-xy.github.io/ -.. _scientific Python packages: http://www.lfd.uci.edu/~gohlke/pythonlibs/ -.. _error propagation theory: http://en.wikipedia.org/wiki/Propagation_of_uncertainty -.. _invoking the Python interpreter: http://docs.python.org/tutorial/interpreter.html -.. _setuptools: http://pypi.python.org/pypi/setuptools -.. _download: http://pypi.python.org/pypi/uncertainties/#downloads -.. _donations: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4TK7KNDTEDT4S -.. _Eric O. LEBIGOT (EOL): http://linkedin.com/pub/eric-lebigot/22/293/277 -.. _sent: mailto:eric.lebigot@normalesup.org -.. _Revised BSD License: http://opensource.org/licenses/BSD-3-Clause -.. _uncertainties package: http://pypi.python.org/pypi/uncertainties/ -.. _pydoc: http://docs.python.org/library/pydoc.html -.. _NumPy: http://numpy.scipy.org/ -.. _donating $10: donations_ -.. _version history: https://pypi.python.org/pypi/uncertainties#version-history -.. _soerp: https://pypi.python.org/pypi/soerp -.. _mcerp: https://pypi.python.org/pypi/mcerp -.. _Pint: https://pypi.python.org/pypi/Pint/ -.. _future: https://pypi.org/project/future/ + install + user_guide + numpy_guide + formatting + tech_guide diff --git a/doc/index_TOC.rst b/doc/index_TOC.rst deleted file mode 100644 index f23b6c93..00000000 --- a/doc/index_TOC.rst +++ /dev/null @@ -1,11 +0,0 @@ -Table of Contents -================= - -.. toctree:: - :maxdepth: 1 - - Overview - user_guide - numpy_guide - tech_guide - diff --git a/doc/install.rst b/doc/install.rst new file mode 100644 index 00000000..44ad8296 --- /dev/null +++ b/doc/install.rst @@ -0,0 +1,159 @@ +.. index:: installation +.. index:: credits +.. _installation: + +==================================== +Installation and Credits +==================================== + +Download and Installation +========================= + +The :mod:`uncertainties` package supports Python versions 3.8 and higher. +Earlier versions of Python are not tested, but may still work. Development +version of Python (currently, 3.13) are likely to work, but are not regularly +tested. + +To install :mod:`uncertainties`, use: + +.. code-block:: sh + + pip install uncertainties + +You can upgrade from an older version of :mod:`uncertainties` with: + +.. code-block:: sh + + pip install --upgrade uncertainties + + +Other packaging systems such as `Anaconda `_, +`MacPorts `_, or Linux package manager may also +maintain packages for :mod:`uncertainties`, so that you may also be able to +install using something like + +.. code-block:: sh + + conda install -c conda-forge uncertainties + +.. code-block:: sh + + sudo port install py**-uncertainties + +or + +.. code-block:: sh + + sudo apt get python-uncertainties + +depending on your platform and installation of Python. For all installations +of Python, using `pip` should work and is therefore recommended. + + +Source code and Development Version +===================================== + +.. _download: https://pypi.python.org/pypi/uncertainties/#files +.. _GitHub releases: https://github.com/lmfit/uncertainties/releases +.. _NumPy: http://numpy.scipy.org/ + +You can `download`_ the latest source package archive from the Python Package +Index (PyPI) and unpack it, or from the `GitHub releases`_ page. This package +can be unpacked using `unzip`, `tar xf` , or other similar utilities, and then +installed with + +.. code-block:: sh + + python -m pip install . + +To work with the development version, use `git` to fork or clone the code: + +.. code-block:: sh + + git clone git@github.com:lmfit/uncertainties.git + +The :mod:`uncertainties` package is written in pure Python and has no external +dependencies. If available (and recommended), the `NumPy`_ package can be +used. Running the test suite requires `pytest` and `pytest_cov`, and building +these docs requires `sphinx`. To install these optional packages, use one of: + +.. code-block:: sh + + pip install ".[arrays]" # to install numpy + pip install ".[test]" # to enable running the tests + pip install ".[doc]" # to enable building the docs + pip install ".[all]" # to enable all of these options + +Getting Help +================= + +.. _GitHub Discussions: https://github.com/lmfit/uncertainties/discussions +.. _GitHub Issues: https://github.com/lmfit/uncertainties/issues +.. _lmfit GitHub organization: https://github.com/lmfit/ + +If you have questions about :mod:`uncertainties` or run into trouble, use the +`GitHub Discussions`_ page. For bug reports, use the `GitHub Issues`_ pages. + + +Credits +================ + +.. _Eric O. LEBIGOT (EOL): http://linkedin.com/pub/eric-lebigot/22/293/277 + +The :mod:`uncertainties` package was written and developed by `Eric O. LEBIGOT +(EOL)`_. EOL also maintained the package until 2024, when the GitHub project +was moved to the `lmfit GitHub organization`_ to allow more sustainable +development and maintenance. Current members of the devlopment and +maintenance team include `Andrew G Savage `_, +`Justin Gerber `_, +`Eric O Legibot `_, +`Matt Newville `_, +and `Will Shanks `_. Contributions and suggestions +for development are welcome. + + +How to cite this package +======================== + +If you use this package for a publication, please cite it as *Uncertainties: a +Python package for calculations with uncertainties*, Eric O. LEBIGOT. A +version number can be added, but is optional. + + +Acknowledgments +=============== + +.. _Python(x,y): https://python-xy.github.io/ +.. _scientific Python packages: http://www.lfd.uci.edu/~gohlke/pythonlibs/ + +Eric O. LEBIGOT (EOL) thanks all the people who made generous donations: that +help to keep this project alive by providing positive feedback. + +EOL greatly appreciates having gotten key technical input from Arnaud Delobelle, +Pierre Cladé, and Sebastian Walter. Patches by Pierre Cladé, Tim Head, José +Sabater Montes, Martijn Pieters, Ram Rachum, Christoph Deil, Gabi Davar, Roman +Yurchak and Paul Romano are gratefully acknowledged. + +EOL also thanks users who contributed with feedback and +suggestions, which greatly helped improve this program: Joaquin Abian, +Jason Moore, Martin Lutz, Víctor Terrón, Matt Newville, Matthew Peel, +Don Peterson, Mika Pflueger, Albert Puig, Abraham Lee, Arian Sanusi, +Martin Laloux, Jonathan Whitmore, Federico Vaggi, Marco A. Ferra, +Hernan Grecco, David Zwicker, James Hester, Andrew Nelson, and many others. + +EOL is grateful to the Anaconda, macOS and Linux distribution maintainers +of this package (Jonathan Stickel, David Paleino, Federico Ceratto, +Roberto Colistete Jr, Filipe Pires Alvarenga Fernandes, and Felix Yan) +and also to Gabi Davar and Pierre Raybaut for including it in +`Python(x,y)`_ and to Christoph Gohlke for including it in his Base +distribution of `scientific Python packages`_ for Windows. + +.. index:: license + +License +======= + +.. _Revised BSD License: http://opensource.org/licenses/BSD-3-Clause + +This software is released under the `Revised BSD License`_ (© 2010–2024, +Eric O. LEBIGOT [EOL]). diff --git a/doc/make.bat b/doc/make.bat deleted file mode 100644 index d1cd1aea..00000000 --- a/doc/make.bat +++ /dev/null @@ -1,112 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -set SPHINXBUILD=sphinx-build -set ALLSPHINXOPTS=-d _build/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (_build\*) do rmdir /q /s %%i - del /q /s _build\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% _build/html - echo. - echo.Build finished. The HTML pages are in _build/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% _build/dirhtml - echo. - echo.Build finished. The HTML pages are in _build/dirhtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% _build/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% _build/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% _build/htmlhelp - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in _build/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% _build/qthelp - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in _build/qthelp, like this: - echo.^> qcollectiongenerator _build\qthelp\uncertaintiesPythonpackage.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile _build\qthelp\uncertaintiesPythonpackage.ghc - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% _build/latex - echo. - echo.Build finished; the LaTeX files are in _build/latex. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% _build/changes - echo. - echo.The overview file is in _build/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% _build/linkcheck - echo. - echo.Link check complete; look for any errors in the above output ^ -or in _build/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% _build/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in _build/doctest/output.txt. - goto end -) - -:end diff --git a/doc/numpy_guide.rst b/doc/numpy_guide.rst index 288b1901..f350765f 100644 --- a/doc/numpy_guide.rst +++ b/doc/numpy_guide.rst @@ -1,14 +1,38 @@ .. index: NumPy support -======================= -Uncertainties in arrays -======================= +=============================== +Uncertainties and numpy arrays +=============================== .. index:: unumpy +.. index:: arrays; simple use, matrices; simple use + +.. _simple_array_use: + +Arrays of uncertainties Variables +==================================== + +It is possible to put uncertainties Variable in NumPy_ arrays and +matrices: + +>>> arr = numpy.array([ufloat(1, 0.01), ufloat(2, 0.1)]) +>>> 2*arr +[2.0+/-0.02 4.0+/-0.2] +>>> print arr.sum() +3.00+/-0.10 + +Many common operations on NumPy arrays can be performed transparently +even when these arrays contain numbers with uncertainties. + The unumpy package ================== + +While :ref:`basic operations on arrays ` that +contain numbers with uncertainties can be performed without it, the +:mod:`unumpy` package is useful for more advanced uses. + This package contains: 1. utilities that help with the **creation and manipulation** of @@ -17,9 +41,6 @@ NumPy_ arrays and matrices of numbers with uncertainties; 2. **generalizations** of multiple NumPy functions so that they also work with arrays that contain numbers with uncertainties. -While :ref:`basic operations on arrays ` that -contain numbers with uncertainties can be performed without it, the -:mod:`unumpy` package is useful for more advanced uses. Operations on arrays (including their cosine, etc.) can thus be performed transparently. @@ -45,7 +66,7 @@ Arrays of numbers with uncertainties can be built from values and uncertainties: >>> arr = unumpy.uarray([1, 2], [0.01, 0.002]) ->>> print arr +>>> print(arr) [1.0+/-0.01 2.0+/-0.002] NumPy arrays of numbers with uncertainties can also be built directly @@ -79,7 +100,7 @@ work). This is why the :class:`unumpy.matrix` class is provided: both the inverse and the pseudo-inverse of a matrix can be calculated in the usual way: if :data:`mat` is a :class:`unumpy.matrix`, ->>> print mat.I +>>> print(mat.I) does calculate the inverse or pseudo-inverse of :data:`mat` with uncertainties. @@ -111,16 +132,14 @@ This module defines uncertainty-aware mathematical functions that generalize those from :mod:`uncertainties.umath` so that they work on NumPy arrays of numbers with uncertainties instead of just scalars: ->>> print unumpy.cos(arr) # Cosine of each array element +>>> print(unumpy.cos(arr)) # Cosine of each array element NumPy's function names are used, and not those from the :mod:`math` module (for instance, :func:`unumpy.arccos` is defined, like in NumPy, and is not named :func:`acos` like in the :mod:`math` module). The definition of the mathematical quantities calculated by these -functions is available in the documentation for -:mod:`uncertainties.umath` (which is accessible through :func:`help` -or ``pydoc``). +functions is available in the documentation for :mod:`uncertainties.umath`. .. index:: pair: testing and operations (in arrays); NaN @@ -165,14 +184,13 @@ Arrays of numbers with uncertainties can be directly :ref:`pickled `, saved to file and read from a file. Pickling has the advantage of preserving correlations between errors. -Storing instead arrays in **text format** loses correlations between -errors but has the advantage of being both computer- and -human-readable. This can be done through NumPy's :func:`savetxt` and -:func:`loadtxt`. +Storing arrays in **text format** loses correlations between errors but has the +advantage of being both computer- and human-readable. This can be done through +NumPy's :func:`savetxt` and :func:`loadtxt`. Writing the array to file can be done by asking NumPy to use the -*representation* of numbers with uncertainties (instead of the default -float conversion): +*representation* of numbers with uncertainties (instead of the default float +conversion): >>> numpy.savetxt('arr.txt', arr, fmt='%r') @@ -182,27 +200,18 @@ the array:: 1.0+/-0.01 2.0+/-0.002 -The file can then be read back by instructing NumPy to convert all the -columns with :func:`uncertainties.ufloat_fromstr`. The number -:data:`num_cols` of columns in the input file (1, in our example) must -be determined in advance, because NumPy requires a converter for each -column separately. For Python 2: - ->>> converters = dict.fromkeys(range(num_cols), uncertainties.ufloat_fromstr) - -For Python 3, since :func:`numpy.loadtxt` passes bytes to converters, -they must first be converted into a string: - ->>> converters = dict.fromkeys( - range(num_cols), - lambda col_bytes: uncertainties.ufloat_fromstr(col_bytes.decode("latin1"))) - -(Latin 1 appears to in fact be the encoding used in -:func:`numpy.savetxt` [as of NumPy 1.12]. This encoding seems -to be the one hardcoded in :func:`numpy.compat.asbytes`.) - -The array can then be loaded: - +The file can then be read back by instructing NumPy with :meth:`numpy.loadtxt`, +but for object arrays, this requires a converter function for each column +separately. We can use func:`uncertainties.ufloat_fromstr`, but +:meth:`numpy.loadtxt` passes bytes to converters, they must first be converted +into a string. In addition the number of maximum number of columns must be +known. An example of using all of this to unpack the data saved with +:meth:`numpy.savetxt` would be: + +>>> from uncertainties import ufloat_fromstr +>>> max_cols = 1 +>>> converters = {col: lambda dat: ufloat_fromstr(dat.decode("utf-8")) +.... for col in range(max_cols)} >>> arr = numpy.loadtxt('arr.txt', converters=converters, dtype=object) .. index:: linear algebra; additional functions, ulinalg diff --git a/doc/tech_guide.rst b/doc/tech_guide.rst index e6d949a4..f3dc5284 100644 --- a/doc/tech_guide.rst +++ b/doc/tech_guide.rst @@ -1,10 +1,35 @@ .. index:: technical details -=============== -Technical Guide -=============== +========================= +Advanced Topics +========================= +This page gives more in-depth technical description of the +:mod:`uncertainties` package. + + .. index:: api + +.. _api_funcs: + + +API: Application Programming Interface +============================================== + +.. module:: uncertainties + +The most common and important functions for creating uncertain +:class:`Variables` are :func:`ufloat` and :func:`ufloat_fromstr`. In addition, +the :func:`wrap` can be used to support the propagation of uncertainties with a +user-supplied function. + +.. autofunction:: ufloat + +.. autofunction:: ufloat_fromstr + +.. autoclass:: Variable + +.. autofunction:: wrap Testing whether an object is a number with uncertainty ------------------------------------------------------ @@ -15,6 +40,13 @@ uncertainty handled by this module is by checking whether ``isinstance(value, uncertainties.UFloat)``. + + +Special Technical Topics +============================================================ + + + .. index:: pickling .. index:: saving to file; number with uncertainty .. index:: reading from file; number with uncertainty @@ -103,7 +135,7 @@ since :data:`x` and :data:`y` are independent random variables that *almost* always give a different value (put differently, :data:`x`-:data:`y` is not equal to 0, as it can take many different values). Note that this is different -from the result of ``z = 3.14; t = 3.14; print z == t``, because +from the result of ``z = 3.14; t = 3.14; print(z == t)``, because :data:`x` and :data:`y` are *random variables*, not pure numbers. Similarly, @@ -162,9 +194,6 @@ that :data:`y` > :data:`x`. Linear propagation of uncertainties ----------------------------------- -Constraints on the uncertainties -================================ - This package calculates the standard deviation of mathematical expressions through the linear approximation of `error propagation theory`_. @@ -205,7 +234,7 @@ calculations are much slower than with approximation schemes. pair: uncertainty; NaN NaN uncertainty -=============== +---------------------- If linear `error propagation theory`_ cannot be applied, the functions defined by :mod:`uncertainties` internally use a `not-a-number value @@ -328,7 +357,7 @@ If the variable :data:`a` above is modified, the value of :data:`poly` is not modified, as is usual in Python: >>> a = 123 ->>> print poly +>>> print(poly) 46.0+/-0.4 # Still equal to x**2 + 42, not x**2 + 123 Random variables can, on the other hand, have their uncertainty @@ -336,7 +365,7 @@ updated on the fly, because quantities with uncertainties (like :data:`poly`) keep track of them: >>> x.std_dev = 0 ->>> print poly +>>> print(poly) 46+/-0 # Zero uncertainty, now As usual, Python keeps track of objects as long as they are used. @@ -345,7 +374,7 @@ Thus, redefining the value of :data:`x` does not change the fact that in :data:`x`: >>> x = 10000 ->>> print poly +>>> print(poly) 46+/-0 # Unchanged These mechanisms make quantities with uncertainties behave mostly like @@ -353,7 +382,6 @@ regular numbers, while providing a fully transparent way of handling correlations between quantities. - .. index:: number with uncertainty; classes, Variable class .. index:: AffineScalarFunc class @@ -372,8 +400,8 @@ classes: 2. a class for functions that depend on independent variables (:class:`AffineScalarFunc`, aliased as :class:`UFloat`). -Documentation for these classes is available in their Python -docstring, which can for instance displayed through pydoc_. +Additional documentation for these classes is available in their Python +docstring. The factory function :func:`ufloat` creates variables and thus returns a :class:`Variable` object: @@ -396,8 +424,6 @@ objects store all the variables they depend on: .. _automatic differentiation: http://en.wikipedia.org/wiki/Automatic_differentiation -.. _pydoc: http://docs.python.org/library/pydoc.html - .. _error propagation theory: http://en.wikipedia.org/wiki/Error_propagation .. _soerp: https://pypi.python.org/pypi/soerp diff --git a/doc/user_guide.rst b/doc/user_guide.rst index 44ab6385..4a3f307e 100644 --- a/doc/user_guide.rst +++ b/doc/user_guide.rst @@ -6,44 +6,58 @@ User Guide ========== -Basic setup +Basic usage =========== -Basic mathematical operations involving numbers with uncertainties -only require a simple import: +Basic mathematical operations involving numbers with uncertainties requires +importing the :func:`ufloat` function which creates a :class:`Variable`: +number with both a nominal value and an uncertainty. ->>> from uncertainties import ufloat + >>> from uncertainties import ufloat + >>> x = ufloat(2.7, 0.01) # a Variable with a value 2.7+/-0.01 -The :func:`ufloat` function creates numbers with uncertainties. Existing -calculation code can usually run with no or little modification and -automatically produce results with uncertainties. +The :mod:`uncertainties` module contains sub-modules for :ref:`advanced +mathematical functions `, and :doc:`arrays and +matrices `, which can be accessed as well. -.. The "import uncertainties" is put here because some examples requires - uncertainties to have been imported (and not only ufloat). +.. index:: + pair: number with uncertainty; creation -The :mod:`uncertainties` module contains other features, which can be -made accessible through +Creating Variables: numbers with uncertainties +================================================ ->>> import uncertainties +To create a number with uncertainties or *Variable*, use the :func:`ufloat` +function, which takes a *nominal value* (which can be interpreted as the most +likely value, or the mean or central value of the distribution of values), a +*standard error* (the standard deviation or :math:`1-\sigma` uncertainty), and +an optional *tag*: -The :mod:`uncertainties` package also contains sub-modules for -:ref:`advanced mathematical functions `, and -:doc:`arrays and matrices `. +>>> x = ufloat(2.7, 0.01) # x = 2.7+/-0.01 +>>> y = ufloat(4.5, 1.2, tag='y_variable') # x = 4..5+/-1.2 .. index:: - pair: number with uncertainty; creation + pair: nominal value; scalar + pair: uncertainty; scalar + +You can access the nominal value and standard deviation for any Variable with +the `nominal_value` and `std_dev` attributes: + +>>> print(x.nominal_value, x.std_dev) +2.7 0.01 -Creating numbers with uncertainties -=================================== -Numbers with uncertainties can be input either numerically, or through -one of many string representations, so that files containing numbers -with uncertainties can easily be parsed. Thus, x = 0.20±0.01 can be -expressed in many convenient ways, including: +Because these are fairly long to type, for convenience, `nominal_value` can be +abbreviated as `n` and `std_dev` as `s`: ->>> x = ufloat(0.20, 0.01) # x = 0.20+/-0.01 +>>> print(x.n, x.s) +2.7 0.01 + +uncertainties Variables can also be created from one of many string +representations. The following forms will all create Variables with the same +value: >>> from uncertainties import ufloat_fromstr +>>> x = ufloat(0.2, 0.01) >>> x = ufloat_fromstr("0.20+/-0.01") >>> x = ufloat_fromstr("(2+/-0.1)e-01") # Factored exponent >>> x = ufloat_fromstr("0.20(1)") # Short-hand notation @@ -51,85 +65,208 @@ expressed in many convenient ways, including: >>> x = ufloat_fromstr(u"0.20±0.01") # Pretty-print form >>> x = ufloat_fromstr("0.20") # Automatic uncertainty of +/-1 on last digit -Each number created this way is an **independent (random) variable** -(for details, see the :ref:`Technical Guide `). +More details on the :func:`ufloat` and :func:`ufloat_from_str` can be found in +:ref:`api_funcs`. -More information can be obtained with ``pydoc uncertainties.ufloat`` -and ``pydoc uncertainties.ufloat_fromstr`` ("20(1)×10\ :sup:`-2`\ " is -also recognized, etc.). +Basic math with uncertain Variables +========================================= +Uncertainties variables created in :func:`ufloat` or :func:`ufloat_fromstr` can +be used in basic mathematical calculations ('+', '-', '*', '/', '**') as with +other Python numbers and variables. -Basic math -========== +>>> t = ufloat(0.2, 0.01) +>>> double = 2.0*t +>>> print(double) +0.4+/-0.02 +>>> square = t**2 +>>> print(square) +0.040+/-0.004 -Calculations can be performed directly, as with regular real numbers: +When adding two Variables, the uncertainty in the result is the quadrature sum +(square-root of the sum of squares) of the uncertainties of the two Variables: ->>> square = x**2 ->>> print square -0.040+/-0.004 +>>> x = ufloat(20, 4) +>>> y = ufloat(12, 3) +>>> print(x+y) +32.0+/-5.0 + +We can check that error propagation when adding two independent variables +(using the abbreviation `.s` for the standard error): + +>>> from math import sqrt +>>> (x+y).s == sqrt(x.s**2 + y.s**2) +True + + +Multiplying two Variables will properly propagate those +uncertainties too: + +>>> print(x*y) +240.0+/-76.83749084919418 +>>> (x*y).s == (x*y).n * sqrt((x.s/x.n)**2 + (y.s/y.n)**2 ) +True + +But note that adding a Variable to itself does not add its uncertainties in +quadrature, but are simply scaled: + +>>> print(x+x) +40.0+/-8.0 +>>> print(3*x + 10) +70.0+/-12.0 + + +It is important to understand that calculations done with Variable know about +the correlation between the Variables. Variables created with :func:`ufloat` +(and :func:`ufloat_fromstr`) are completely uncorrelated with each other, but +are known to be completely correlated with themselves. This means that + + +>>> x = ufloat(5, 0.5) +>>> y = ufloat(5, 0.5) +>>> x - y +0.0+/-0.7071067811865476 +>>> x - x +0.0+/-0 + +For two *different* Variables, uncorrelated uncertainties will be propagated. +But when doing a calculation with a single Variable, the uncertainties are +correlated, and calculations will reflect that. .. index:: mathematical operation; on a scalar, umath .. _advanced math operations: -Mathematical operations -======================= +Mathematical operations with uncertain Variables +===================================================== -Besides being able to apply basic mathematical operations to numbers -with uncertainty, this package provides generalizations of **most of -the functions from the standard** :mod:`math` **module**. These -mathematical functions are found in the :mod:`uncertainties.umath` -module: +Besides being able to apply basic mathematical operations to uncertainties +Variables, this package provides generalized versions of 40 of the the +functions from the standard :mod:`math` *module*. These mathematical functions +are found in the :mod:`uncertainties.umath` module: ->>> from uncertainties.umath import * # Imports sin(), etc. ->>> sin(x**2) -0.03998933418663417+/-0.003996800426643912 + >>> from uncertainties.umath import sin, exp, sqrt + >>> x = ufloat(0.2, 0.01) + >>> sin(x) + 0.19866933079506122+/-0.009800665778412416 + >>> sin(x*x) + 0.03998933418663417+/-0.003996800426643912 + >>> exp(-x/3.0) + 0.9355069850316178+/-0.003118356616772059 + >>> sqrt(230*x + 3) + 7.0+/-0.16428571428571428 -The list of available mathematical functions can be obtained with the -``pydoc uncertainties.umath`` command. -.. index:: - pair: testing (scalar); NaN +The functions in the :mod:`uncertainties.umath` module include: -NaN testing ------------ + 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', + 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'erf', 'erfc', + 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', + 'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', + 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', + 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc' -NaN values can appear in a number with uncertainty. Care must be -taken with such values, as values like NaN±1, 1±NaN and NaN±NaN are by -definition *not* NaN, which is a float. -Testing whether a number with uncertainty has a **NaN nominal value** can -be done with the provided function ``uncertainties.umath.isnan()``, -which generalizes the standard ``math.isnan()``. +Comparison operators +==================== -Checking whether the *uncertainty* of ``x`` is NaN can be done directly -with the standard function: ``math.isnan(x.std_dev)`` (or equivalently -``math.isnan(x.s)``). +Comparison operators ('==', '!=', '>', '<', '>=', and '<=') for Variables with +uncertainties are somewhat complicated, and need special attention. As we +hinted at above, and will explore in more detail below and in the +:ref:`Technical Guide `, this relates to the correlation +between Variables. -.. index:: arrays; simple use, matrices; simple use -.. _simple_array_use: +Equality and inequality comparisons +------------------------------------ -Arrays of numbers with uncertainties -==================================== +If we compare the equality of two Variables with the same nominal value and +uncertainty, we see -It is possible to put numbers with uncertainties in NumPy_ arrays and -matrices: +>>> x = ufloat(5, 0.5) +>>> y = ufloat(5, 0.5) +>>> x == x +True +>>> x == y +False ->>> arr = numpy.array([ufloat(1, 0.01), ufloat(2, 0.1)]) ->>> 2*arr -[2.0+/-0.02 4.0+/-0.2] ->>> print arr.sum() -3.00+/-0.10 +The difference here is that although the two Python objects have the same +nominal value and uncertainty, these are independent, uncorrelated values. It +is not exactly true that the difference is based on identity, note that -Thus, usual operations on NumPy arrays can be performed transparently -even when these arrays contain numbers with uncertainties. +>>> x == (1.0*x) +True +>>> x is (1.0*x) +False + +In order for the result of two calculations with uncertainties to be considered +equal, the :mod:`uncertainties` package does not test whether the nominal value +and the uncertainty have the same value. Instead it checks whether the +difference of the two calculations has a nominal value of 0 *and* an +uncertainty of 0. + +>>> (x -x) +0.0+/-0 +>>> (x -y) +0.0+/-0.7071067811865476 + + +Comparisons of magnitude +------------------------------------ + +The concept of comparing the magnitude of values with uncertainties is a bit +complicated. That is, a Variable with a value of 25 +/- 10 might be greater +than a Variable with a value of 24 +/- 8 most of the time, but *sometimes* it +might be less than it. The :mod:`uncertainties` package takes the simple +approach of comparing nominal values. That is + +>>> a = ufloat(25, 10) +>>> b = ufloat(24, 8) +>>> a > b +True + +Note that combining this comparison and the above discussion of `==` and `!=` +can lead to a result that maybe somewhat surprising: + + +>>> a = ufloat(25, 10) +>>> b = ufloat(25, 8) +>>> a >= b +False +>>> a > b +False +>>> a == b +False +>>> a.nominal_value >= b.nominal_value +True + +That is, since `a` is neither greater than `b` (nominal value only) nor equal to +`b`, it cannot be greater than or equal to `b`. + + + .. index:: + pair: testing (scalar); NaN + + +Handling NaNs and infinities +=============================== + +NaN values can appear in either the nominal value or uncertainty of a +Variable. As is always the case, care must be exercised when handling NaN +values. + +While :func:`math.isnan` and :func:`numpy.isnan` will raise `TypeError` +exceptions for uncertainties Variables (because an uncertainties Variable is +not a float), the function :func:`umath.isnan` will return whether the nominal +value of a Variable is NaN. Similarly, :func:`umath.isinf` will return whether +the nominal value of a Variable is infinite. + +To check whether the uncertainty is NaN or Inf, use one of :func:`math.isnan`, +:func:`math.isinf`, :func:`nupmy.isnan`, or , :func:`nupmy.isinf` on the +``std_dev`` attribute. -:doc:`More complex operations on NumPy arrays and matrices -` can be -performed through the dedicated :mod:`uncertainties.unumpy` module. .. index:: correlations; detailed example @@ -143,7 +280,7 @@ calculation. For example, when :data:`x` is the number with uncertainty defined above, >>> square = x**2 ->>> print square +>>> print(square) 0.040+/-0.004 >>> square - x*x 0.0+/-0 @@ -161,29 +298,9 @@ performed in as many steps as necessary, exactly as with simple floats. When various quantities are combined through mathematical operations, the result is calculated by taking into account all the correlations between the quantities involved. All of this is done -completely **transparently**. - - -Access to the uncertainty and to the nominal value -================================================== - -.. index:: - pair: nominal value; scalar - pair: uncertainty; scalar +transparently. -The nominal value and the uncertainty (standard deviation) can also be -accessed independently: ->>> print square -0.040+/-0.004 ->>> print square.nominal_value -0.04 ->>> print square.n # Abbreviation -0.04 ->>> print square.std_dev -0.004 ->>> print square.s # Abbreviation -0.004 Access to the individual sources of uncertainty =============================================== @@ -205,7 +322,7 @@ when the variables are **tagged**: >>> sum_value 21.0+/-0.223606797749979 >>> for (var, error) in sum_value.error_components().items(): -... print "{}: {}".format(var.tag, error) +... print("{}: {}".format(var.tag, error)) ... u variable: 0.1 v variable: 0.2 @@ -233,43 +350,6 @@ these two errors, since the variables from .. index:: comparison operators -Comparison operators -==================== - -Comparison operators behave in a natural way: - ->>> print x -0.200+/-0.010 ->>> y = x + 0.0001 ->>> y -0.2001+/-0.01 ->>> y > x -True ->>> y > 0 -True - -One important concept to keep in mind is that :func:`ufloat` creates a -random variable, so that two numbers with the same nominal value and -standard deviation are generally different: - ->>> y = ufloat(1, 0.1) ->>> z = ufloat(1, 0.1) ->>> print y -1.00+/-0.10 ->>> print z -1.00+/-0.10 ->>> y == y -True ->>> y == z -False - -In physical terms, two rods of the same nominal length and uncertainty -on their length are generally of different sizes: :data:`y` is different -from :data:`z`. - -More detailed information on the semantics of comparison operators for -numbers with uncertainties can be found in the :ref:`Technical Guide -`. .. index:: covariance matrix @@ -360,14 +440,14 @@ Alternatively, correlated values can be defined through: - a *correlation* matrix between each variable of this sequence (the correlation matrix is the covariance matrix normalized with individual standard deviations; it has ones on its - diagonal)—in the form of a NumPy array-like object, e.g. a + diagonal)—in the form of a NumPy array-like object, e.g. a list of lists, or a NumPy array. -Example: +Example: >>> (u3, v3, sum3) = uncertainties.correlated_values_norm( ... [(1, 0.1), (10, 0.1), (21, 0.22360679774997899)], corr_matrix) ->>> print u3 +>>> print(u3) 1.00+/-0.10 The three returned numbers with uncertainties have the correct @@ -379,232 +459,6 @@ through :func:`correlation_matrix`). single: Fortran code; wrapping single: wrapping (C, Fortran,…) functions -.. index:: - printing - formatting - -Printing -======== - -.. Overview: - -Numbers with uncertainties can be printed conveniently: - ->>> print x -0.200+/-0.010 - -The resulting form can generally be parsed back with -:func:`ufloat_fromstr` (except for the LaTeX form). - -.. Precision matching: - -The nominal value and the uncertainty always have the **same -precision**: this makes it easier to compare them. - -Standard formats ----------------- - -.. Formatting method: - -More **control over the format** can be obtained (in Python 2.6+) -through the usual :func:`format` method of strings: - ->>> print 'Result = {:10.2f}'.format(x) -Result = 0.20+/- 0.01 - -(Python 2.6 requires ``'{0:10.2f}'`` instead, with the usual explicit -index. In Python 2.5 and earlier versions, :func:`str.format` is not -available, but one can use the :func:`format` method of numbers with -uncertainties instead: ``'Result = %s' % x.format('10.2f')``.) - -.. Legacy formats and base syntax of the format specification: - -**All the float format specifications** are accepted, except those -with the ``n`` format type. In particular, a fill character, an -alignment option, a sign or zero option, a width, or the ``%`` format -type are all supported. - -The usual **float formats with a precision** retain their original -meaning (e.g. ``.2e`` uses two digits after the decimal point): code -that works with floats produces similar results when running with -numbers with uncertainties. - -Precision control ------------------ - -.. Precision control: - -It is possible to **control the number of significant digits of the -uncertainty** by adding the precision modifier ``u`` after the -precision (and before any valid float format type like ``f``, ``e``, -the empty format type, etc.): - ->>> print '1 significant digit on the uncertainty: {:.1u}'.format(x) -1 significant digit on the uncertainty: 0.20+/-0.01 ->>> print '3 significant digits on the uncertainty: {:.3u}'.format(x) -3 significant digits on the uncertainty: 0.2000+/-0.0100 ->>> print '1 significant digit, exponent notation: {:.1ue}'.format(x) -1 significant digit, exponent notation: (2.0+/-0.1)e-01 ->>> print '1 significant digit, percentage: {:.1u%}'.format(x) -1 significant digit, percentage: (20+/-1)% - -When :mod:`uncertainties` must **choose the number of significant -digits on the uncertainty**, it uses the `Particle -Data Group -`_ rounding -rules (these rules keep the number of digits small, which is -convenient for reading numbers with uncertainties, and at the same -time prevent the uncertainty from being displayed with too few -digits): - ->>> print 'Automatic number of digits on the uncertainty: {}'.format(x) -Automatic number of digits on the uncertainty: 0.200+/-0.010 ->>> print x -0.200+/-0.010 - -Custom options --------------- - -.. Options: - -:mod:`uncertainties` provides even more flexibility through custom -formatting options. They can be added at the end of the format string: - -- ``P`` for **pretty-printing**: - - >>> print '{:.2e}'.format(x) - (2.00+/-0.10)e-01 - >>> print u'{:.2eP}'.format(x) - (2.00±0.10)×10⁻¹ - - The pretty-printing mode thus uses "±", "×" and superscript - exponents. Note that the pretty-printing mode implies using - **Unicode format strings** (``u'…'`` in Python 2, but simply ``'…'`` - in Python 3). - -- ``S`` for the **shorthand notation**: - - >>> print '{:+.1uS}'.format(x) # Sign, 1 digit for the uncertainty, shorthand - +0.20(1) - - In this notation, the digits in parentheses represent the - uncertainty on the last digits of the nominal value. - -- ``L`` for a **LaTeX** output: - - >>> print x*1e7 - (2.00+/-0.10)e+06 - >>> print '{:L}'.format(x*1e7) # Automatic exponent form, LaTeX - \left(2.00 \pm 0.10\right) \times 10^{6} - -- ``p`` is for requiring that parentheses be always printed around the …±… part - (without enclosing any exponent or trailing "%", etc.). This can for instance - be useful so as to explicitly factor physical units: - - >>> print '{:p} kg'.format(x) # Adds parentheses - (0.200+/-0.010) kg - >>> print("{:p} kg".format(x*1e7)) # No parentheses added (exponent) - (2.00+/-0.10)e+06 kg - -These custom formatting options **can be combined** (when meaningful). - -Details -------- - -.. Common exponent: - -A **common exponent** is automatically calculated if an exponent is -needed for the larger of the nominal value (in absolute value) and the -uncertainty (the rule is the same as for floats). The exponent is -generally **factored**, for increased legibility: - ->>> print x*1e7 -(2.00+/-0.10)e+06 - -When a *format width* is used, the common exponent is not factored: - ->>> print 'Result = {:10.1e}'.format(x*1e-10) -Result = 2.0e-11+/- 0.1e-11 - -(Using a (minimal) width of 1 is thus a way of forcing exponents to not -be factored.) Thanks to this feature, each part (nominal value and -standard deviation) is correctly aligned across multiple lines, while the -relative magnitude of the error can still be readily estimated thanks to -the common exponent. - -.. Special cases: - -An uncertainty which is *exactly* **zero** is always formatted as an -integer: - ->>> print ufloat(3.1415, 0) -3.1415+/-0 ->>> print ufloat(3.1415e10, 0) -(3.1415+/-0)e+10 ->>> print ufloat(3.1415, 0.0005) -3.1415+/-0.0005 ->>> print '{:.2f}'.format(ufloat(3.14, 0.001)) -3.14+/-0.00 ->>> print '{:.2f}'.format(ufloat(3.14, 0.00)) -3.14+/-0 - -**All the digits** of a number with uncertainty are given in its -representation: - ->>> y = ufloat(1.23456789012345, 0.123456789) ->>> print y -1.23+/-0.12 ->>> print repr(y) -1.23456789012345+/-0.123456789 ->>> y -1.23456789012345+/-0.123456789 - -**More information** on formatting can be obtained with ``pydoc -uncertainties.UFloat.__format__`` (customization of the LaTeX output, -etc.). - -Global formatting ------------------ - -It is sometimes useful to have a **consistent formatting** across -multiple parts of a program. Python's `string.Formatter class -`_ -allows one to do just that. Here is how it can be used to consistently -use the shorthand notation for numbers with uncertainties: - -.. code-block:: python - - class ShorthandFormatter(string.Formatter): - - def format_field(self, value, format_spec): - if isinstance(value, uncertainties.UFloat): - return value.format(format_spec+'S') # Shorthand option added - # Special formatting for other types can be added here (floats, etc.) - else: - # Usual formatting: - return super(ShorthandFormatter, self).format_field( - value, format_spec) - - frmtr = ShorthandFormatter() - - print frmtr.format("Result = {0:.1u}", x) # 1-digit uncertainty - -prints with the shorthand notation: ``Result = 0.20(1)``. - - -Customizing the pretty-print and LaTeX outputs ----------------------------------------------- - -The pretty print and LaTeX outputs themselves can be customized. - -For example, the pretty-print representation of numbers with uncertainty can -display multiplication with a centered dot (⋅) instead of the default symbol -(×), like in ``(2.00±0.10)⋅10⁻¹``; this is easily done through the global -setting ``uncertainties.core.MULT_SYMBOLS["pretty-print"] = "⋅"``. - -Beyond this multiplication symbol, the "±" symbol, the parentheses and the -exponent representations can also be customized globally. The details can be -found in the documentation of :func:`uncertainties.core.format_num`. Making custom functions accept numbers with uncertainties ========================================================= @@ -636,9 +490,6 @@ result are automatically calculated numerically. **Analytical uncertainty calculations can be performed** if derivatives are provided to :func:`wrap`. -More details are available in the documentation string of :func:`wrap` -(accessible through the ``pydoc`` command, or Python's :func:`help` -shell function). Miscellaneous utilities ======================= @@ -674,9 +525,9 @@ access the **nominal value and uncertainty of all numbers in a uniform manner**. This is what the :func:`nominal_value` and :func:`std_dev` functions do: ->>> print uncertainties.nominal_value(x) +>>> print(uncertainties.nominal_value(x)) 0.2 ->>> print uncertainties.std_dev(x) +>>> print(uncertainties.std_dev(x)) 0.01 >>> uncertainties.nominal_value(3) 3 diff --git a/pyproject.toml b/pyproject.toml index 5798fcaa..10479bf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ description = """\ fast calculation of derivatives').\ """ readme = "README.rst" -requires-python = ">=3.1" +requires-python = ">=3.8" keywords = [ "error propagation", "uncertainties", "uncertainty calculations", "standard deviation", "derivatives", "partial derivatives", @@ -49,6 +49,10 @@ classifiers = [ ] dependencies = [] +[tool.setuptools.packages.find] +include = ["uncertainties"] +exclude = ["doc", "test"] + [project.urls] Documentation = "http://uncertainties-python-package.readthedocs.io/" Repository = "https://github.com/lmfit/uncertainties" @@ -56,7 +60,10 @@ Issues = "https://github.com/lmfit/uncertainties/issues" Changelog = "https://github.com/lmfit/uncertainties/blob/master/CHANGES.rst" [project.optional-dependencies] -optional = ["numpy"] +arrays = ["numpy"] +test = ["pytest", "pytest_cov"] +doc = ["sphinx", "sphinx-copybutton", "python-docs-theme"] +all = ["uncertainties[doc,test,arrays]"] [tool.pytest.ini_options] -testpaths = ["tests"] \ No newline at end of file +testpaths = ["tests"] diff --git a/uncertainties/core.py b/uncertainties/core.py index a5870dfe..c4815462 100644 --- a/uncertainties/core.py +++ b/uncertainties/core.py @@ -70,6 +70,7 @@ def isinfinite(x): # Variable subclass), but possibly manipulated by external code # ['derivatives()' method, etc.]. 'UFloat', + 'Variable', # Wrapper for allowing non-pure-Python function to handle # quantitities with uncertainties: @@ -140,7 +141,7 @@ def correlated_values(nom_values, covariance_mat, tags=None): covariance_mat -- full covariance matrix of the returned numbers with uncertainties. For example, the first element of this matrix is the variance of the first number with uncertainty. This matrix must be a - NumPy array-like (list of lists, NumPy array, etc.). + NumPy array-like (list of lists, NumPy array, etc.). tags -- if 'tags' is not None, it must list the tag of each new independent variable. @@ -148,9 +149,9 @@ def correlated_values(nom_values, covariance_mat, tags=None): # !!! It would in principle be possible to handle 0 variance # variables by first selecting the sub-matrix that does not contain - # such variables (with the help of numpy.ix_()), and creating + # such variables (with the help of numpy.ix_()), and creating # them separately. - + std_devs = numpy.sqrt(numpy.diag(covariance_mat)) # For numerical stability reasons, we go through the correlation @@ -229,8 +230,8 @@ def correlated_values_norm(values_with_std_dev, correlation_mat, # The coordinates of each new uncertainty as a function of the # new variables must include the variable scale (standard deviation): - transform *= std_devs[:, numpy.newaxis] - + transform *= std_devs[:, numpy.newaxis] + # Representation of the initial correlated values: values_funcs = tuple( AffineScalarFunc( @@ -416,121 +417,51 @@ def __str__(self): self.__class__.__name__, ', '.join(map(str, self.returned_elements))) -def wrap(f, derivatives_args=[], derivatives_kwargs={}): - """ - Wraps a function f into a function that also accepts numbers with - uncertainties (UFloat objects); the wrapped function returns the - value of f with the correct uncertainty and correlations. The - wrapped function is intended to be used as a drop-in replacement - for the original function: they can be called in the exact same - way, the only difference being that numbers with uncertainties can - be given to the wrapped function where f accepts float arguments. - - Doing so may be necessary when function f cannot be expressed - analytically (with uncertainties-compatible operators and - functions like +, *, umath.sin(), etc.). - - f must return a float-like (i.e. a float, an int, etc., not a - list, etc.), unless when called with no number with - uncertainty. This is because the wrapped function generally - returns numbers with uncertainties: they represent a probability - distribution over the real numbers. - - If the wrapped function is called with no argument that has an - uncertainty, the value of f is returned. - - Parameters: the derivatives_* parameters can be used for defining - some of the partial derivatives of f. All the (non-None) - derivatives must have the same signature as f. - - derivatives_args -- - - Iterable that, when iterated over, returns either derivatives - (functions) or None. derivatives_args can in particular be a - simple sequence (list or tuple) that gives the derivatives of - the first positional parameters of f. - - Each function must be the partial derivative of f with respect - to the corresponding positional parameters. These functions - take the same arguments as f. - - The positional parameters of a function are usually - positional-or-keyword parameters like in the call func(a, - b=None). However, they also include var-positional parameters - given through the func(a, b, *args) *args syntax. In the last - example, derivatives_args can be an iterable that returns the - derivative with respect to a, b and then to each optional - argument in args. - - A value of None (instead of a function) obtained when - iterating over derivatives_args is automatically replaced by - the relevant numerical derivative. This derivative is not used - if the corresponding argument is not a number with - uncertainty. A None value can therefore be used for non-scalar - arguments of f (like string arguments). - - If the derivatives_args iterable yields fewer derivatives than - needed, wrap() automatically sets the remaining unspecified - derivatives to None (i.e. to the automatic numerical - calculation of derivatives). - - An indefinite number of derivatives can be specified by having - derivatives_args be an infinite iterator; this can for - instance be used for specifying the derivatives of functions - with a undefined number of argument (like sum(), whose partial - derivatives all return 1). - - derivatives_kwargs -- - - Dictionary that maps keyword parameters to their derivatives, - or None (as in derivatives_args). - - Keyword parameters are defined as being those of kwargs when f - has a signature of the form f(..., **kwargs). In Python 3, - these keyword parameters also include keyword-only parameters. - - Non-mapped keyword parameters are replaced automatically by - None: the wrapped function will use, if necessary, numerical - differentiation for these parameters (as with - derivatives_args). - - Note that this dictionary only maps keyword *parameters* from - the *signature* of f. The way the wrapped function is called - is immaterial: for example, if f has signature f(a, b=None), - then derivatives_kwargs should be the empty dictionary, even - if the wrapped f can be called a wrapped_f(a=123, b=42). - - Example (for illustration purposes only, as - uncertainties.umath.sin() runs faster than the examples that - follow): wrap(math.sin) is a sine function that can be applied to - numbers with uncertainties. Its derivative will be calculated - numerically. wrap(math.sin, [None]) would have produced the same - result. wrap(math.sin, [math.cos]) is the same function, but with - an analytically defined derivative. - - Numerically calculated derivatives are meaningless when the - function is not differentiable (e.g., math.hypot(x, y) in (x, y) = - (0, 0), and sqrt(x) in x = 0). The corresponding uncertainties are - either meaningless (case of hypot) or raise an exception when - calculated (case of sqrt). In such cases, it is recommended (but - not mandatory) to supply instead a derivative function that - returns NaN where the function is not differentiable. This - function can still numerically calculate the derivative where - defined, for instance by using the - uncertainties.core.partial_derivative() function. - - The correctness of the supplied analytical derivatives an be - tested by setting them to None instead and comparing the - analytical and the numerical differentiation results. - - Note on efficiency: the wrapped function assumes that f cannot - accept numbers with uncertainties as arguments. If f actually does - handle some arguments even when they have an uncertainty, the - wrapped function ignores this fact, which might lead to a - performance hit: wrapping a function that actually accepts numbers - with uncertainty is likely to make it slower. - """ +def wrap(f, derivatives_args=None, derivatives_kwargs=None): + """Wrap a function f into one that accepts Variables. + + The function f must return a float or integer value. The returned + wrapped function will return values with both uncertainties and + correlations, but can be used as a drop-in replacement for the + original function. + Arguments: + ---------- + derivatives_args: list or iterable + list or tupleof derivative functionss or None with respect to + the positional arguments of `f`. See Note 1. + derivatives_kwargs: dictionary + dict of derivative functionss or None with respect to the + keyword arguments of `f`. See Note 1. + + Notes: + ------- + 1. Each function must be the partial derivative of f with respect to the + corresponding positional parameters, and must have the same signature + as ``f``. `derivative_args` hold derivitative functions for positional + arguments (include `*varargs`), while `derivative_kwargs` holds + derivitative functions for keyword arguments (include `**kwargs`). If an + entry is `None` or not supplied, and if the argument value isa numeric + Variable, a numerical derivative will be used. Non-numeric are ignored. + 2. If derivatives are meaningless or the function is not function is not + differentiable, the derivative funcion should return NaN for values + for which the the function is not differentiable. + + Example: + -------- + To wrap `sin`, one could do + >>> from uncertainties import wrap, umath + >>> import math + >>> usin_a = wrap(math.sin) # uses numerical derivative + >>> usin_b = wrap(math.sin, [math.cos]) # use analytic derivative + >>> usin_c = umath.sin # builtin, same as usin_2 + + These will all give the same result. + """ + if derivatives_args is None: + derivatives_args = [] + if derivatives_kwargs is None: + derivatives_kwargs = {} derivatives_args_index = IndexableIter( # Automatic addition of numerical derivatives in case the # supplied derivatives_args is shorter than the number of @@ -980,7 +911,7 @@ def nrmlze_superscript(number_str): ValueError is raised if the conversion cannot be done. - number_str -- string to be converted (of type str, but also possibly, for + number_str -- string to be converted (of type str, but also possibly, for Python 2, unicode, which allows this string to contain superscript digits). ''' # !! Python 3 doesn't need this str(), which is only here for giving the @@ -1932,7 +1863,7 @@ def __format__(self, format_spec): outputs like (1.0±0.2) or (1.0±0.2)e7, which can be useful for removing any ambiguity if physical units are added after the printed number. - + An uncertainty which is exactly zero is represented as the integer 0 (i.e. with no decimal point). @@ -2692,12 +2623,10 @@ class NegativeStdDev(Exception): class Variable(AffineScalarFunc): """ - Representation of a float-like scalar random variable, along with - its uncertainty. + Representation of a float-like scalar Variable with its uncertainty. - Objects are meant to represent variables that are independent from - each other (correlations are handled through the AffineScalarFunc - class). + Variables are independent from each other, but correlations between them + are handled through the AffineScalarFunc class. """ # To save memory in large arrays: @@ -2976,7 +2905,7 @@ def parse_error_in_parentheses(representation): The digits between parentheses correspond to the same number of digits at the end of the nominal value (the decimal point in the uncertainty is optional). Example: 12.34(142) = 12.34±1.42. - + Raises ValueError if the string cannot be parsed. """ @@ -3136,108 +3065,79 @@ def str_to_number_with_uncert(representation): def ufloat_fromstr(representation, tag=None): """ - Return a new random variable (Variable object) from a string. - - Strings 'representation' of the form '12.345+/-0.015', - '12.345(15)', '12.3' or u'1.2±0.1' (Unicode string) are recognized - (see more complete list below). In the last case, an uncertainty - of +/-1 is assigned to the last digit. - - Invalid representations raise a ValueError. - - This function tries to parse back most of the formats that are made - available by this module. Examples of valid string representations: - - 12.3e10+/-5e3 - (-3.1415 +/- 0.0001)e+02 # Factored exponent - - # Pretty-print notation (only with a unicode string): - 12.3e10 ± 5e3 # ± symbol - (12.3 ± 5.0) × 10⁻¹² # Times symbol, superscript - 12.3 ± 5e3 # Mixed notation (± symbol, but e exponent) - - # Double-exponent values: - (-3.1415 +/- 1e-4)e+200 - (1e-20 +/- 3)e100 - - 0.29 - 31. - -31. - 31 - -3.1e10 - - -1.23(3.4) - -1.34(5) - 1(6) - 3(4.2) - -9(2) - 1234567(1.2) - 12.345(15) - -12.3456(78)e-6 - 12.3(0.4)e-5 - 169.0(7) - 169.1(15) - .123(4) - .1(.4) - - # NaN uncertainties: - 12.3(nan) - 12.3(NAN) - 3±nan - - Surrounding spaces are ignored. - - About the "shorthand" notation: 1.23(3) = 1.23 ± 0.03 but - 1.23(3.) = 1.23 ± 3.00. Thus, the presence of a decimal point in - the uncertainty signals an absolute uncertainty (instead of an - uncertainty on the last digits of the nominal value). - """ - - (nominal_value, std_dev) = str_to_number_with_uncert( - representation.strip()) + Create an uncertainties Variable from a string representation. + Several representation formats are supported. - return ufloat(nominal_value, std_dev, tag) - -def ufloat_obsolete(representation, tag=None): - ''' - Legacy version of ufloat(). Will eventually be removed. + Arguments: + ---------- + representation: string + string representation of a value with uncertainty + tag: string or `None` + optional tag for tracing and organizing Variables ['None'] + + Returns: + -------- + uncertainties Variable. + + Notes: + -------- + 1. Invalid representations raise a ValueError. + + 2. Using the form "nominal(std)" where "std" is an integer creates + a Variable with "std" giving the least significant digit(s). + That is, "1.25(3)" is the same as `ufloat(1.25, 0.03)`, + while "1.25(3.)" is the same as `ufloat(1.25, 3.)` + + Examples: + ----------- + + >>> x = ufloat_fromsstr("12.58+/-0.23") # = ufloat(12.58, 0.23) + >>> x = ufloat_fromsstr("12.58 ± 0.23") # = ufloat(12.58, 0.23) + >>> x = ufloat_fromsstr("3.85e5 +/- 2.3e4") # = ufloat(3.8e5, 2.3e4) + >>> x = ufloat_fromsstr("(38.5 +/- 2.3)e4") # = ufloat(3.8e5, 2.3e4) + + >>> x = ufloat_fromsstr("72.1(2.2)") # = ufloat(72.1, 2.2) + >>> x = ufloat_fromsstr("72.15(4)") # = ufloat(72.15, 0.04) + >>> x = ufloat_fromstr("680(41)e-3") # = ufloat(0.68, 0.041) + >>> x = ufloat_fromstr("23.2") # = ufloat(23.2, 0.1) + >>> x = ufloat_fromstr("23.29") # = ufloat(23.29, 0.01) + + >>> x = ufloat_fromstr("680.3(nan)") # = ufloat(680.3, numpy.nan) + """ + (nom, std) = str_to_number_with_uncert(representation.strip()) + return ufloat(nom, std, tag) - representation -- either a (nominal_value, std_dev) tuple, or a - string representation of a number with uncertainty, in a format - recognized by ufloat_fromstr(). - ''' - if isinstance(representation, tuple): - return ufloat(representation[0], representation[1], tag) - else: - return ufloat_fromstr(representation, tag) - -# The arguments are named for the new version, instead of bearing -# names that are closer to their obsolete use (e.g., std_dev could be -# instead std_dev_or_tag, since it can be the tag, in the obsolete -# ufloat((3, 0.14), "pi") form). This has the advantage of allowing -# new code to use keyword arguments as in ufloat(nominal_value=3, -# std_dev=0.14), without breaking when the obsolete form is not -# supported anymore. def ufloat(nominal_value, std_dev=None, tag=None): """ - Return a new random variable (Variable object). - - - ufloat(nominal_value, std_dev), - - ufloat(nominal_value, std_dev, tag=...). - - nominal_value -- nominal value of the random variable. It is more - meaningful to use a value close to the central value or to the - mean. This value is propagated by mathematical operations as if it - was a float. + Create an uncertainties Variable - std_dev -- standard deviation of the random variable. The standard - deviation must be convertible to a positive float, or be NaN. - - tag -- optional string tag for the variable. Variables don't have - to have distinct tags. Tags are useful for tracing what values - (and errors) enter in a given result (through the - error_components() method). + Arguments: + ---------- + nominal_value: float + nominal value of Variable + std_dev: float or `None` + standard error of Variable, or `None` if not available [`None`] + tag: string or `None` + optional tag for tracing and organizing Variables ['None'] + + Returns: + -------- + uncertainties Variable + + Examples + ---------- + >>> a = ufloat(5, 0.2) + >>> b = ufloat(1000, 30, tag='kilo') + + + Notes: + -------- + 1. `nominal_value` is typically interpreted as `mean` or `central value` + 2. `std_dev` is typically interpreted as `standard deviation` or the + 1-sigma level uncertainty. + 3. The returned Variable will have attributes `nominal_value`, `std_dev`, + and `tag` which match the input values. """ - return Variable(nominal_value, std_dev, tag=tag) \ No newline at end of file + return Variable(nominal_value, std_dev, tag=tag)