Make dependeny return optional => predictable.

This commit is contained in:
Valentin Boettcher 2019-05-15 07:07:17 +02:00
parent e97c1d8838
commit 246a226c49
3 changed files with 35 additions and 22 deletions

View file

@ -28,7 +28,7 @@ result = x(a=(1, 20), b=(2, 30), c=2)
# used if present. This may be useful to calculate statistical and systemic # used if present. This may be useful to calculate statistical and systemic
# errors in one go. # errors in one go.
print(result) print(result)
# >> array([ 1.41421356, 35.35533906]) # >> (1.41421356, 35.35533906)
# As a goodie, you can print out the gaussian error distribution in # As a goodie, you can print out the gaussian error distribution in
# symbolic form. (Works best in Jupyter Notebooks) # symbolic form. (Works best in Jupyter Notebooks)
@ -82,6 +82,10 @@ x(a=([1,2,3], [1,2,3]), b=([1,2,3], 1))
x(a=([1,2,3], [1,2,3]), b=([1,2], 1)) x(a=([1,2,3], [1,2,3]), b=([1,2], 1))
``` ```
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)))`
### Dependencies ### Dependencies
To make the calculation of complex values easier, one can define To make the calculation of complex values easier, one can define
dependencies for a `SecondaryValue`: dependencies for a `SecondaryValue`:
@ -90,14 +94,22 @@ dependencies for a `SecondaryValue`:
from SecondaryValue import SecondaryValue from SecondaryValue import SecondaryValue
dep = SecondaryValue('u') dep = SecondaryValue('u')
x = SecondaryValue("a + b", dependencies={'a': dep}) x = SecondaryValue("a + b", dependencies=dict(a=dep))
# x will now accept u as an additional kwarg and calculate d==dep on the fly # x will now accept u as an additional kwarg and calculate d==dep on the fly
# and return a dictionary containing it as a second return value. # 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.
print(x(b=1, u=(1, 2))) print(x(b=1, u=(1, 2)))
# >> (array([2., 2.]), {'a': array([1., 2.])}) # >> (2.0, 2.0)
# you can overwrite the dependency calculation # you can overwrite the dependency calculation
print(x(b=1/2, a=1/2)) print(x(b=1/2, a=1/2))
# >> 1.0 # >> 1.0
``` ```
If there are no dependency values, an empty dict will be returned when
`retdeps=True` is specified.

View file

@ -66,9 +66,9 @@ class SecondaryValue:
if name in kwargs: if name in kwargs:
continue continue
tmp = sec_val(**kwargs) # we always calculate the depndencies
kwargs[name] = tmp[0] if (len(tmp) > 1 and isinstance(tmp, tuple)) \ tmp = sec_val(retdeps=True, **kwargs)
else tmp kwargs[name] = tmp[0]
calc_deps[name] = tmp calc_deps[name] = tmp
return kwargs, calc_deps return kwargs, calc_deps
@ -111,6 +111,7 @@ class SecondaryValue:
dep_values (the values and errors of the dependencies) dep_values (the values and errors of the dependencies)
:rtype: Tuple :rtype: Tuple
""" """
kwargs, dep_values = self._calc_deps(**kwargs) kwargs, dep_values = self._calc_deps(**kwargs)
@ -125,7 +126,6 @@ class SecondaryValue:
kwargs = {var: val for var, val in kwargs.items() \ kwargs = {var: val for var, val in kwargs.items() \
if var in self._symbols} if var in self._symbols}
max_uncertainties = max([len(val) for _, val in kwargs.items() \ max_uncertainties = max([len(val) for _, val in kwargs.items() \
if isinstance(val, Iterable)] or [0]) if isinstance(val, Iterable)] or [0])
@ -158,7 +158,6 @@ class SecondaryValue:
central_value = np.empty(value_length) central_value = np.empty(value_length)
for i in range(0, value_length): for i in range(0, value_length):
current_values = join_row(scalar_values, vector_values, i) current_values = join_row(scalar_values, vector_values, i)
central_value[i] = self._parsed_lambda(**current_values) central_value[i] = self._parsed_lambda(**current_values)
else: else:
central_value = self._dtype(self._parsed_lambda(**scalar_values)) central_value = self._dtype(self._parsed_lambda(**scalar_values))
@ -204,22 +203,24 @@ class SecondaryValue:
return terms return terms
def __call__(self, *args, **kwargs): def __call__(self, *args, retdeps=False, **kwargs):
"""Calculates a value from the expression by substituting """Calculates a value from the expression by substituting
variables by the values of the given keyword arguments. If an
argument is specified as a tuplpe of (value, error) the
gausssian error propagation will be computed.
The values and errors can be iterable, but must compatible shapes. The values and errors can be iterable, but must compatible
shapes.
:returns: value or [value, error] or [value, error], dependencies :param retdeps: wether to return a dictionary with the
calculated dependencies
:returns: value or [value, error] or [value, error, ...],
dependencies
:rtype: numpy data type or np array of [value, errors, ...] or :rtype: numpy data type or np array of [value, errors, ...] or
a tuple the beforementioned as first element and a a tuple the beforementioned as first element and a
dictionary with the calculated dependencies as a second value dictionary with the calculated dependencies as a
second value
""" """
# process the keyword arguments
# process the keyword arguments
values, errors, dep_values = self._process_args(*args, **kwargs) values, errors, dep_values = self._process_args(*args, **kwargs)
# calulate the central value # calulate the central value
@ -237,7 +238,7 @@ class SecondaryValue:
result.insert(0, central_value) result.insert(0, central_value)
result = tuple(result) result = tuple(result)
if dep_values: if retdeps:
return result, dep_values return result, dep_values
return result return result
@ -293,5 +294,5 @@ def filter_out_vecotrized(dictionary):
return scalar, vector return scalar, vector
def join_row(scalar, vector, index): def join_row(scalar, vector, index):
return {**scalar, **{key: val[index] \ return {**scalar, **{key: val[index] \
for key, val in vector.items()}} for key, val in vector.items()}}

View file

@ -6,7 +6,7 @@ def readme():
return f.read() return f.read()
setup(name='SecondaryValue', setup(name='SecondaryValue',
version='0.1.3', version='0.1.4',
description='A helper to calculate the gaussian error propagation.', description='A helper to calculate the gaussian error propagation.',
long_description=readme(), long_description=readme(),
long_description_content_type='text/markdown', long_description_content_type='text/markdown',