Evolving functions in python - 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)

Related

Find a function maximum with scipy.minimize

I´ve been asigned to find a given fuction´s maximum with scipy.minimize with the BFGS method using lambda. I´ve already figured out that in order to do that, I need to minimize the -(f) function, but I cannot change the function itself, only the way it´s called in the minimize. Also, the asnwer must be a float.
A abd B are the two functions to maximize
Thanks in advance for the help!!
def A(x):
return -(x-1)**2
B = lambda x: -(x+2)**4
#This is where the minimize is called
def argmax(f):
from scipy.optimize import minimize
return
Since the A function must be sign inverted, create another function that calls -A(x), for example:
from scipy.optimize import minimize
res = minimize(lambda t: -A(t), 0)
print(res.x[0])
which prints
0.9999999925496535
Similarly B must be flipped so that minimize finds the argmax of B:
res = minimize(lambda t: -A(t), 0)
print(res.x[0])
which prints
-1.987949853410927
Both of these answers are close to correct (expecting 1 and -2), although the result for B has fairly large error of 0.012.
The one-liner form for both maximizations (via minimizations) would be
return minimize(lambda t: -A(t), 0).x[0]
(and similarly for B)
To minimize both functions at the same time, create a function taking a vector:
return minimize(lambda v: -A(v[0])-B(v[1]), [0, 0]).x
which returns:
array([ 0.99999975, -1.98841637])

Why my function (f(x)=x*2) doesn't returns anything?

I know that there are way simpler ways to calculate the square of a number and store it in an array, but for the sake of another problem. I need to understand why nothing happens in this code and its structure (is the return(a) necessary ?):
s=[1,2,3,4,5]
def square(x):
return x*x
def iterate(b):
sol=[]
for b in s:
a=square(b)
return(a)
sol.append(a)
print(sol)
The goal is to store the square in sol : sol = [1,4,9,16,25]. But the code runs without printing anything. What make the following code work and not the previous one ?
s=[1,2,3,4,5]
def square(x):
return x*x
sol=[]
for b in s:
a=square(b)
sol.append(a)
print(sol)
(My problem involves curve fitting, and this structure doesnt fit my needs)
The problem is that you define iterate within square but you never call iterate. It would be better to have iterate be a separate function that calls square:
values = [1,2,3,4,5] # do not call your variable set - it is a Python keyword
def square(x):
return x*x
def iterate(values):
solution = []
for value in values:
value_squared = square(value)
solution.append(value_squared)
return solution
You could also do this without defining iterate using a list comprehension:
[square(value) for value in values]
Edit:
To answer your other questions, here is your code:
s=[1,2,3,4,5]
def square(x):
return x*x
def iterate(b):
sol=[]
for b in s:
a=square(b)
return(a)
sol.append(a)
print(sol)
In square, you never call iterate so this part of the code never runs.
If you add a call to iterate within square, you will end up in an infinite loop. This is because within iterate you call square, but you always iterate over your list s. This means that inside iterate, square(b) will always be square(1).
Within iterate you use the global variable s but it would be better to restructure your code so that you take s as input.
If you are learning about inner functions, you could define iterate and within this define square:
values = [1,2,3,4,5]
def iterate(values):
def _square(x):
return x*x
solution = []
for value in values:
value_squared = _square(value)
solution.append(value_squared)
return solution

Python- np.random.choice

