web-dev-qa-db-fra.com

Python optimisation non linéaire contrainte

Quel est le package recommandé pour l'optimisation non linéaire contrainte dans python?

Le problème spécifique que j'essaie de résoudre est le suivant:

J'ai un X (Nx1) inconnu, j'ai M (Nx1) u vecteurs et M (NxN) s matrices.

max [5th percentile of (ui_T*X), i in 1 to M]
st 
0<=X<=1 and
[95th percentile of (X_T*si*X), i in 1 to M]<= constant

Lorsque j'ai commencé le problème, je n'avais qu'une estimation ponctuelle pour u et s et j'ai pu résoudre le problème ci-dessus avec cvxpy.

J'ai réalisé qu'au lieu d'une estimation pour u et s, j'avais la distribution entière des valeurs, donc je voulais changer ma fonction objectif afin de pouvoir utiliser la distribution entière. La description du problème ci-dessus est ma tentative d'inclure ces informations de manière significative.

cvxpy ne peut pas être utilisé pour résoudre ce problème, j'ai essayé scipy.optimize.anneal, mais je n'arrive pas à fixer des limites aux valeurs inconnues. J'ai aussi regardé pulp mais il n'autorise pas les contraintes non linéaires.

15
akhil

scipy possède un package spectaculaire pour l'optimisation non linéaire contrainte.

Vous pouvez commencer en lisant le optimizedoc , mais voici un exemple avec SLSQP:

minimize(func, [-1.0,1.0], args=(-1.0,), jac=func_deriv, constraints=cons, method='SLSQP', options={'disp': True})
12
Slater Victoroff

Alors que l'algorithme SLSQP dans scipy.optimize.minimize est bon, il a un tas de limitations. Le premier est un solveur QP, donc cela fonctionne pour les équations qui s'intègrent bien dans un paradigme de programmation quadratique. Mais que se passe-t-il si vous avez des contraintes fonctionnelles? Aussi, scipy.optimize.minimize n'est pas un optimiseur global, vous devez donc souvent commencer très près des résultats finaux.

Il existe un package d'optimisation non linéaire contraint (appelé mystic) qui existe depuis presque aussi longtemps que scipy.optimize lui-même - je le suggère comme le go-to pour gérer toute optimisation non linéaire contrainte générale.

Par exemple, votre problème, si je comprends votre pseudo-code, ressemble à ceci:

import numpy as np

M = 10
N = 3
Q = 10
C = 10

# let's be lazy, and generate s and u randomly...
s = np.random.randint(-Q,Q, size=(M,N,N))
u = np.random.randint(-Q,Q, size=(M,N))

def percentile(p, x):
    x = np.sort(x)
    p = 0.01 * p * len(x)
    if int(p) != p:
        return x[int(np.floor(p))]
    p = int(p)
    return x[p:p+2].mean()

def objective(x, p=5): # inverted objective, to find the max
    return -1*percentile(p, [np.dot(np.atleast_2d(u[i]), x)[0] for i in range(0,M-1)])


def constraint(x, p=95, v=C): # 95%(xTsx) - v <= 0
    x = np.atleast_2d(x)
    return percentile(p, [np.dot(np.dot(x,s[i]),x.T)[0,0] for i in range(0,M-1)]) - v

bounds = [(0,1) for i in range(0,N)]

Donc, pour gérer votre problème dans mystic, il vous suffit de spécifier les limites et les contraintes.

from mystic.penalty import quadratic_inequality
@quadratic_inequality(constraint, k=1e4)
def penalty(x):
  return 0.0

from mystic.solvers import diffev2
from mystic.monitors import VerboseMonitor
mon = VerboseMonitor(10)

result = diffev2(objective, x0=bounds, penalty=penalty, npop=10, gtol=200, \
                 disp=False, full_output=True, itermon=mon, maxiter=M*N*100)

print result[0]
print result[1]

Le résultat ressemble à ceci:

Generation 0 has Chi-Squared: -0.434718
Generation 10 has Chi-Squared: -1.733787
Generation 20 has Chi-Squared: -1.859787
Generation 30 has Chi-Squared: -1.860533
Generation 40 has Chi-Squared: -1.860533
Generation 50 has Chi-Squared: -1.860533
Generation 60 has Chi-Squared: -1.860533
Generation 70 has Chi-Squared: -1.860533
Generation 80 has Chi-Squared: -1.860533
Generation 90 has Chi-Squared: -1.860533
Generation 100 has Chi-Squared: -1.860533
Generation 110 has Chi-Squared: -1.860533
Generation 120 has Chi-Squared: -1.860533
Generation 130 has Chi-Squared: -1.860533
Generation 140 has Chi-Squared: -1.860533
Generation 150 has Chi-Squared: -1.860533
Generation 160 has Chi-Squared: -1.860533
Generation 170 has Chi-Squared: -1.860533
Generation 180 has Chi-Squared: -1.860533
Generation 190 has Chi-Squared: -1.860533
Generation 200 has Chi-Squared: -1.860533
Generation 210 has Chi-Squared: -1.860533
STOP("ChangeOverGeneration with {'tolerance': 0.005, 'generations': 200}")
[-0.17207128  0.73183465 -0.28218955]
-1.86053344078

