optimisation SciPy pour le système sous-contraint
-
27-10-2019 - |
Question
Je dois souvent résoudre des problèmes non linéaires dans lesquels le nombre de variables dépasse le nombre de contraintes (ou parfois l'inverse). Généralement, certaines des contraintes ou variables sont redondantes d'une manière compliquée. Est-il possible de résoudre ces problèmes?
La plupart des solveurs SciPy semblent supposer que le nombre de contraintes est égal au nombre de variables, et que le jacobien est inversible. leastsq
fonctionne parfois, mais il ne cherche même pas lorsque les contraintes sont moins que le nombre de variables. Je me rends compte que je pouvais courir fmin
sur linalg.norm(F)
, mais cela est beaucoup moins efficace que toute méthode qui utilise la jacobienne.
Voici un exemple d'un problème qui montre ce dont je parle. Il a évidemment une solution, mais leastsq
donne une erreur. Bien sûr, cet exemple est facile à résoudre à la main, je l'ai mis ici pour montrer la question.
import numpy as np
import scipy.optimize
mat = np.random.randn(5, 7)
def F(x):
y = np.dot(mat, x)
return np.array([ y[0]**2 + y[1]**3 + 12, y[2] + 17 ])
x0 = np.random.randn(7)
scipy.optimize.leastsq(F, x0)
Le message d'erreur que je reçois est:
Traceback (most recent call last):
File "question.py", line 13, in <module>
scipy.optimize.leastsq(F, x0)
File "/home/dstahlke/apps/scipy/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 278, in leastsq
raise TypeError('Improper input: N=%s must not exceed M=%s' % (n,m))
TypeError: Improper input: N=7 must not exceed M=2
J'ai parcouru le net pour une réponse et ont même demandé sur la liste de diffusion SciPy, et n'a obtenu aucune réponse. Pour l'instant je piraté la source SciPy de telle sorte que les utilisations du solveur de newton_krylov
pinv()
, mais je ne pense pas que ce soit une solution optimale.
La solution
Que diriez-vous redimensionner le tableau de retour de F () au nombre de variables:
import numpy as np
import scipy.optimize
mat = np.random.randn(5, 7)
def F(x):
y = np.dot(mat, x)
return np.resize(np.array([ y[0]**2 + y[1]**3 + 12, y[2] + 17]), 7)
while True:
x0 = np.random.randn(7)
r = scipy.optimize.leastsq(F, x0)
err = F(r[0])
norm = np.dot(err, err)
if norm < 1e-6:
break
print err