multiple functions as arguments in python - python

I have the following problem: I have two sets of data (set T and set F). And the following functions:
x(T) = arctan(T-c0), A(x(T)) = arctan(x(T) -c1),
B(x(T)) = arctan(x(T) -c2)
and Y(x(t),F) = ((A(x(t)) - B(x(t)))/2 - A(x(t))arctan(F-c3) + B(x(t))arctan(F-c4))
# where c0,c1,c2,c3,c4 are constants
Now I want to create a surface plot of Y. And for that I would like to implement Y as a python (numpy) function what turns out to be quite complicated, because Y takes other functions as input.
Another idea of mine was to evaluate x, B and A on the data separately and store the results in numpy arrays. With those I also could get the output of the function Y , but I don't know which way is better in order to plot the data and I really would like to know how to write Y as a python function.
Thank you very much for your help

It is absolutely possible to use functions as input parameters to other functions. A use case could look like:
def plus_one(standard_input_parameter_like_int):
return standard_input_parameter_like_int + 1
def apply_function(function_as_input, standard_input_parameter):
return function_as_input(standard_input_parameter)
if(__name__ == '__main__'):
print(apply_function(plus_one, 1))
I hope that helps to solve your specific problem.

[...] somethin like def s(x,y,z,*args,*args2): will yield an
error.
This is perfectly normal as (at least as far as I know) there is only one variable length non-keyword argument list allowed per function (that has to be exactly labeled as *args). So if you remove the asterisks (*) you should actually be able to run s properly.
Regarding your initial question you could do something like:
c = [0.2,-0.2,0,0,0,0]
def x(T):
return np.arctan(T-c[0])
def A(xfunc,T):
return np.arctan(xfunc(T) - c[1])
def B(xfunc,T):
return np.arctan(xfunc(T) - c[2])
def Y(xfunc,Afunc,Bfunc,t,f):
return (Afunc(xfunc,t) - Bfunc(xfunc,t))/2.0 - Afunc(xfunc,t) * np.arctan(f - c[3]) + Bfunc(xfunc,t)*np.arctan(f-c[4])
_tSet = np.linspace(-1,1,20)
_fSet = np.arange(-1,1,20)
print Y(x,A,B,_tSet,_fSet)
As you can see (and probably already tested by yourself judging from your comment) you can use functions as arguments. And as long as you don't use any 'if' conditions or other non-vectorized functions in your 'sub'-functions the top-level function should already be vectorized.

Related

Finding root of a function with two outputs specified in return statement

I am currently writing a code in Python where the objective is to find the root of the output of a function with respect to input variable x. The code looks like this:
def Compound_Correlation_Function(x):
# Here comes a long part of the code...
Equity_Solve = Tranches.loc[0, 'Par_Spread_bps'] - Market_Data.iloc[0,0]
Mezzanine_Solve = Tranches.loc[1, 'Par_Spread_bps'] - Market_Data.iloc[1,0]
return Equity_Solve, Mezzanine_Solve
Correlation_Value = optimize.root(Compound_Correlation_Function, x0 = 0.3)
As can be seen in the code block above, there are two outputs specified:
Equity_Solve
Mezzanine_Solve
I now want to find the root for both outputs separately. If I comment out the Mezzanine_Solve part in the return statement, then the the optimize procedure gives me the solution I want. Obviously, I want to automate my code as much as possible. Is it possible to specify the output for which I want to find the root in the optimize statement?
I tried the following, without success:
Correlation_Value = optimize.root(Compound_Correlation_Function[0], x0 = 0.3)
Correlation_Value = optimize.root(Compound_Correlation_Function(x)[0], x0 = 0.3)
Correlation_Value = optimize.root(Compound_Correlation_Function()[], x0 = 0.3)
Any help is appreciated. Thank you in advance!
I think the problem is that your function returns a tuple of numbers, but root is expecting a single number.
Assuming you want to solve each equation separately, then you could include an argument in Compound_Correlation_Function to switch between the functions:
def Compound_Correlation_Function(x, return_equity=True):
# Here comes a long part of the code...
if return_equity:
Equity_Solve = Tranches.loc[0, 'Par_Spread_bps'] - Market_Data.iloc[0,0]
return Equity_Solve
else:
Mezzanine_Solve = Tranches.loc[1, 'Par_Spread_bps'] - Market_Data.iloc[1,0]
return Mezzanine_Solve
Then pass the return_equity argument in as an extra argument via args, i.e. call
root(Compound_Correlation_Function, x0=0.3, args=(True,))
to solve Equity_Solve, and set args=(False,) to solve Mezzanine_Solve.
You could also define a function wrapper that calls Compound_Correlation_Function and returns only one of the values.
surely you're overthinking it. Just define two new functions:
def equity_solve(x):
return Compound_Correlation_Function(x)[0]
def mezzanine_solve(x):
return Compound_Correlation_Function(x)[1]

