As you may or may not know, the formula for the uncertainty in a variable defined by some function is given by:
.
I want to create a python function that does this for me. This is what I currently have:
import sympy as sp
from sympy import lambdify, symbols,diff
L,g = symbols('L g', real=True)
value_of_var = [5.4,9.8] # Arbitrary values for L and g
uncertainty_in_var = [0.3, 0.1]
function = 2*sp.pi*sp.sqrt(L/g) #Period of pendulum (purely for test purposes)
variables = [L,g]
def uncertainty_calculator(function):
partials = [diff(function,x) for x in variables] #Takes partial derivatives of variables
partial_times_uncertainty = [(x*y)**2 for x,y in zip(uncertainty_in_var,partials)] #Squares the product of the uncertainty and its respective partial
uncertainty = lambdify(variables, sp.sqrt(sum(partial_times_uncertainty)))
number = uncertainty(value_of_var[0],value_of_var[1])
return number
uncertainty = uncertainty_calculator(function)
print(uncertainty)
I'm pretty sure this works fine for this specific function, however, I want to generalize it more, I want it to take in a list of values, the uncertainty of those values, and a function, and give me the uncertainty. The problem im having is that if my variables already have a value, then the function is evaluated to a number, and because of this I just get zero when trying to calculate the partial derivatives, this is fixed by the symbols('L g', real=True)
line which keeps the function unevaluated until it gets to the lamdify part. Does anyone know how this can be done, having a function like this would really help with my labs as calculating that function by hand is a real pain.
If the function is not univariate then you will always have to specify the function and then the symbols and their values. If you are using this interactively, you could cut down on the input by passing the function as a string and the values in their order corresponding to the sorted order of the variables. The return of the function could then be a replacement dictionary (so you can check) and the computed uncertainty.
>>> def ucalc(f, x, u):
... f = S(f)
... syms = list(ordered(f.free_symbols))
... assert len(x) == len(u) == len(syms)
... reps = dict(zip(syms, x))
... ui = IndexedBase('u')
... args = []
... for i, xi in enumerate(syms):
... reps[ui[xi]] = u[i]
... args.append((ui[xi]*diff(f, xi))**2)
... return reps, sqrt(Add(*args)).n(subs=reps)
...
>>> ucalc('2*pi*sqrt(L/g)',(9.8,5.4),(.1,.3))
({L: 9.8, g: 5.4, u[L]: 0.1, u[g]: 0.3}, 0.239055276534314)
Note that an IndexedBase symbol is used to create the temporary variables for uncertainties since those do not appear in the equation itself.