Generating a 'sequence of dicts' for scipy.optimize.minimize

Question:

I’m using scipy.optimize.minimize with constraints. The example in the documentation ( at http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.optimize.minimize.html ) uses for constraints:

cons = ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + 2},
        {'type': 'ineq', 'fun': lambda x: -x[0] - 2 * x[1] + 6},
        {'type': 'ineq', 'fun': lambda x: -x[0] + 2 * x[1] + 2})

I’d like to do something similar but generate the elements of this sequence in a loop, but I’m having trouble.

I’ve tried treating cons as a tuple, an example of the same form as above:

cons = (,)
for i in range(4):
    cons += ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i})

but I get TypeError: unsupported operand type(s) for +=: 'dict' and 'dict'.

I also tried using str and eval:

cons = (str({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1]}))
for i in range(3):
    cons += (str({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i + 1}))
cons = eval(cons)

but that didn’t work either, I get something like

cons = eval(cons)
  File "<string>", line 1
    {'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}{'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}{'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}{'fun': <function <lambda> at 0x062964F0>, 'type': 'eq'}
            ^
SyntaxError: invalid syntax
Asked By: JGM272

||

Answers:

Instead of adding a dict (parentheses are not doing anything here):

for i in range(4):
    cons += ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i})

add a tuple, adding a comma after the dict:

for i in range(4):
    cons += ({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i},)
>>> cons
({'fun': <function <lambda> at 0xb746de2c>, 'type': 'ineq'}, {'fun': <function <lambda> at 0xb747656c>, 'type': 'ineq'}, {'fun': <function <lambda> at 0xb74765a4>, 'type': 'ineq'}, {'fun': <function <lambda> at 0xb74765dc>, 'type': 'ineq'})

Note that as @Jivan indicates in its answer, you should use lists for this kind of job.

Answered By: fredtantini

Tuples are immutable.

Once you have created a tuple, you cannot modify the number of the elements in there, or their order.

You can do that way:

cons = []
for i in range(4):
    cons.append({'type': 'ineq', 'fun': lambda x:  x[0] - 2 * x[1] + i})

Which will give you a list of dictionaries. Once your list is done, if you want to change it into a tuple, you can do:

cons = tuple(cons)

The result:

>>> cons
({'fun': <function <lambda> at 0x106e2cb18>, 'type': 'ineq'},
    {'fun': <function <lambda> at 0x106e2cf50>, 'type': 'ineq'},
    {'fun': <function <lambda> at 0x106e335f0>, 'type': 'ineq'},
    {'fun': <function <lambda> at 0x106e33cf8>, 'type': 'ineq'})

Note that Raymond Hettinger himself said not so long ago:

Generally, lists are for looping; tuples for structs. Lists are
homogeneous; tuples heterogeneous.Lists for variable length.

So in your case, you might want to keep the list unless a tuple is needed by some third-part module.

Answered By: Jivan

This is an older question, but both of the proposed solutions seem wrong. Or am I missing something?

def generate_constraints_wrong(n_params):
    cons = []
    for i in range(n_params):
        cons.append({'type': 'ineq', 'fun': lambda x:  x[i]})
    return tuple(cons)

def generate_constraints_wrong2(n_params):
    cons = tuple()
    for i in range(n_params):
        cons += ({'type': 'ineq', 'fun': lambda x:  x[i]},)
    return cons

def generate_constraints_right(n_params):
    # let's create a function generator that uses closure to pass i to the generated function
    def wrapper_fun(x, i):
        def fun(x):
            return x[i]
        return fun
    cons = []
    for i in range(n_params):
        f = wrapper_fun(x, i)
        cons.append({'type': 'ineq', 'fun': f})
    return tuple(cons)

# verify the generated functions
n_params = 3
x = [1,10, 100]
cons1 = generate_constraints_wrong(n_params)
cons2 = generate_constraints_wrong2(n_params)
cons3 = generate_constraints_right(n_params)
print(cons1[0]['fun'](x)) # this should be 1 but instead we end up modifying all of our lambda objects to have the last i
print(cons1[1]['fun'](x))
print(cons2[0]['fun'](x))
print(cons2[1]['fun'](x))
print(cons3[0]['fun'](x))
print(cons3[1]['fun'](x))

prints:

100
100
100
100
1
10

The problem is that the closure of each lambda points to the object i, rather than to the value the object i had at a particular loop iteration. For more details see:
Python lambda closure scoping

Answered By: means-to-meaning
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.