Python recursive function definition storage issue - python

I am trying to get a list of functions defined recursively by storing each step of a math serie:
x^0+x^1+x^2+x^3+... → f0(x)=1, f1(x)=1+x, f2(x)=1+x+x², f3(x)=1+x+x²+x³, ...
I coded this in python:
n = 3
devs = [lambda x: 1]
for k in range(n):
devs.append(lambda x, f=devs[k]: f(x) + x**(k+1))
print(devs[-1](4)) # x=4
I learned in this answer that you need to pass the previous function as default argument in order to solve the maximum recursion depth error, but this outputs 193.
I think it has to do with the k in the default parameter. It seems to calculate: 1+4^3+4^3+4^3=193, or more accurately 1+n*x^n instead of x^0+x^1+...+x^n
Can you please tell me what am I doing wrong here?

I think the same question you linked has the answer to your problem. Because the k inside the function is evaluated only outside the loop, it will have the last value from the loop. You can check the documention for how it works in depth.
The correct code is
n = 4
devs = [lambda x: 1]
for k in range(n):
devs.append(lambda x, f=devs[k], j=k: f(x) + x**(j+1))

One liner just for fun!
n=10
devs = (lambda f:[f] + [f := (lambda x, a=k, g=f: g(x) + x**(a+1)) for k in range(n)])(lambda x:1)

Related

Iterating constraints with scipy.minimize

I am trying to minimize a function with 2 variables x[0],x[1]. A, B, and C are dataframes with dimensions 10x10. The optimization works as intended when I don't use constraints, however I also care for the constrainted case. For the constrainted case, I want
A.iloc[i,j]*x[0]*B.iloc[i,j]*x[1]*C.iloc[i,j]
to be equal to or greater than zero for all combinations of i and j. To achieve this, I have defined constraints in the following way:
cons=[]
def f(a):
def g(x):
return A.iloc[i,j]*x[0]*B.iloc[i,j]*x[1]*C.iloc[i,j]
return g
for i in range (10):
for j in range(10):
cons.append({'type':'ineq', 'fun': f(t)})
While I am getting the right number of constraints (i.e. len(cons) = 100), the optimization results do not satisfy the constraints that I had in mind, meaning it results in values for x[0], x[1] and x[2] for which
A.iloc[i,j]*x[0]*B.iloc[i,j]*x[1]*C.iloc[i,j]
is smaller than zero for many j,i. I have ascertained that result.success = True, so the optimization suddenly stopping can be ruled out as a potential problem. While looking for a solution to this problem, I have found this case of someone trying to iterate constraints in scipy aswell, but they only iterated over one range rather than over two and I was not able to modify their solution to work for my case.
Your function f doesn't make any sense since the function g doesn't depend on a. If you really want f to return a new function depending on the indices i and t, f should be a function of the two indices:
cons=[]
def f(t, i):
def g(x):
return A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33
return g
for t in range (72):
for i in range(33):
cons.append({'type':'ineq', 'fun': f(t, i)})
Note also that you could easily define the constraint functions on the fly thanks to lambda expressions:
cons = []
for t in range(72):
for i in range(33):
cons.append({'type': 'ineq', 'fun': lambda x, t=t, i=i: A.iloc[t,i]+(x[0]*B.iloc[t,i]+x[1]*C.iloc[t,i]+x[2]*D.iloc[t,i])/33})

Making a for-loop that uses a function multiple times