Integrating a function of two variables over an array of values

I'm currently trying to solve this integral using SciPy:
I was first advised to use interpolation, which I tried but cannot figure out for some reason, but would probably be a good approach. I found this post about using np.vectorize and I think it might still work, but I am getting an error. Here is the code that I have written thus far (also note that n and n,eq are not indices, they're just variable names):
import numpy as np
from scipy import integrate
def K(x): #This is a function in the integral.
b = 0.252
return b*(((4/(x**3))+(3/(x**2))+1/x) + (4/(x**3) + 1/(x**2))*np.exp(-x))
def Xntot_integrand(x,z): #Defining the integrand
Xneq_x = (1+np.exp(x))**(-1) #This is the term outside the integral and squared within it.
return Xneq_x(x)**2 * np.exp(K(z) - K(x)) * np.exp(x)
Xntot_integrand = np.vectorize(Xntot_integrand)
def Xntot_integrated(x,z):
return quad(Xntot_integrand, 0, z)
Xntot_integrated=np.vectorize(Xntot_integrated)
T_narrow = np.linspace(1,0.01,100) #Narrow T range from 1 to 0.01 MeV
z_narrow = Q/T_narrow
final_integrated_Xneq = Xntot_integrated(z_narrow)
I am getting the error that I am missing a positional argument when I call Xntot_integrated (which makes sense, I think it is still in the two variables x and z).
So I suppose the issue is stemming from where I use quad() because after it is integrated, x should go away. Any advice? Should I use tabulation/interpolation instead?
You need to be using the args keyword argument of integrate.quad to pass additional inputs to the function, so it would look like this:
def Xntot_integrated(z):
return integrate.quad(Xntot_integrand, 0, z, args=(z,))
Note here x is not an input to the integrated function, only z, the first input to the integrand is the integration variable and any extra information is passed via args=(z,) tuple.
alternatively you can define a wrapper that knows z from context and only takes the integration variable as input:
def Xntot_integrated(z):
def integrand(x):return Xntot_integrand(x,z)
return integrate.quad(integrand, 0, z)
but most API's that take a function typically have a keyword argument to specify those inputs. (threading.Thread comes to mind.)
also your Xneq_x should probably be a function itself since you accidentally use it as such inside your integrand (it is just a value there right now) and you will need to use it outside the integration anyway :)

Issues when profiling list reversal in Python vs Erlang

