Why is it considered 'powerful' to pass a function as a parameter? - python

I am reading through A Concise Introduction to Programming in Python by Mark J.Johnson and I stumbled upon a piece of code that uses darts to estimate the area under the graph. The code is working perfectly fine but I am getting confused as to why you would pass a function as a parameter if you could just call the function anyway.
from random import uniform
from math import exp
def area(function , a ,b ,m ,n = 1000 ): #changed parameter for better understanding
hits = 0
total_area = m * (b-a)
for i in range(n):
x = uniform(a,b)
y = uniform(0,m)
if y <= function(x):
hits += 1
frac = hits / float(n)
return frac * total_area
def f(x):
return exp(-x**2)
def g(x): #new function
return exp(-x**2) + 2
def main():
print area(f,0,2,1)
print area(g,0,2,1)
main()
He states that passing a function as a parameter is 'powerful' but I can't see why?

f is but one graph function. It is not the only function that you could define to create a graph.
You can also define other functions:
def g(x):
return 2 * x ** 2 + x + 5
and pass this into area() without having to alter that function. area() is generic enough to calculate the area of different graph functions, and all you need to do is pass in the graph function to have it calculate that area.
Had you hardcoded f instead of using a parameter, you could no longer do that.

I think the answer should be obvious, especially in this case: You can write a generic function for something like calculus integration that works on any function you pass in. You can modify the function you're integrating by supplying a new function. Likewise for other operations like graphing.

Related

How to find out which values a function f is called when using scipy.integrate.quad?

I have a function f(x) = 1/x^2 and I evaluate the integral from 0 to 1 using scipy.integrate.quad. scipy.integrate.quad is an adaptive integration routine and I would like to know in which regions of [0,1] is the function f evaluated. So, for which values of x is the function f called when making an estimate of the integral?
I'm thinking of using a global variable and appending the x values called to it to keep track of the which x values get used. However, I'm not too familiar on how to do this and any help would be greatly apprecaited, thanks.
The plan is to then plot a histogram to see what regions in the interval [0,1] are evaluated the most.
You could use a decorator class that saves each value x before evaluating the function:
class MemoizePoints:
def __init__(self, fun):
self.fun = fun
self.points = []
def __call__(self, x, *args):
self.points.append(x)
return self.fun(x, *args)
#MemoizePoints
def fun(x):
return 1 / x**2
quad(fun, a = 1e-6, b = 1.0)
Then, fun.points contains all the x values for which the function f was evaluated.
Here, the decorator #MemoizePoints is just syntactic sugar for
def fun(x):
return 1 / x**2
fun = MemoizePoints(fun)

How to package a sequence functions that act on parameter in order in Python

