Оптимизация Python с использованием sympy lambdify и scipy

Я пытаюсь максимизировать функцию, определенную sympy, но не могу заставить ее работать. Основная идея может быть резюмирована следующим образом:

    import sympy
    from scipy.optimize import minimize
    from sympy.utilities.lambdify import lambdify

    a,b,G = sympy.symbols('a b G')
    func = (G - a)**2 + b
    my_func = lambdify((G,a,b), -1*func)
    results = minimize(my_func,[0.1,0.1,0.1])

Код работает, если я определяю функцию с одной переменной, но если у меня есть более одной переменной, я получаю следующее сообщение об ошибке.

    TypeError: <lambda>() takes exactly 3 arguments (1 given)

Может ли кто-нибудь помочь мне определить, где пошло не так?


person user3821012    schedule 06.12.2015    source источник
comment
minimize ожидает, что функция будет иметь один аргумент в том смысле, что вещь, которую вы передаете, является одним объектом. Однако одним объектом может быть список. Вы уверены, что вам нужно sympy для этого? Scipy (не sympy) выполняет числовые операции, а не символические...   -  person Dair    schedule 06.12.2015
comment
@Dair Да, мне нужно использовать sympy для удобства, иначе мне будет сложно определить целевую функцию. Я понимаю, что область предназначена только для числовых вычислений, но знаете ли вы, есть ли способ лямбдифицировать символическую функцию, чтобы ее можно было передать в scipy.minimize?   -  person user3821012    schedule 06.12.2015
comment
Я не могу придумать символический способ, но несимволический способ не так уж плох: func = lambda x: (x[0] - x[1])**2 + x[2]   -  person Dair    schedule 06.12.2015
comment
Помогает ли это?   -  person lhcgeneva    schedule 06.12.2015
comment
@lhcgeneva, теоретически это может сработать. Но у меня более 20 независимых переменных — другими словами, размерность x больше 20 — и это количество переменных может меняться. Это означает, что я должен выписывать все переменные вручную и менять их всякий раз, когда изменяется количество переменных, так что это не идеально...   -  person user3821012    schedule 06.12.2015
comment
Но вы делаете то же самое сейчас, или я неправильно понимаю? Разве вы не можете просто определить свой jacobian динамически?   -  person lhcgeneva    schedule 06.12.2015
comment
Нет, я не вычисляю якобиан. Приведенный выше код просто дал пример. На самом деле я занимаюсь оптимизацией символьной функции. У меня есть очень длинная символическая функция (с 20 переменными), и мне нужно найти значения (для 20 переменных), которые максимизируют функцию.   -  person user3821012    schedule 06.12.2015
comment
Нет, вы не вычисляете якобиан, но вы определяете свою функцию, как в посте, на который я ссылаюсь. Якобиан, который вы также можете рассчитать, используя двадцать или любое количество переменных, это не должно быть проблемой.   -  person lhcgeneva    schedule 06.12.2015


Ответы (1)


Как указал @Dair, lambdify sympy обычно требует более одного аргумента, в то время как scipy ожидает только один аргумент, список (или массив), который содержит все значения каждой переменной. Поскольку мою целевую функцию удобнее всего определить с помощью sympy, мне нужно найти способ обойти эту несовместимость sympy и scipy.

@lhcgeneva указала ответ на аналогичный вопрос. В этом ответе неудобно обрабатывать большое количество независимых переменных, особенно когда количество независимых переменных может измениться, что требует переопределения «векторизованной» версии целевой функции. Однако, вдохновленный этим сообщением, я нашел следующее решение, используя *tuple():

    import sympy
    from scipy.optimize import minimize
    from sympy.utilities.lambdify import lambdify

    a,b,G = sympy.symbols('a b G')
    func = -1*((G - a)**2 + b)
    my_func = lambdify((G,a,b), func)

    def my_func_v(x):
        return my_func(*tuple(x))

    results = minimize(my_func_v,[0.1,0.1,0.1])

В примере, который я привел, использование *tuple() кажется излишним, но для проблемы, которую я хочу решить, это избавляет от многих хлопот. Вот пример, который больше похож на вопрос, который я хочу решить

NUM_VAR = 10
x = np.array(sympy.symbols('x0:%d'%NUM_VAR))
func = np.sum((x-1)**2)
my_func = lambdify(x, func)


def my_func_v(x):
    return my_func(*tuple(x))

results = minimize(my_func_v,np.zeros(NUM_VAR))

Эта вещь *tuple() может избавить меня от записи всех элементов x, как показано ниже (для случая NUM_VAR=10):

def my_func_v(x):
    return my_func(x[0],x[1],x[2],x[3],x[4],x[5],x[6],x[7],x[8],x[9])

Кроме того, нам не нужно менять my_func_v при изменении NUM_VAR.

person user3821012    schedule 07.12.2015