mystic est très flexible, et peut gérer tout type de contraintes (par exemple les égalités, les inégalités), y compris les contraintes symboliques et fonctionnelles. J'ai spécifié les contraintes comme des "pénalités" ci-dessus, ce qui est la manière traditionnelle, en ce sens qu'elles appliquent une pénalité à l'objectif lorsque la contrainte est violée. mystic fournit également des transformations de noyau non linéaires, qui contraignent l'espace de la solution en réduisant l'espace des solutions valides (c'est-à-dire par une cartographie spatiale ou une transformation du noyau).

À titre d'exemple, voici mystic résolution d'un problème qui casse beaucoup de solveurs QP, car les contraintes ne sont pas sous la forme d'une matrice de contraintes. C'est optimiser la conception d'un récipient sous pression.

"Pressure Vessel Design"

def objective(x):
    x0,x1,x2,x3 = x
    return 0.6224*x0*x2*x3 + 1.7781*x1*x2**2 + 3.1661*x0**2*x3 + 19.84*x0**2*x2

bounds = [(0,1e6)]*4
# with penalty='penalty' applied, solution is:
xs = [0.72759093, 0.35964857, 37.69901188, 240.0]
ys = 5804.3762083

from mystic.symbolic import generate_constraint, generate_solvers, simplify
from mystic.symbolic import generate_penalty, generate_conditions

equations = """
-x0 + 0.0193*x2 <= 0.0
-x1 + 0.00954*x2 <= 0.0
-pi*x2**2*x3 - (4/3.)*pi*x2**3 + 1296000.0 <= 0.0
x3 - 240.0 <= 0.0
"""
cf = generate_constraint(generate_solvers(simplify(equations)))
pf = generate_penalty(generate_conditions(equations), k=1e12)


if __name__ == '__main__':

    from mystic.solvers import diffev2
    from mystic.math import almostEqual
    from mystic.monitors import VerboseMonitor
    mon = VerboseMonitor(10)

    result = diffev2(objective, x0=bounds, bounds=bounds, constraints=cf, penalty=pf, \ 
                     npop=40, gtol=50, disp=False, full_output=True, itermon=mon)

    assert almostEqual(result[0], xs, rel=1e-2)
    assert almostEqual(result[1], ys, rel=1e-2)

Trouvez ceci, et environ 100 exemples similaires, ici: https://github.com/uqfoundation/mystic .

Je suis l'auteur, donc je suis légèrement partial. Cependant, le biais est très léger. mystic est à la fois mature et bien pris en charge, et a une capacité inégalée pour résoudre des problèmes d'optimisation non linéaire à contraintes dures.

11
Mike McKerns

Comme d'autres l'ont également commenté, le package SciPy minimiser est un bon point de départ. J'ai inclus un exemple ci-dessous (référence Hock Schittkowski # 71) qui comprend une fonction objective, une contrainte d'égalité et une contrainte d'inégalité.

import numpy as np
from scipy.optimize import minimize

def objective(x):
    return x[0]*x[3]*(x[0]+x[1]+x[2])+x[2]

def constraint1(x):
    return x[0]*x[1]*x[2]*x[3]-25.0

def constraint2(x):
    sum_eq = 40.0
    for i in range(4):
        sum_eq = sum_eq - x[i]**2
    return sum_eq

# initial guesses
n = 4
x0 = np.zeros(n)
x0[0] = 1.0
x0[1] = 5.0
x0[2] = 5.0
x0[3] = 1.0

# show initial objective
print('Initial SSE Objective: ' + str(objective(x0)))

# optimize
b = (1.0,5.0)
bnds = (b, b, b, b)
con1 = {'type': 'ineq', 'fun': constraint1} 
con2 = {'type': 'eq', 'fun': constraint2}
cons = ([con1,con2])
solution = minimize(objective,x0,method='SLSQP',\
                    bounds=bnds,constraints=cons)
x = solution.x

# show final objective
print('Final SSE Objective: ' + str(objective(x)))

# print solution
print('Solution')
print('x1 = ' + str(x[0]))
print('x2 = ' + str(x[1]))
print('x3 = ' + str(x[2]))
print('x4 = ' + str(x[3]))

Il existe également un fil de discussion plus complet sur solveurs de programmation non linéaire pour Python si SLSQP ne peut pas résoudre votre problème. Mon matériel de cours sur Engineering Design Optimization est disponible si vous avez besoin d'informations supplémentaires sur les méthodes du solveur.

4
John Hedengren

Généralement, pour l'ajustement, vous pouvez utiliser les fonctions scipy.optimize ou lmfit qui étend simplement le paquet scipy.optimize pour faciliter la transmission des choses comme des limites. Personnellement, j'aime utiliser kmpfit , une partie de la bibliothèque kapteyn et est basée sur l'implémentation C de MPFIT.

scipy.optimize.minimize() est probablement le plus facile à obtenir et est couramment utilisé.

3
pseudocubic