I am setting up a constrained optimization problem in Python 3 using Scipy. The following code is a simplified version of the code where I am having trouble, but which reproduces the bug I am trying to fix.
For the purposes of the code snippet, one can forget everything about constrained optimization and Scipy. Just that the list P
corresponds to the flattened list of xy coordinates of a polygonal chain of 5 links (shown for example in the figure above, not necessarily drawn to scale)
The list comprehension for cons_length
is intended to create a list of functions that computes the length of the i
th link in the chain i.e. a list
of functions parametrized by the link number.
import numpy as np
# number of links in the polygonal chain
N = 5
# Flattened list of xy coordinates of the vertices of a chain of N links
# (the length of such a flattened list is 2*N+2), in this case 12.
P = [1 ,1, \
2 ,3, \
5 ,8, \
13 ,21, \
34 ,55, \
89 ,144]
# extract the x coordinate of the ith point
def x(i):
return lambda P: P[2*i]
# extract the y coordinate of the ith point
def y(i):
return lambda P: P[2*i+1]
# get length of the ith link in the chain
def length(i):
def f(P):
linkvec = np.asarray([ x(i+1)(P) - x(i)(P), \
y(i+1)(P) - y(i)(P)])
result = np.linalg.norm(linkvec)
print("---> Calculating length of link: ", i, " :: ", result)
return result
return f
# A list of functions, that should ostensibly compute the length of the ith
# link in the polygonal chain represented by `P`
cons_length = [ lambda P: length(idx)(P) for idx in range(N) ]
# For each function in cons_length check what it evaluates to.
# Why is each for loop evaluating to the same result?
for i in range(len(cons_length)):
print("ival: ", i, "link-length: ", cons_length[i](P), "\n" )
On running the code, I get a baffling output as shown the following screenshot:
Why are the results of each for
loop the same?! This question is in some sense a follow-up to a question along a similar theme I has asked yesterday here: How to create a set of parametrized functions in python?
But I believe the code above takes care of the binding issues mentioned there and in particular uses the suggestion in one of the comments by juanpa.arrivillaga
The correct way to do this is to write a factory function, basicalyl, you need to create an enclosing scope that where that variable isn't being changed. I prefer something like
def func_maker(idx): def f(x): return x + idx; return f
(of course, with correct indenation) and then usefns.append(func_maker(idx))
in a loop.
So where am I going wrong?