Imagine there are three functions, all them accept and return the same type args.
Normally, we can write it as fun3(fun2(fun1(args)), this can be say that a sequence function act on parameter in order, which likes one variety Higher-order functions "map".
You know in Mathematica, we can write this as fun3#fun2#fun1#args.
Now the question is that can we integrate fun3#fun2#fun1 as another fun without modifying their definition, so fun(args) can replace fun3(fun2(fun1(args)), this looks more elegant and concise.
def merge_steps(*fun_list):
def fun(arg):
result = arg
for f in fun_list:
result = f(result)
return result
return fun
def plus_one(arg):
return arg + 1
def double_it(arg):
return arg ** 2
def power_ten(arg):
return arg ** 10
combine1 = merge_steps(power_ten, plus_one, double_it)
combine2 = merge_steps(plus_one, power_ten, double_it)
combine1(3)
> 3486902500
or use lambda:
steps = [power_ten, plus_one, double_it]
reduce(lambda a, f: f(a), steps, 3)
> 3486902500
I think you can use Function Recursion in python to do this.
def function(args, times):
print(f"{times} Times - {args}")
if times > 0 :
function(args,times - 1)
function("test", 2)
Note: I just add times argument to not generate infinite loop.
I'm not certain I understand your question, but are you talking about function composition along these lines?
# Some single-argument functions to experiment with.
def double(x):
return 2 * x
def reciprocal(x):
return 1 / x
# Returns a new function that will execute multiple single-argument functions in order.
def compose(*funcs):
def g(x):
for f in funcs:
x = f(x)
return x
return g
# Demo.
double_recip_abs = compose(double, reciprocal, abs)
print(double_recip_abs(-2)) # 0.25
print(double_recip_abs(.1)) # 5.0

Evolving functions in python

Updated Question
Following from my original post, with the use of #Attack68 's code, I have created a program that successfully evolved the function with a choice of multiplicative functions based on a random variable. However, now I am receiving an error saying the list indices must be integers (even though I'm fairly sure they are), I'm not sure what has happened, The code is as follows:
import numpy as np
import scipy.integrate as integrate
x=np.linspace(0.0,1.0,100)
n=10 #iterations
d=700.0
def f(x):
return np.sin(x)
def g(x,list_):
return np.cos(x)*apply(x,list_)
base = [f, g]
list_ = list()
for i in range(n):
testvar=np.random.randint(1, 100, 1)
if testvar> 50 and i!=0:
func_idx = 0 # choose a random operation: 0=ten, 1=inv
else:
func_idx= 1
list_.append(func_idx)
# now you have a list of indexes referencing your base functions so you can apply them:
def apply(x,list_):
y = 1
for i in range(len(list_)):
y *= base[list_[i]](x)
return y
print(list_)
#testint=integrate.quad(apply(x,list_),-d,d)[0]
#print(testint)
print(apply(list_, x))
I am now getting the error:
TypeError: list indices must be integers or slices, not numpy.float64
I am also attempting to get this to integrate the new function after each iteration but it seems that the form of this function is not callable by scipys quad integrator, any suggestions on how to integrate the evolving function on each iteration would also be appreciated.
Original:
I am creating a simulation in python where I consider a function that evolves over a loop. This function starts off defined as:
def f(x):
return 1.0
So simply a flat distribution. After each iteration of the loop, I want the function to be redefined depending on certain (random) conditions. It could be multiplied by cos(b*x) or it could be multiplied by some function A(x), the evolution will not be the same each time due to the randomness, so I cannot simply multiply by the same value each time.
The progression in one instance could be:
f(x)----> f(x)*A(x)----> f(x)*A(x)*A(x)...
but in another instance it could be:
f(x)----> f(x)*A(x)----> f(x)*A(x)*cos(x)...
or
f(x)----> f(x)*cos(x)----> f(x)*cos(x)*cos(x)...
etc.
after each, of n iterations of this evolution, I have to compute an integral that is related to the function, so I need to essentially update the function after each iteration to be called by scipys quad integrator.
I have tried to use arrays to manipulate the distribution instead and it works as far as the function evolution goes, but upon integration, it gives the incorrect result with numpy.trapz and I cannot work out why. Sci-pys quad integrator is more accurate anyway and I had managed to get this to work previously for the first iteration only, but it requires a function based input, so without this function evolution I cannot use it.
If someone could show me if/how this function evolution is possible that'd be great. If it is not possible, perhaps someone could try to help me understand what numpy.trapz actually does so I can workout how to fix it?
How about this:
class MyFunction:
def __init__(self):
def f1(x):
return 1.0
self.functions = [f1]
def append_function(self, fn):
self.functions.append(fn)
def __call__(self, x):
product = 1.0
for f in self.functions:
product *= f(x)
return product
This object starts off as simply returning 1.0. Later you add more functions and it returns the product of all of them.
Your description suggests your iterated values are combined through a product and are not in fact a composition of functions. A simple way of recording these is to have a set of base functions:
import numpy as np
import scipy.integrate as int
def two(x):
return x*2
def inv(x):
return 1/x
base = [two, inv]
funcs = np.random.choice(base, size=10)
def apply(x, funcs):
y = 1
for func in funcs:
y *= func(x)
return y
print('function value at 1.5 ', apply(1.5, funcs))
answer = int.quad(apply, 1, 2, args=(funcs,))
print('integration over [1,2]: ', answer)

Python: addition of function in a for loop

Dear python Stackoverflow users,
I want to add gaussian functions in a loop as it can be done with integer with the += sign. But I have no idea how to reasign a function value within a loop.
I have tried something like:
def gaussian(x, mu, sig):
return np.exp(-np.power(x - mu, 2.) / (2 * np.power(sig, 2.)))
def f(x):
return 0
for i in xdata:
f(x) = f(x) + gaussian(x,i,20)
But I obtained the message "SyntaxError: can't assign to function call"
How could I make this work?
Thanks!
As per my comment:
Replace f(x) in your code by y and replace def f(x): return 0 by y = 0.
You replied:
Ok, it works! Then a function can be defined only with a letter, without mentioning the variable!
That’s not really what’s going on. You are not defining a function. Your desired use of f(x) is not a function. The function f you defined does only one thing:
def f (x):
return 0
The function will always return 0. Once the function is defined like that, it cannot be changed (without replacing it by another function).
What you want to do is collect a function result—but not the result of f but the result of gaussian. Or actually, you want to collect all the results of the gaussian function calls and sum them up.
So what you do is create a variable which you add your results to, which as such represents the sum of those function calls.
def gaussian(x, mu, sig):
return np.exp(-np.power(x - mu, 2.) / (2 * np.power(sig, 2.)))
y = sum(gaussian(x, i, 20) for i in xdata)
f(x) return a result, so it is as you say 0 = 12, for example... That will throw an error, because '=' is assignment operator, so at left of it, you should only have a variable (or a constant for first assignment...).
So maybe you wanted to do:
y = f(x) + gaussian(x,i,20)
But I don't want why you want to do a "+=", because it is only for variables... And your function always returns 0. So you might just say, instead of
def f(x):
return 0
simply
y = 0
and then, tell after in the loop
y += gaussian(x, i, 20).
Or if you want to build a curve, you can use an array.
There is also an other error:
x IS NOT DEFINED in your loop (defined in two functions, but only as local vars)

How to obtain a math function as an output in python

I want to do something like this
def gaussian(x, amp):
return amp * exp(-(x-cen)**2 /wid)
I want to substitute just amp and x and obtain an equation as output
for example:
gaussian(1,3)
3 * exp(-(1-cen)**2 /wid) as output.
Can I do this for a couple of lists, in one several values of amplitude an in the other their respective x's
I am not sure what you mean by "I need an equation". Do you need something you can evaluate? Then probably you can return a lambda object, and then you can evaluate that. Or you can use closure something like:
import math
def gaussian(x, amp):
def _gauss( cen,wid):
return amp * math.exp(-(x-cen)**2 /wid)
return _gauss
g = gaussian(10,1)
print g(2,4)
g now is a callable function where x and amp has been replaced so you need to pass only cen and wid
The reason why this work is because the internal function, _gauss, gets evaluated every time you call the wrapper function, doing so the function will be evaluated using the argument passed by the parent function and be used there as "static". Since then you return a function you can evaluate that and pass all the params left, this is a common technique for when a library forces you to have parameterlles callbacks.
Only draw back is more expensive then a simple function call, that is to generate the child function, not to evaluate it.
I would convert your return to a string:
def gaussian(x, amp):
return str(amp) + '* exp(-(' + str(x) + '-cen)**2 /wid)'
This should return the value you want:
gaussian(1,3)
returns
'3 * exp(-(1-cen)**2 /wid)'

Categories

Resources