I have a function mul(f,g)
Can anyone show me how I can make a forloop that uses mul(f,g) multiple times?
For example f=(x+1)^3 becomes mul(f,mul(f,f))
Thanks in advance!
As a for loop #Julia is right an storing the value external to the loop if the right approach:
lst = [1, 2, 3]
product = lst[0]
for n in lst:
product = mult(n, product)
However there are a few other alternatives that I want to point out, that could be useful in more complex situations. First there is concept called recursion which is helpful in many cases where you need to call the same function multiple times on or within the same data structure (in this case a list):
def pow(n, p):
"""Raise n to a power p"""
if p == 0:
return n
return n * pow(n, p)
lst = [1, 2, 3]
You may also use a function from functools to reduce a list into a single value:
import functools
lst = [1, 2, 3]
product = functools.reduce(lambda n, m: n* m, lst)
Interesting exercise. Since the mul() function keeps calling itself, this is, by definition, recursion. However, it is not the way recursion is usually done, where the base case determines the depth of recursion. Here, the depth of recursion is determined by an external for loop. Here is one way to do it:
def f(x):
return x + 1
def mul(f,g):
return(f ** g)
#To calculate (x + 1)^y where x=2 and y=3 , you write the following for loop:
x = 2
y = 3
for g in range(1, y+1):
g = mul(f(x),g)
print(g) #27
#To calculate (x + 1)^y where x=4 and y=2, you write the following for loop:
x = 4
y = 2
for g in range(1, y+1):
g = mul(f(x),g)
print(g) #25

Minimize function with scipy

I'm trying to do one task, but I just can't figure it out.
This is my function:
1/(x**1/n) + 1/(y**1/n) + 1/(z**1/n) - 1
I want that sum to be as close to 1 as possible.
And these are my input variables (x,y,z):
test = np.array([1.42, 5.29, 7.75])
So n is the only decision variable.
To summarize:
I have a situation like this right now:
1/(1.42**1/1) + 1/(5.29**1/1) + 1/(7.75**1/1) = 1.02229
And I want to get the following:
1/(1.42^(1/0.972782944446024)) + 1/(5.29^(1/0.972782944446024)) + 1/(7.75^(1/0.972782944446024)) = 0.999625
So far I have roughly nothing, and any help is welcome.
import numpy as np
from scipy.optimize import minimize
def objectiv(xyz):
x = xyz[0]
y = xyz[1]
z = xyz[2]
n = 1
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))
test = np.array([1.42, 5.29, 7.75])
print(objectiv(test))
OUTPUT: 1.0222935270013889
How to properly define a constraint?
def conconstraint(xyz):
x = xyz[0]
y = xyz[1]
z = xyz[2]
n = 1
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n)) - 1
And it is not at all clear to me how and what to do with n?
EDIT
I managed to do the following:
def objective(n,*args):
x = odds[0]
y = odds[1]
z = odds[2]
return abs((1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))) - 1)
odds = [1.42,5.29,7.75]
solve = minimize(objective,1.0,args=(odds))
And my output:
fun: -0.9999999931706812
x: array([0.01864994])
And really when put in the formula:
(1/(1.42^(1/0.01864994)) + 1/(5.29^(1/0.01864994)) + 1/(7.75^(1/0.01864994))) -1 = -0.999999993171
Unfortunately I need a positive 1 and I have no idea what to change.
We want to find n that gets our result for a fixed x, y, and z as close as possible to 1. minimize tries to get the lowest possible value for something, without negative bound; -3 is better than -2, and so on.
So what we actually want is called least-squares optimization. Similar idea, though. This documentation is a bit hard to understand, so I'll try to clarify:
All these optimization functions have a common design where you pass in a callable that takes at least one parameter, the one you want to optimize for (in your case, n). Then you can have it take more parameters, whose values will be fixed according to what you pass in.
In your case, you want to be able to solve the optimization problem for different values of x, y and z. So you make your callback accept n, x, y, and z, and pass the x, y, and z values to use when you call scipy.optimize.least_squares. You pass these using the args keyword argument (notice that it is not *args). We can also supply an initial guess of 1 for the n value, which the algorithm will refine.
The rest is customization that is not relevant for our purposes.
So, first let us make the callback:
def objective(n, x, y, z):
return 1/(x**(1/n)) + 1/(y**(1/n)) + 1/(z**(1/n))
Now our call looks like:
best_n = least_squares(objective, 1.0, args=np.array([1.42, 5.29, 7.75]))
(You can call minimize the same way, and it will instead look for an n value to make the objective function return as low a value as possible. If I am thinking clearly: the guess for n should trend towards zero, making the denominators increase without bound, making the sum of the reciprocals go towards zero; negative values are not possible. However, it will stop when it gets close to zero, according to the default values for ftol, xtol and gtol. To understand this part properly is beyond the scope of this answer; please try on math.stackexchange.com.)

