Utilisation du solveur minPack pour résoudre des systèmes d'équations non-linéaires
Introduction
Dans de nombreux cas, les équations décrivant le comportement non-nominal d'un système énergétique sont non-linéaires, de telle sorte que leur résolution constitue un problème difficile, surtout lorsque le nombre d'inconnues est élevé.
Présentation de minPack
Basé sur la méthode de Levendberg-Marquardt, minPack est un ensemble d'algorithmes mis au point en Fortran, puis traduits sous Java. Cette méthode combine l'algorithme de Gauss-Newton et la méthode de descente de gradient. Son intérêt principal est d'être très robuste et de ne nécessiter comme initialisation qu'une solution approchée.
Précisons que minPack est en fait un ensemble d'algorithmes de minimisation qui peut être utilisé pour trouver le minimum de la norme L2 des résidus ||F(X)|| d'un ensemble de m équations non-linéaires à n inconnues F(X).
Lorsque n est égal à m, minPack peut ainsi servir à résoudre le système d'équations F(X), ou tout au moins chercher un vecteur X qui soit le plus proche possible de la solution.
On peut aussi utiliser minPack pour identifier un jeu de n paramètres permettant d'ajuster une équation non linéaire sur un ensemble de m données, ou bien pour optimiser une fonction de plusieurs variables.
Mise en œuvre de minPack
L'ensemble des classes nécessaire est inclus dans la bibliothèque extBib.zip qui doit être déclarée dans le classPath.
L'implémentation sous Java de minPack se fait en utilisant une interface appelée optimization.Lmdif_fcn, qui contraint les classes appelantes à disposer d'une fonction appelée fcn().
Cette fonction fcn() reçoit comme principaux arguments un vecteur (tableau x[n]) contenant les variables et un vecteur (tableau fvec[m]) renvoyant les résidus des fonctions que l'on cherche à annuler. Comme nous l'avons indiqué, leur nombre peut excéder celui des variables, mais il doit être le même si on cherche la solution d'un système d'équations.
Le guidage de l'algorithme se fait en pratique en jouant sur deux critères de précision, l'un portant sur la somme des résidus, et l'autre sur la précision du calcul des dérivées partielles, estimées par différences finies.
Exemple
Pour illustrer l'emploi de minPack, nous avons réalisé une classe de test, appelée TestMinPack, qui résout le système d'équations correspondant à l'intersection d'un cercle et d'une droite. Le code se présente comme indiqué ci-dessous.
Attention : afin de conserver les mêmes indices qu'en Fortran, l'implémentation Java déclare des tableaux de dimension n+1 au lieu de n, l'indice 0 n'étant pas utilisé.
int info[] = new int[2];
int m = 2;//système de deux équations
int n = 2;//à deux inconnues
int iflag[] = new int[2];
double fvec[] = new double[m+1];//vecteur des résidus
double x[] = new double[n+1];
double residu0;//norme L2 initiale
double residu1;//norme L2 finale
System.out.print("\n\n\n\n\n problem dimensions: " + n + " " + m + "\n");
//initialisation des inconnues
x[1] = 1;
x[2] = 0;
iflag[1] = 0;
testMinPack.fcn(m,n,x,fvec,iflag);//premier appel pour calcul norme L2 initiale
//norme L2 initiale
residu0 = optimization.Minpack_f77.enorm_f77(m,fvec);
testMinPack.nfev = 0;
testMinPack.njev = 0;
double epsi=0.0005;//précision globale demandée sur la convergence
double epsfcn = 1.e-6;//précision calcul des différences finies
//appel à minPack
optimization.Minpack_f77.lmdif2_f77(testMinPack, m, n, x, fvec, epsi, epsfcn, info);
//norme L2 finale
residu1 = optimization.Minpack_f77.enorm_f77(m,fvec);
//affichage des résultats
System.out.println("\n Initial L2 norm of the residuals: " + residu0 +
"\n Final L2 norm of the residuals: " + residu1 +
"\n Number of function evaluations: " + testMinPack.nfev +
"\n Number of Jacobian evaluations: " + testMinPack.njev +
"\n Info value: " + info[1] );
System.out.println();
System.out.println("precision: \t" + residu1);
for(int i=1;i<=n;i++){//affichage de la solution
System.out.println("x["+i+"] \t" + x[i]);
}
La fonction fcn est définie comme suit :
public void fcn(int m, int n, double x[], double fvec[], int iflag[]) {
if (iflag[1]==1) this.nfev++;
if (iflag[1]==2) this.njev++;
fvec[1] =x[1]*x[1]+x[2]*x[2]-1;
fvec[2] =3*x[1]+2*x[2]-1;
}
Les résultats que l'on obtient sont les suivants, pour l'initialisation x[1] = 1, x[2] = 0 :
problem dimensions: 2 2
Initial L2 norm of the residuals: 2.0
Final L2 norm of the residuals: 2.39665398638067E-10
Number of function evaluations: 6
Number of Jacobian evaluations: 10
Info value: 2
precision: 2.39665398638067E-10
x[1] 0.7637079407212384
x[2] -0.6455619110818576
pour l'initialisation x[1] = 0, x[2] = 0, on obtient:
problem dimensions: 2 2
Initial L2 norm of the residuals: 1.4142135623730951
Final L2 norm of the residuals: 3.853333208070353E-10
Number of function evaluations: 9
Number of Jacobian evaluations: 12
Info value: 2
precision: 3.853333208070353E-10
x[1] -0.3021694793631984
x[2] 0.9532542190447976
Cet exemple illustre d'une part comment minPack peut être utilisé, mais aussi l'importance qu'il y a à bien initialiser le problème soumis. Dans notre cas, le problème a deux solutions, mais l'algorithme se satisfait de la première qu'il trouve, la plus proche de la valeur de départ.
La classe TestMinPack est donnée ci-dessous.