Оптимизация на 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 за удобство, в противен случай ще ми е трудно да дефинирам целевата функция. Разбирам, че обхватът е само за числени изчисления, но знаете ли дали има начин да lambdify символна функция, така че да може да бъде предадена на 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
Но вие правите същото нещо сега или аз разбирам това погрешно? Не можеш ли просто да дефинираш своя якобиан динамично?   -  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