2019-05-13 09:15:24 +02:00
|
|
|
# Secondary Value
|
|
|
|
|
|
|
|
This is a utility to simplify the calculation of values and their
|
|
|
|
uncertaintues from symbolic formulas by using `sympy` and `numpy`.
|
|
|
|
|
|
|
|
## Installation
|
|
|
|
Just a quick `pip install SecondaryValue` will do.
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
(A Documentation will follow soon. For now: look at the docstrings!)
|
|
|
|
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
|
|
from SecondaryValue import SecondaryValue
|
|
|
|
|
|
|
|
# create a secondary value
|
|
|
|
# the argument can be either a string or a sympy expression
|
|
|
|
x = SecondaryValue("a*b/sqrt(c)")
|
|
|
|
|
|
|
|
# Calculate a result value by substituting the keyword arguments
|
|
|
|
# where a keyword agument may consist of (value, error_1, error_2, ...)
|
|
|
|
# and (...) stands for any iterable.
|
|
|
|
result = x(a=(1, 20), b=(2, 30), c=2)
|
|
|
|
|
|
|
|
# The calculation returns a numpy array with the length of the longest
|
|
|
|
# keyword argument above: [value, error_1, error_2]
|
|
|
|
# For each error_n the uncertainties error_n of the keyword args above are
|
|
|
|
# used if present. This may be useful to calculate statistical and systemic
|
|
|
|
# errors in one go.
|
|
|
|
print(result)
|
2019-05-15 07:07:17 +02:00
|
|
|
# >> (1.41421356, 35.35533906)
|
2019-05-13 09:15:24 +02:00
|
|
|
|
2019-05-13 09:54:19 +02:00
|
|
|
# As a goodie, you can print out the gaussian error distribution in
|
|
|
|
# symbolic form. (Works best in Jupyter Notebooks)
|
|
|
|
x.pretty_gauss_propagation('a', 'b', 'c')
|
|
|
|
```
|
|
|
|
|
|
|
|
### Default Values
|
2019-05-13 19:08:05 +02:00
|
|
|
To reduce boilerplate one can set default substitutions for symbols (with errors).
|
|
|
|
This especially useful for constants.
|
2019-05-13 09:54:19 +02:00
|
|
|
|
|
|
|
```python
|
|
|
|
from SecondaryValue import SecondaryValue
|
|
|
|
|
|
|
|
# create a secondary value with default arguments
|
|
|
|
x = SecondaryValue("a + b", defaults=dict(b=1/2))
|
|
|
|
|
|
|
|
# this works because `b` is substituted from the defaults
|
|
|
|
result = x(b=1/2)
|
|
|
|
print(result)
|
|
|
|
# >> 1.0
|
|
|
|
|
2019-05-13 09:15:24 +02:00
|
|
|
# As a goodie, you can print out the gaussian error distribution in
|
|
|
|
# symbolic form. (Works best in Jupyter Notebooks)
|
|
|
|
x.pretty_gauss_propagation('a', 'b', 'c')
|
|
|
|
```
|
2019-05-13 16:01:45 +02:00
|
|
|
|
|
|
|
### Vectorized Input
|
|
|
|
`SecondaryValue` supports vectorized input. As a rule-of-thump: Put
|
|
|
|
the iterable (list, np.array) where you would put scalars.
|
|
|
|
|
|
|
|
You can mix scalars and vectors as long as all errors and values are
|
|
|
|
either scalar or have the same length.
|
|
|
|
|
|
|
|
```python
|
|
|
|
from SecondaryValue import SecondaryValue
|
|
|
|
x = SecondaryValue('a**2+b')
|
|
|
|
|
|
|
|
x(a=[[1,2,3]], b=1)
|
|
|
|
# >> array([ 2., 5., 10.])
|
|
|
|
|
|
|
|
x(a=([1,2,3], 1), b=1)
|
|
|
|
# >> (array([ 2., 5., 10.]), array([2., 4., 6.]))
|
|
|
|
|
|
|
|
x(a=([1,2,3], [1,2,3]), b=1)
|
|
|
|
# >> (array([ 2., 5., 10.]), array([ 2., 8., 18.]))
|
|
|
|
|
|
|
|
x(a=([1,2,3], [1,2,3]), b=([1,2,3], 1))
|
|
|
|
# >> (array([ 2., 6., 12.]), array([ 2.23606798, 8.06225775, 18.02775638]))
|
|
|
|
|
|
|
|
# THAT DOES NOT WORK:
|
|
|
|
x(a=([1,2,3], [1,2,3]), b=([1,2], 1))
|
|
|
|
```
|
|
|
|
|
2019-05-15 07:07:17 +02:00
|
|
|
If all the returned arrays in the tuple have the same shape, you can
|
|
|
|
easily convert that tuple to a numpy array:
|
|
|
|
`np.array(x(a=([1,2,3], [1,2,3]), b=([1,2,3], 1)))`
|
|
|
|
|
2019-05-13 09:15:24 +02:00
|
|
|
### Dependencies
|
|
|
|
To make the calculation of complex values easier, one can define
|
|
|
|
dependencies for a `SecondaryValue`:
|
|
|
|
|
|
|
|
```python
|
|
|
|
from SecondaryValue import SecondaryValue
|
|
|
|
|
|
|
|
dep = SecondaryValue('u')
|
2019-05-15 07:07:17 +02:00
|
|
|
x = SecondaryValue("a + b", dependencies=dict(a=dep))
|
2019-05-13 09:15:24 +02:00
|
|
|
|
|
|
|
# x will now accept u as an additional kwarg and calculate d==dep on the fly
|
2019-05-15 07:07:17 +02:00
|
|
|
# and return a dictionary containing it as a second return value if you
|
|
|
|
# specify `retdeps=True`.
|
|
|
|
print(x(b=1, u=(1, 2), retdeps=True))
|
|
|
|
# >> ((2.0, 2.0), {'a': ((1.0, 2.0), {})})
|
|
|
|
|
|
|
|
# To make the output predictable, the dependencies aren't returned by deafult.
|
2019-05-13 09:15:24 +02:00
|
|
|
print(x(b=1, u=(1, 2)))
|
2019-05-15 07:07:17 +02:00
|
|
|
# >> (2.0, 2.0)
|
2019-05-13 09:15:24 +02:00
|
|
|
|
|
|
|
# you can overwrite the dependency calculation
|
|
|
|
print(x(b=1/2, a=1/2))
|
|
|
|
# >> 1.0
|
|
|
|
```
|
2019-05-15 07:07:17 +02:00
|
|
|
|
|
|
|
If there are no dependency values, an empty dict will be returned when
|
|
|
|
`retdeps=True` is specified.
|