I was profiling Erlang's lists:reverse Built in Function (BIF) to see how well it scales with the size of the input. More specifically, I tried:
1> X = lists:seq(1, 1000000).
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,
23,24,25,26,27,28,29|...]
2> timer:tc(lists, reverse, [X]).
{57737,
[1000000,999999,999998,999997,999996,999995,999994,999993,
999992,999991,999990,999989,999988,999987,999986,999985,
999984,999983,999982,999981,999980,999979,999978,999977,
999976,999975,999974|...]}
3> timer:tc(lists, reverse, [X]).
{46896,
[1000000,999999,999998,999997,999996,999995,999994,999993,
999992,999991,999990,999989,999988,999987,999986,999985,
999984,999983,999982,999981,999980,999979,999978,999977,
999976,999975,999974|...]}
4> Y = lists:seq(1, 10000000).
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,
23,24,25,26,27,28,29|...]
5> timer:tc(lists, reverse, [Y]).
{434079,
[10000000,9999999,9999998,9999997,9999996,9999995,9999994,
9999993,9999992,9999991,9999990,9999989,9999988,9999987,
9999986,9999985,9999984,9999983,9999982,9999981,9999980,
9999979,9999978,9999977,9999976,9999975,9999974|...]}
6> timer:tc(lists, reverse, [Y]).
{214173,
[10000000,9999999,9999998,9999997,9999996,9999995,9999994,
9999993,9999992,9999991,9999990,9999989,9999988,9999987,
9999986,9999985,9999984,9999983,9999982,9999981,9999980,
9999979,9999978,9999977,9999976,9999975,9999974|...]}
Ok, so far it seems like the reverse BIF scales in approximately linear time with respect to the input (e.g. multiply the size of the input by 10 and the size of time taken also increases by a factor of 10). In pure Erlang that would make sense since we would use something like tail recursion to reverse the list. I guess that even as a BIF implemented in C, the algorithm for reversing seems a list to be the same (maybe because of the way lists are just represented in Erlang?).
Now I wanted to compare this with something another language - perhaps another dynamically typed language that I already use. So I tried a similar thing in Python - taking care to, very explicitly, use actual lists instead of generators which I anticipate would affect the performance of Python positively in this test, giving it an unfair advantage.
import time
ms_conv_factor = 10**6
def profile(func, *args):
start = time.time()
func(args)
end = time.time()
elapsed_seconds = end - start
print(elapsed_seconds * ms_conv_factor, flush=True)
x = list([i for i in range(0, 1000000)])
y = list([i for i in range(0, 10000000)])
z = list([i for i in range(0, 100000000)])
def f(m):
return m[::-1]
def g(m):
return reversed(m)
if __name__ == "__main__":
print("All done loading the lists, starting now.", flush=True)
print("f:")
profile(f, x)
profile(f, y)
print("")
profile(f, x)
profile(f, y)
print("")
profile(f, z)
print("")
print("g:")
profile(g, x)
profile(g, y)
print("")
profile(g, x)
profile(g, y)
print("")
profile(g, z)
This seems to suggest that after the function has been loaded and run once, the length of the input makes no difference and the reversal times are incredibly fast - in the range of ~0.7µs.
Exact result:
All done loading the lists, starting now.
f:
1.430511474609375
0.7152557373046875
0.7152557373046875
0.2384185791015625
0.476837158203125
g:
1.9073486328125
0.7152557373046875
0.2384185791015625
0.2384185791015625
0.476837158203125
My first, naive, guess was that python might be able to recognize the reverse construct and create something like a reverse iterator and return that (Python can work with references right? Maybe it was using some kind of optimization here). But I don't think that theory makes sense since the original list and the returned list are not the same (changing one shouldn't change the other).
So my question(s) here is(are):
Is my profiling technique here flawed? Have I written the tests in a way that favor one language over the other?
What is the difference in implementation of lists and their reversal in Erlang vs Python that make this situation (of Python being WAY faster) possible?
Thanks for your time (in advance).
This seems to suggest that after the function has been loaded and run
once, the length of the input makes no difference and the reversal
times are incredibly fast - in the range of ~0.7µs.
Because your profiling function is incorrect. It accepts variable positional arguments, but when it passes them to the function, it doesn't unpack them so you are only ever working with a tuple of length one. You need to do the following:
def profile(func, *args):
start = time.time()
func(*args) # Make sure to unpack the args!
end = time.time()
elapsed_seconds = end - start
print(elapsed_seconds * ms_conv_factor, flush=True)
So notice the difference:
>>> def foo(*args):
... print(args)
... print(*args)
...
>>> foo(1,2,3)
(1, 2, 3)
1 2 3
Also note, reversed(m) creates a reversed iterator, so it doesn't actually do anything until you iterate over it. So g will still be constant time.
But rest assured, reversing a list in Python takes linear time.

How can i use a function with "variable" constants in scipy.optimize?

how can i use "variable" constants in scipy.optimize functions? I am trying to create an iterative optimisation algorithm, which updates certain parameters in the objective function after each optimisation run.
to use a very simple example of what i want to do:
from scipy import optimize as opt
def f(x, R):
return R * (x[0]**2 + x[1]**3)
R = 0.1 # initial R value
y = []
y.append([2,2]) # initial point
for i in range(0,10):
y.append(opt.fmin(f, y[i])) # how can i include 'R' in this line??
R = some_function_to_update_R(R)
any help would be appreciated
EDIT:
would it help to re-declare the objective function each time i optimise? so make the loop look like this instead?
for i in range(0,10):
def f_temp(x_temp):
return f(x_temp,R)
y.append(opt.fmin(f_temp, y[i]))
R = some_function_to_update_R(R)
or is there some better way?
fmin supports an optional args argument, representing a tuple of additional arguments to be passed to the function you're trying to optimize:
y.append(opt.fmin(f, y[i], args=(R,)))
This is explained in the documentation for fmin; you should get into the habit of checking the documentation when you want to figure out how to do something.

Changing only the calculation in a function

Is it possible to have a function where you specify a function within it as a variable.
For example, I have two functions which follow exactly the same process, except one calculaate the Average using np.mean and the other calculates the standard deviation where only np.std is different.
i.e.
it would be defined:
def calculate(function)
you would call one in the script like:
calculate(mean)
and the other
calculate(std)
I'm just wondering if it is possible to do something like this s it would greatly reduce my script length.
EDIT
Sorry I should have said that I wanted the mean and std to be the ones predefined in numpy. getattr() in Xu's answer worked
Use getattr to get the method object according to the method name:
def calculate(function):
func = getattr(np, function)
func(...) # do what you want
calculate("mean") # calculate the average number
calculate("std") # calculate the standard deviation
Yes this is possible.
Example:
def addIt(x):
return x+x
def test(fn):
for x in xrange(5):
print fn(x)
test(addIt)
Output:
0
2
4
6
8
def f1(t): return t * 2
def f2(t): return t * t
def comb(t,f): return f(t)
print comb(10, f1)
print comb(10, f2)

Categories

Resources