I am using the numpy.random.choice module to generate an 'array' of choices based on an array of functions:
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base=[f, g]
funcs=np.random.choice(base,size=2)
This code will produce an 'array' of 2 items referencing a function from the base array.
The reason for this post is, I have printed the outcome of funcs and recieved:
[<function f at 0x00000225AC94F0D0> <function f at 0x00000225AC94F0D0>]
Clearly this returns a reference to the functions in some form, not that I understand what that form is or how to manipulate it, this is where the problem comes in. I want to change the choice of function, so that it is no longer random and instead depends on some conditions, so it might be:
for i in range(2):
if testvar=='true':
choice[i] = 0
if testvar== 'false':
choice[i] = 1
This would return an array of indicies to be put in later function
The problem is, the further operations of the code (I think) require this previous form of function reference: [ ] as an input, instead of a simple array of 0,1 Indicies and I don't know how I can get an array of form [ ] by using if statements.
I could be completely wrong about the rest of the code requiring this input, but I don't know how I can amend it, so am hence posting it here. The full code is as follows: (it is a slight variation of code provided by #Attack68 on Evolving functions in python) It aims to store a function that is multiplied by a random function on each iteration and integrates accordingly. (I have put a comment on the code above the function that is causing the problem)
import numpy as np
import scipy.integrate as int
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base = [f, g]
funcs = np.random.choice(base, size=2)
print(funcs)
#The below function is where I believe the [<function...>] input to be required
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)
Here is my attempt of implementing a non-random event:
import numpy as np
import scipy.integrate as int
import random
def f(x):
return np.sin(x)
def g(x):
return np.cos(x)
base = [f, g]
funcs = list()
for i in range(2):
testvar=random.randint(0,100) #In my actual code, this would not be random but dependent on some other situation I have not accounted for here
if testvar>50:
func_idx = 0 # choose a np.random operation: 0=f, 1=g
else:
func_idx= 1
funcs.append(func_idx)
#funcs = np.random.choice(base, size=10)
print(funcs)
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)
This returns the following error:
TypeError: 'int' object is not callable
If: You are trying to refactor your original code that operates on a list of randomly chosen functions to a version that operates with random indices which correspond to items in a list of functions. Refactor apply.
def apply(x,indices,base=base):
y = 1
for i in indices:
f = base[i]
y *= f(x)
return y
...this returns a reference to the functions in some form, not that I understand what that form is or how to manipulate it...
Functions are objects, the list contains a reference to the objects themselves. They can be used by either assigning them to a name then calling them or indexing the list and calling the object:
>>> def f():
... return 'f'
>>> def g():
... return 'g'
>>> a = [f,g]
>>> q = a[0]
>>> q()
'f'
>>> a[1]()
'g'
>>> for thing in a:
print(thing())
f
g
Or you can pass them around:
>>> def h(thing):
... return thing()
>>> h(a[1])
'g'
>>>
If you still want to use your function apply as-is, you need to keep your input a list of functions. Instead of providing a list of indices, you can use those indices to create your list of functions.
Instead of apply(1.5, funcs), try:
apply(1.5, [base(n) for n in funcs])

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

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.

scipy.optimize with matrix constraint

How do I tell fmin_cobyla about a matrix constraint Ax-b >= 0? It won't take it as a vector constraint:
cons = lambda x: dot(A,x)-b
thanks.
Since the constraint must return a scalar value, you could dynamically define the scalar constraints like this:
constraints = []
for i in range(len(A)):
def f(x, i = i):
return np.dot(A[i],x)-b[i]
constraints.append(f)
For example, if we lightly modify the example from the docs,
def objective(x):
return x[0]*x[1]
A = np.array([(1,2),(3,4)])
b = np.array([1,1])
constraints = []
for i in range(len(A)):
def f(x, i = i):
return np.dot(A[i],x)-b[i]
constraints.append(f)
def constr1(x):
return 1 - (x[0]**2 + x[1]**2)
def constr2(x):
return x[1]
x = optimize.fmin_cobyla(objective, [0.0, 0.1], constraints+[constr1, constr2],
rhoend = 1e-7)
print(x)
yields
[-0.6 0.8]
PS. Thanks to #seberg for pointing out an earlier mistake.
Actually the documentation says Constraint functions;, it simply expects a list of functions each returning only a single value.
So if you want to do it all in one, maybe just modify the plain python code of the fmin_cobyla, you will find there that it defines a wrapping function around your functions, so it is easy... And the python code is really very short anyways, just small wrapper around scipy.optimize._cobyal.minimize.
On a side note, if the function you are optimizing is linear (or quadratic) like your constraints, there are probably much better solvers out there.

Categories

Resources