Добавление нескольких ограничений для минимизации и автоматического создания списка словарей ограничений?

Есть ли способ автоматически создать список словаря с несколькими ограничениями в scipy.minimize? Когда я использую следующий код (где ограничение списка представляет собой список многомерных многочленов мудреца по одному и тому же кольцу)

cons = [{'type': 'eq', 'fun': lambda s: ((constraint[0])(*s))},
        {'type': 'eq', 'fun': lambda s: ((constraint[1])(*s))},
        {'type': 'eq', 'fun': lambda s: ((constraint[2])(*s))},
        {'type': 'eq', 'fun': lambda s: ((constraint[3])(*s))}]

y0 = [.5 for xx in x]
bnds = tuple([(0.0, 1.0) for xx in x])
ssoln = scipy.optimize.minimize(HH, y0, jac=dHH, method='SLSQP', bounds=bnds, constraints=cons)
print ssoln

Мой результат

status: 0
 success: True
    njev: 14
    nfev: 22
     fun: -2.2669026273652237
       x: array([ 0.034829615490635,  0.933405952554424,  0.93340765416238 ,
        0.093323548109654,  0.335713397575351,  0.413107862378296])
 message: 'Optimization terminated successfully.'
     jac: array([-3.321836605297572,  2.640225014918886,  2.640252390205999,
       -2.273713195767229, -0.682455873949375, -0.351132324172705,  0.               ])
     nit: 14

Однако, если я попытаюсь создать минусы

cons=[]
for ii in range(len(constraint)):
    cons.append({'type': 'eq', 'fun': lambda s:  ((constraint[ii])(*s))})

минимизировать неудачи с

status: 6
 success: False
    njev: 1
    nfev: 1
     fun: -4.1588830833596715
       x: array([ 0.5,  0.5,  0.5,  0.5,  0.5,  0.5])
 message: 'Singular matrix C in LSQ subproblem'
     jac: array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.])
     nit: 1

Мой список, ограничение, многочленов мудреца может меняться по длине, количеству многочленов от проблемы к проблеме, и я не хочу, чтобы мне приходилось жестко кодировать список cons dict, который был впервые приведен выше для каждой проблемы. Есть ли способ автоматизировать?

Следующие работы работают, но я понимаю, что это не лучшая практика для оценки строк

str1='{\'type\': \'eq\', \'fun\': lambda s: ((constraint['
str2='])(*s))},'
mystr='['
for ii in range(len(constraint)):
    mystr=mystr+str1+str(ii)+str2
mystr=mystr+']'
cons = eval(mystr)

person Stephen Kauffman    schedule 26.12.2014    source источник
comment
Я сам недавно столкнулся с этой проблемой. Вы когда-нибудь находили решение, которое не предполагало оценки строки?   -  person Ryan    schedule 04.11.2017


Ответы (1)


Проблема в вашем цикле. Оператор lambda выполняет так называемое ленивое вычисление. В конце вашего цикла лямбда cons выполняет функцию для последнего значения ii, а не для каждого индекса.

Чтобы выполнить строгую оценку, вы можете использовать объект partial из модуля python functools (в обоих python 2 или python 3).

Чтобы проиллюстрировать, с lambda:

constraint = (lambda x: x, lambda x: x**2, lambda x: x**3, lambda x: x**4)

cons=[]
for ii in range(len(constraint)):
    # lambda s will evaluate the last value of ii
    cons.append({'type': 'eq', 'fun': lambda s: ((constraint[ii])(s))})

print([i['fun'](2) for i in cons])
# The value of ii is 3, so it will always call lambda x: x**4
>> [16, 16, 16, 16]

from functools import partial

def f_constraint(s, index):
    return constraint[index](s)

cons=[]
for ii in range(len(constraint)):
    # the value of ii is set in each loop
    cons.append({'type': 'eq', 'fun': partial(f_constraint, index=ii)})

print([i['fun'](2) for i in cons])
>> [2, 4, 8, 16]

Замена s на * s в соответствии с вашим определением:

from functools import partial

def f_constraint(s, index):
    return constraint[index](*s)

cons=[]
for ii in range(len(constraint)):
    cons.append({'type': 'eq', 'fun': partial(f_constraint, index=ii)})

Надеюсь, поможет!

person Mstaino    schedule 02.11.2018