How to take a function as an argument? (Python) - python

I'm writing a program to calculate the volume of a solid of rotation. The first step of this is to calculate an integral. I'm using scipy.integrate for this, but I can't figure out the best way to have a equation (like x=x**2 input at the command line. I was originally planning on adding an argument 'with respect to: x|y' and then taking the function as a lambda. Unfortunately, argparse won't take lambda as an argument type, and trying to use a string to construct a lambda (f = lambda x: args.equation) just returns a string (understandably really).
Here's what I've got so far:
import sys
import argparse
import math
from scipy import integrate
parser = argparse.ArgumentParser(description='Find the volume of the solid of rotation defined')
parser.add_argument('equation', help='continous function')
parser.add_argument('a', type=float, help='bound \'a\'')
parser.add_argument('b', type=float, help='bound \'b\'')
parser.add_argument('-axis', metavar='x|y', help='axis of revolution')
args = parser.parse_args()
def volume(func, a, b, axis=None):
integral = integrate.quad(func, a, b)
return scipy.py * integral
print volume(args.equation, args.a, args.b)
Any advice will be appreciated
thanks

If there are absolutely no concerns about security risks from letting the user run arbitrary Python code, then you can use eval to create a callable object:
volume(eval('lambda x: %s' % args.equation), args.a, args.b)

You should be able to use eval() on the string you get from your arguments:
>>> f = eval("lambda x: x**2")
>>> f(5)
25

Related

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 :)

Getting mathematical function as user's input

I need to know how transfer string input into executable function.
For example - user write string 'x*Sin(x**2)' and then programm takes it as function, can calculate a value for given x, can plot derivation of this function etc. I've read that there is module called scitools.stringfunction, but as far as I know this module is not callable in python-3.
Any ideas how to make it?
For Python 2.X
f = lambda x: input() # the user inputs: x**2 + 1
y = f(3)
print y # outputs: 10
For Python 3.X
f = lambda x: eval(input())
y = f(5)
print y
Just make sure to import the required mathematical functions. And make sure the user inputs a valid Python arithmetic expression.
using sympy you could do something like this:
from sympy import var
from sympy import sympify
x = var('x') # the possible variable names must be known beforehand...
user_input = 'x * sin(x**2)'
expr = sympify(user_input)
res = expr.subs(x, 3.14)
print(res) # -1.322...
if you want to turn the user input into a function you can call you could to this:
from sympy.utilities.lambdify import lambdify
f = lambdify(x, expr)
# f(3.14) -> -1.322...
sympy can do sybolic calculations (including derivatives); if you want to make plots i strongly suggest matplotlib.
the advantage of using a math library opposed to eval is that you do not need to sanitize the user input (against malicious code).
(deleted this thanks to a comment from ejm).

Python function as an argument for a R function using rpy2

I wrote a function in Python 2.7:
# Python #
def function_py(par):
#something happens
return(value)
and I want to use this function as an argument for another function in R. More precisely, I want to perform to compute the Sobol' indices using the following function:
# R #
library('sensitivity')
sobol(function_py_translated, X1,X2)
where function_py_translated would b the R equivalent of function_py.
I'm trying to use the rpy2 module, and for a simple function, I could make a working case:
import rpy2.rinterface as ri
import rpy2.robjects.numpy2ri
sensitivity = importr('sensitivity')
radd = ri.baseenv.get('+')
def costfun(X):
a = X[0]
b = X[1]
return(radd(a,b))
costfunr=ri.rternalize(costfun)
X1 = robjects.r('data.frame(matrix(rnorm(2*1000), nrow = 1000))')
X2 = robjects.r('data.frame(matrix(rnorm(2*1000), nrow = 1000))')
sobinde = sensitivity.sobol(costfunr,X1,X2)
print(sobinde.__getitem__(11))
The main problem is that I had to redefine the "+". Is there a way to work around this ? Being able to pass an arbitrary function without prior transformation ? The function I want to analyze is much more complicated.
Thank you very much for your time

ValueError: need more than 3 values to unpack when using optimize.minimize

I'm pretty new to python and I got stuck on this:
I'd like to use scipy.optimize.minimize to maximize a function and I'm having some problem with the extra arguments of the function I defined.
I looked for a solution in tons of answered questions but I can't find anything that solves my problem.
I saw in Structure of inputs to scipy minimize function how to pass extra arguments that one wants to be constant in the minimization of the function and my code seems fine to me from this point of view.
This is my code:
import numpy as np
from scipy.stats import pearsonr
import scipy.optimize as optimize
def min_pears_function(a,exp):
(b,c,d,e)=a
return (1-(pearsonr(b + exp[0] * c + exp[1] * d + exp[2],e)[0]))
a = (log_x,log_y,log_t,log_z) # where log_x, log_y, log_t and log_z are numpy arrays with same length
guess_PF=[0.6,2.0,0.2]
res = optimize.minimize(min_pears_function, guess_PF, args=(a,), options={'xtol': 1e-8, 'disp': True})
When running the code I get the following error:
ValueError: need more than 3 values to unpack
But I can't see what needed argument I'm missing. The function seems to work fine, so I guess the problem is in optimize.minimize call?
Your error occurs here:
def min_pears_function(a,exp):
# XXX: This is your error line
(b,c,d,e)=a
return (1-(pearsonr(b + exp[0] * c + exp[1] * d + exp[2],e)[0]))
This is because:
the initial value you pass to optimize.minimize is guessPF which has just three values ([0.6,2.0,0.2]).
this initial value is passed to min_pears_function as the variable a.
Did you mean for it to be passed as exp? Is it exp you wish to solve for? In that case, redefine the signature as:
def min_pears_function(exp, a):
...

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.

Categories

Resources