Sum of squares using lambda functions

I am thinking of different ways to take the sum of squares in python. I have found that the following works using list comprehensions:
def sum_of_squares(n):
return sum(x ** 2 for x in range(1, n))
But, when using lambda functions, the following does not compute:
def sum_of_squares_lambda(n):
return reduce(lambda x, y: x**2 + y**2, range(1, n))
Why is this?
Think about what reduce does. It takes the output of one call and uses it as the first argument when calling the same function again. So imagine n is 4. Suppose you call your lambda f. Then you are doing f(f(1, 2), 3). That is equivalent to:
(1**2 + 2**2)**2 + 3**2
Because the first argument to your lambda is squared, your first sum of squares will be squared again on the next call, and then that sum will be squared again on the next, and so on.
You're only supposed to square each successive element. x**2 + y**2 squares the running total (x) as well as each successive element (y). Change that to x + y**2 and you'll get the correct result. Note that, as mentioned in comments, this requires a proper initial value as well, so you should pass 0 as the optional third argument.
>>> sum(x ** 2 for x in range(5,15))
985
>>> reduce(lambda x, y: x + y**2, range(5,15))
965
>>> reduce(lambda x, y: x + y**2, range(5,15), 0)
985
Suppose you plug in 4:
1^2 + 2^2 = 5 (you then take this result and put it back in the lambda)
5^2 + 3^2 = 34 (for reference the correct answer is 14)
It squares the "previous" sum as well in the lambda.
(You also have an off by one issue, it should be n+1 in your range calls.)
(for python 3 users)
from functools import reduce
To be able to use reduce.
Second, your function sum_of_squares_lambda won't work the way you expect it to because reduce will take twice (instead of once) every argument except the first and the last one.

Python lambda function

What is happening here?
reduce(lambda x,y: x+y, [x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0])
I understand how x is iterating through all of the numbers from 1 to 999 and taking out those that are divisible by 3 or 5, but the 'lambda x,y: x+y' part is stumping me.
This is bad Python for
sum(x for x in range(1,1000) if x % 3 == 0 or x % 5 == 0)
It simply sums all numbers in the range 1..999 divisible by 3 or 5.
reduce() applies the given function to the first two items of the iterable, then to the result and the next item of the iterable, and so on. In this example, the function
lambda x, y: x + y
simply adds its operands.
saying
f = lambda x, y : x + y
is almost the same as saying
def f(x, y):
return x + y
in other words lambda returns a function that given the parameters to the left of the : sign will return the value of the expression on the right of it.
In respect to a function is however quite limited, for example allows only one expression and no statements are allowed. This is not a serious problem however because in Python you can define a full function even in the middle of another function and pass that instead.
The usage you shown is however quite bad bacause a lambda there is pointless... Python would allow to compute that sum directly instead of using reduce.
Also, by the way, for the result of that computation there is an easy closed-form solution that doesn't require any iteration at all... (hint: the sum of all numbers from 1 to n is n*(n+1)/2 and the sum of all multiples of 5 from 5 to n is 5*(sum of all numbers from 1 to n/5) ... therefore ...)
A lambda designates an anonymous function. The syntax lambda x,y: x+y can be stated in English as "declare a nameless function taking two parameters named x and y. Perform the operation x+y. The return value of this nameless function will by the result of this operation"
reduce applies some function sequentially to the first two elements of a supplied list, then to the result of that function and the third element, and so on. Therefore, the lambda in the supplied code is used by reduce to add together the elements of the supplied list, which will contain all of the multiples of 3 and 5 less than 1000.

Categories