Baniere Saphir Control
http://www.saphir-control.fr/

bar


BACK

Scilab : un logiciel libre pour le calcul scientifique
Résoudre et optimiser avec Scilab

Maurice GOURSAT (INRIA)



Tous ces articles en ligne sur Scilab ont été écrits pour LINUX MAGAZINE FRANCE par les développeurs du Scilab Group. Les articles sont sous licence FDL (Free Documentation Licence)





De nombreux problèmes se formulent de la manière suivante : trouver le zéro d'une fonction ou son minimum, c'est-à-dire (la valeur numérique de) x tel que F(x)=0 ou trouver x qui donne Min (F(x)). Pour les formulations les plus simples les deux problèmes sont équivalents. Les classifications se font ensuite selon que F est linéaire ou non et que x est ou non soumis à des contraintes. Nous allons prendre quelques exemples et voir comment on les résout avec Scilab. Ces exemples nécessitent la connaissance de quelques notions de base et une lecture attentive pour ceux qui ont oublié leurs cours.

1   Exemple 1 : problème de trafic automobile.

De nombreux systèmes sont modélisés par un système linéaire, c'est-à-dire par une relation :

A x=b

A est une matrice (mxn) connue, b un vecteur de taille n connu et x est le vecteur que l'on cherche.

Considérons le double carrefour suivant :
carrefour

Les chiffres représentent le trafic c'est-à-dire le nombre de véhicules par heure. On suppose évidemment que pour chaque carrefour toutes les voitures qui arrivent en repartent : ceci est une des lois de base des réseaux. C'est la loi dite des noeuds ou première loi de Kirchhoff. Elle est valable pour les réseaux électriques, d'eau, de télécommunications...

Dans notre cas on s'intéresse au débit en véhicules par heure et pour les 2 trafics manquants on obtient :

x1=1200 + 200 pour la première intersection et

x1 + x2=1600 + 700 pour la deuxième intersection.

On a donc comme ci-dessus A x=b avec

A=(1   0; 1   1) et b=(1400   2300) .


-->A=[1 0;1 1]
 A  =
 
!   1.    0. !
!   1.    1. !
 
-->b=[1400 ; 2300]
 b  =
 
!   1400. !
!   2300. !
 
-->x=A\b //on cherche la solution x de A.x=b
 x  =
 
!   1400. !
!   900.  !

L'opérateur emph de Scilab résout donc le problème. Dans le cas présent le résultat se fait sans calcul mais son utilité peut s'imaginer lorsque A est la matrice origines-destinations d'une ville et que l'on a quelques milliers de routes et de carrefours et que l'on cherche les trafics sur les tronçons.

2   Exemple 2 : le zapping à la télévision

Un sondage rapide sur l'écoute des chaines de télévision montre que lors d'une soirée pour les 4 chaines TF1, A2, FR3, M6 et CA (autres chaines du cable) les pourcentages de téléspectateurs sont respectivement de 30, 20, 15, 15, 20 (ces chiffres sont pure fiction et toute ressemblance avec la réalité n'est que pure coïncidence ! ). Chaque jour, au cours de la soirée les téléspectateurs zappent d'une chaine vers les autres avec les pourcentages suivants :


70      15     10       10       5
10      70     10        5       5
 5       5     60        5       5
 5       5     10       70       5
10       5     10       10      80

Précisons ce que signifie le tableau : sur la première colonne figurent les pourcentages de ``zapping'' des téléspectateurs de TF1. Il y a donc chaque jour 70% de ces téléspectateurs qui restent sur TF1, 10% qui passent sur A2, 5% sur FR3, 5% sur M6 et 10% vers les autres chaines. Les colonnes suivantes correspondent au ``zapping'' des téléspectateurs des autres chaines. La diagonale du tableau, c'est-à-dire 70   70   60   70   80 représentent donc la fidélité à la chaine.

On souhaite connaître l'évolution du nombre de téléspectateurs par chaine avec ces informations. Plus précisèment on souhaite savoir si la répartition entre les chaînes va se stabiliser et si oui quelle sera la répartition d'équilibre. Le vecteurs des pourcentages initiaux est :

p0=[30   20   15   15   20]


-->A
 A  =
 
!   0.7     0.15    0.1    0.1     0.05 !
!   0.1     0.7     0.1    0.05    0.05 !
!   0.05    0.05    0.6    0.05    0.05 !
!   0.05    0.05    0.1    0.7     0.05 !
!   0.1     0.05    0.1    0.1     0.8  !
 
-->p
 p  =
 
!   30. !
!   20. !
!   15. !
!   15. !
!   20. !
 
-->A*p
 ans  =
 
!   28.   !
!   20.25 !
!   13.25 !
!   15.5  !
!   23.   !
 
Au bout d'un jour les proportions par chaine sont donc données par p(1)=A p ; puis le jour suivant par p(2)=A p(1) et ainsi de suite et finalement la répartition stable est telle que A p s=ps soit (A- I)ps=0 (avec la contrainte de somme égale à 100 pour les termes de p). La répartition stable est telle que les téléspectateurs qui quittent une chaine sont compensés par ceux des autres chaines qui arrivent.

La première méthode consiste donc à trouver la solution à notre problème en itérant la formule pi+1=A pi soit pn=An p0 :


-->B=A-eye(A) //on fabrique A-I
 B  =
 
! - 0.3     0.15    0.1    0.1     0.05 !
!   0.1   - 0.3     0.1    0.05    0.05 !
!   0.05    0.05  - 0.4    0.05    0.05 !
!   0.05    0.05    0.1  - 0.3     0.05 !
!   0.1     0.05    0.1    0.1   - 0.2  !
 
-->(A**30)*p //A*A*A*...*A*p 
 ans  =
 
!   23.640942 !
!   19.25045  !
!   11.111111 !
!   15.873018 !
!   30.124479 !
l'opérateur ``élévation à la puissance'' noté ** s'applique donc à une matrice et il est donc facile de trouver la répartition après 30 jours (on va voir avec le résultat ci-dessous que pour 30 on a la répartition stable avec beaucoup de précision).

L'autre méthode consiste à résoudre (avec B=A-I) B . p=0. Ceci correspond à trouver le ``noyau'' de B qui est donné par la commande kernel


-->pp=kernel(B) //le noyau de B est la solution
 pp  =
 
!   0.5026089 !
!   0.4092673 !
!   0.2362262 !
!   0.3374660 !
!   0.6404674 !
 
-->pp=100*pp/sum(pp) //on transforme la solution en pourcentages
 pp  =
 
!   23.640662 !
!   19.250253 !
!   11.111111 !
!   15.873016 !
!   30.124958 !
 
-->B*pp //on vérifie que ce produit est bien zero
 ans  =
 
   1.0E-14 *
 
!   0.0990852 !
!   0.1425075 !
!   0.0710586 !
!   ¯.2032012 !
!   0.1138846 !

Cet exemple montre quelques manipulations simples et performantes de Scilab avec du calcul vectoriel.

Cet exemple simple montre cependant un point important (et bien connu !) : pour une chaine de télévision le point dominant est pour le long terme le pourcentage de fidélité des téléspectateurs plutôt que la performance absolue instantanée.

3   Systèmes dynamiques

On a vu dans un article précédent comment simuler des systèmes dynamiques avec l'exemple des requins qui mangent les sardines et celui du pendule qui glisse le long d'une courbe (article de mai 2000).

Les systèmes linéaires définis ci-dessus forment l'outil de base pour l'étude des systèmes dynamiques, même si la simulation se fait finalement avec des intégrateurs (ou solveurs de systèmes différentiels, algébro-différentiels...).

On peut écrire le modèle classique suivant :

ì
í
î
x'(t) = A x(t) + b(u(t))
y(t) = cT x(t)  
    (1)

avec x(0)=x0

La première équation donne l'évolution du système et la seconde la partie des variables que l'on observe; x est l'état du système et u la commande qu'on applique pour faire évoluer le système.

Dans le cas des requins et des sardines x serait un vecteur de deux termes : le nombre de requins et le nombre de sardines. Le contrôle u serait le nombre de sardines et de requins pêchés; on peut imaginer le cas où l'observation est réduite à une seule composante de x, par exemple le nombre de requins. En fait le modèle de Lotka-Volterra n'est pas linéaire et n'entre donc pas dans la classe de modèles que nous regardons ici.

Dans un autre cas, par exemple un système mécanique de type avion le modèle comporte la position, la vitesse, l'accélération. On peut donc avoir un modèle d'ordre supérieur tel que :

yn(t) + a1 yn-1(t) + ... + any(t)=q0 u(t)     (2)

avec les conditions initiales :

y(0)=y0, y'(0)=y1, ...     (3)

En posant simplement (on dit qu'on prend un état augmenté) :

x1(t)=y(t),x2(t)=y'(t), ...

on peut écrire le modèle sous la forme :

x'(t)=A x(t) + b u(t); x(0)=x0

avec :
A= é
ê
ê
ê
ê
ê
ê
ë
0 1 0 0 ... 0
0 0 1 0 ... 0
0 0 0 1 ... 0
... ... ... ... ... ...
0 0 0 0 ... 1
-an -an-1 -an-2 ... ... -a1
ù
ú
ú
ú
ú
ú
ú
û
    (4)

et

b= é
ê
ê
ê
ê
ê
ê
ë
0
0
0
...
0
q0
ù
ú
ú
ú
ú
ú
ú
û
    (5)

La solution libre ( c'est-à-dire avec b=0) du système différentiel est :

x(t)=eAt x0     (6)

eAt est l'exponentielle de matrice, c'est-à-dire :

eAt=
¥
å
0
Antn

n!
    (7)

avec les propriétés suivantes :

d

dt
eAt=A eAt ; eA(t+s)=eAt eAs; (eAt)-1=e-At ; eA 0=1     (8)



-->A=rand(4,4) //matrice aleatoire 
 A  =
 
!   0.2922267    0.5935095    0.6325745    0.4818509 !
!   0.5664249    0.5015342    0.4051954    0.2639556 !
!   0.4826472    0.4368588    0.9184708    0.4148104 !
!   0.3321719    0.2693125    0.0437334    0.2806498 !

-->expm(A) //l'exponentielle de la matrice A
 ans  =
 
!   2.1501546    1.477585     1.6726308    1.1853378 !
!   1.3102627    2.320914     1.3517984    0.9077929 !
!   1.4668403    1.4775185    3.2282155    1.264257  !
!   0.6898985    0.6707707    0.4436347    1.5931327 !


La fonction Scilab expm (la commande exp calcule l'exponentielle ``simple'' c'est-à-dire terme à terme pour une matrice, le m final signifie matricielle) n'est pas très utile pour intégrer des systèmes différentiels car les intégrateurs donnent de meilleurs résultats. Il est cependant possible d'utiliser une forme simplifiée de la matrice (forme de Jordan par exemple) et d'étudier certaines propriétés du système différentiel, en particulier sa stabilité.

Notons que l'on a :

e
(U-1AU)t
 
=U-1. e-At.U     (9)

si U est une matrice de changement de base.

On peut voir que si A est une matrice diagonale, son exponentielle est simplement la matrice diagonale dont les termes sont les exponentielles des éléments de la diagonale de A. Il est alors facile de voir si le système explose ou non. Si les termes sont à partie réelle négative le sytème est stable : exp(z*t) tend vers 0 lorsque t croît pourvu que Re(z) < 0. C'est cette étude que permet la forme de Jordan de la matrice.

4   Forme de Jordan et stabilité

Le résultat de base est : pour toute matrice carrée A il existe une matrice de changement de base U qui transforme A sous sa forme canonique de Jordan c'est-à-dire :

U-1. A .U=J=diag(J1,J2,...,Jp)     (10)

où les Ji sont les blocs de Jordan; un bloc Ji est associé à la valeur propre li et sa taille est l'ordre de multiplicité de cette valeur propre; il est de la forme :

Ji= é
ê
ê
ê
ê
ê
ê
ë
li 1 0 0 ... 0
0 li 1 0 ... 0
0 0 li 1 ... 0
... ... ... ... ... ...
0 0 0 0 ... 1
0 0 0 ... ... li
ù
ú
ú
ú
ú
ú
ú
û
    (11)

Nous pouvons donc reprendre la formule ci-dessus pour obtenir :

eAt=U-1. eJt .U=U-1 é
ê
ê
ê
ê
ê
ê
ê
ê
ê
ê
ë
e
J1t
 
0 0 0 ... 0
0
e
J2t
 
0 0 ... 0
0 0
e
J3t
 
0 ... 0
... ... ... ... ... ...
0 0 0 0 ... 0
0 0 0 ... ...
e
J1t
 
ù
ú
ú
ú
ú
ú
ú
ú
ú
ú
ú
û
    (12)

On peut expliciter chaque terme de cette matrice. Un cas particulier important est celui où chaque bloc de Jordan est de dimension 1, c'est-à-dire que la matrice est diagonalisable.

Un système x'(t)=A x(t); x(0)=x0 est asymptotiquement stable si || x(t) || ® 0 lorsque t ® ¥ . Il est stable si x(t) reste borné et instable sinon.

Il est donc très facile de voir avec la mise sous forme de Jordan qu'un système est stable si toutes les valeurs propres sont à partie réelle négative et instable s'il existe une valeur propre à partie réelle positive.


-->A
 A  =
 
! - 1.    2.    3.    4.    5. !
! - 2.  - 3.    4.    5.    1. !
! - 3.  - 4.  - 5.    1.    2. !
! - 4.  - 5.    1.  - 2.    3. !
! - 5.  - 1.  - 2.  - 3.  - 4. !
 
-->spec(A) //donne les valeurs propres de A
 ans  =
 

! - 2.3085744 + 9.7098818i !
! - 2.3085744 - 9.7098818i !
! - 2.7463994 + 3.3236617i !
! - 2.7463994 - 3.3236617i !
! - 4.8900525              !
 
-->[J, U, jj]=bdiag(A) //on fait une diagonalisation par blocs
 jj  =
 
!   2. !
!   2. !
!   1. !
 U  =
 
!   0.1464163    0.6610705    0.6303059  - 0.2895151  - 0.0504537 !
!   0.3614366    0.5356768  - 0.4367886    0.6566914    0.2082125 !
!   0.4907292  - 0.2012461  - 0.1359894  - 0.2563754  - 0.8555927 !
!   0.7376988  - 0.0981921  - 0.3357698  - 0.2802899    0.5853825 !
!   0.2508209  - 0.4752837    0.5802959    0.5849911    0.0010181 !
 J  =
 
! - 2.2334457  - 9.4719406    0.           0.           0.        !
!   9.9543962  - 2.383703     0.           0.           0.        !
!   0.           0.         - 2.4115925    2.7693088    0.        !
!   0.           0.         - 4.0294614  - 3.0812063    0.        !
!   0.           0.           0.           0.         - 4.8900525 !

Le résultat est donc formé d'un vecteur jj donnant les tailles des blocs, de la matrice J avec les blocs sur la diagonale et la matrice U de passage de A à U. Dans le cas précédent 2 blocs de Jordan sont de dimension 2 car A est considérée comme réelle et les valeurs propres sont complexes conjuguées. Cette ambiguité est levée si on fait l'opération sur les complexes :


-->A=A+%i*0; //on rend complexe la matrice A

-->[J, U, jj]=bdiag(A);
 
-->jj
 jj  =
 
!   1. !
!   1. !
!   1. !
!   1. !
!   1. !
 
-->J
 J  =
 
 
         column 1 to 2
 
! - 2.3085744 - 9.7098818i    0                      !
!   0                       - 2.3085744 + 9.7098818i !
!   0                         0                      !
!   0                         0                      !
!   0                         0                      !
 
         column 3 to 5
 
!   0                         0                         0         !
!   0                         0                         0         !
! - 2.7463994 + 3.3236617i    0                         0         !
!   0                       - 2.7463994 - 3.3236617i    0         !
!   0                         0                       - 4.8900525 !
 

Partant d'un matrice dont on ne connait pas les propriétés on peut donc voir si cette matrice est celle d'un système stable et on va pouvoir simuler con comportement en sachant que nous ne devons pas avoir d'explosion numérique.

5   Systèmes non linéaires - Cas monovariable

On cherche donc à résoudre f(x)=0 ou encore minimmiser f(x) sous la contrainte x1 < x < x2.

On peut résoudre une équation f(x)=0 par une méthode de type sécante si on sait encadrer le zéro par l'algorithme simple suivant :

y - f(x1)=(x2 - x1)
f(x2 - f(x1)

x2 - x1
    (13)

est l'équation de la droite joignant les points x1,f(x1) et x2,f(x2). On trouve ainsi une valeur de x améliorant l'encadrement du zéro par :

x3=x1 - f(x1)
x2 - x1

f(x2 - f(x1
    (14)

6   Systèmes non linéaires - Cas général

On veut maintenant résoudre un système non linéaire de n équations à n inconnues, c'est-à-dire le système suivant :

F(x)= é
ê
ê
ë
f1 (x1,x2,...,xn)
f2 (x1,x2,...,xn)
... fn (x1,x2,...,xn)
ù
ú
ú
û
=0     (15)

Différentes méthodes existent pour résoudre ce problème : la plus simple est la méthode de Newton qui procède par linéarisation et résolution du système linéarisé de manière itérative; c'est la simple généralisation de la sécante du cas monovariable.

Du développement autour d'un point :

f(x)=f(x0) + (x - x0) f'(x0) +
(x - x0)2

2!
f''(x0) + ...     (16)

on conserve seulement les 2 premiers termes et le système linéarisé s'écrit :

F(x)=F(x0) + (x - x0) J(x0)     (17)

où J est le jacobien :

Ji,j=
Fi

xj
    (18)

Exemple :

F(x)= é
ê
ê
ë
-x12 - x1x2 + 2
3 x22 + x2 x3 -4
x33 + x12 -6
ù
ú
ú
û
=0     (19)

Le jacobien est :

J= é
ê
ê
ë
-2 x1 - x2 -x1 0
0 6 x2 + x3 x2
2 x1 0 3 x32
ù
ú
ú
û
    (20)

Dans Scilab la fonction fsolve résout le problème; on lui fournit un programme (en C, fortran ou langage Scilab) qui calcule la valeur de la fonction pour une valeur d'argument donnée (ici valeur).

On fournit si possible une fonction calculant le jacobien (ici jaco).


-->getf("valeur.sci");

-->getf("jaco.sci");

-->[x,v,info]=fsolve([0 0 0]',valeur,jaco)
 info  =
 
    1.  
 v  =
 
   1.0E-12 *
 
! - 0.4463097 !
!   0.0710543 !
! - 4.7251092 !
 x  =
 
! - 1.9739351 !
!   0.9607306 !
!   1.2813065 !
 
Les essais ci-dessous montre l'intérêt de fournir le jacobien : un appel simple ne converge pas si on part de (0 0 0); la convergence de l'appel simple dépend fortement de la valeur initiale.

Dans ce cas les fonctions valeurs et jaco sont les suivantes :

function [v]=valeur(x)

x1=x(1);x2=x(2);x3=x(3);
v=zeros(3,1);
v(1)=-x1*x1-x1*x2 + 2;
v(2)=3*x2*x2+x2*x3 - 4;
v(3)=x3*x3*x3+x1*x1 - 6;


function [J]=jaco(x)

x1=x(1);x2=x(2);x3=x(3);
J=zeros(3,3);
J(1,1)=-2*x1-x2;J(1,2)=-x1;
J(2,2)=6*x2+x3;J(2,3)=x2;
J(3,1)=2*x1;J(3,3)=3*x3*x3;

 -->[x,v,info]=fsolve([0 0 0]',valeur)
 info  =
 
    4.  
 v  =
 
!   1.9693403 !
! - 3.9999228 !
! - 5.9691468 !
 x  =
 
!   0.1758927 !
! - 0.0015834 !
! - 0.0439760 !
 
-->[x,v,info]=fsolve([1 -2 -2]',valeur)
 info  =
 
    1.  
 v  =
 
   1.0E-14 *
 
! - 0.0444089 !
! - 0.0444089 !
! - 0.2664535 !
 x  =
 
!   2.2311307 !
! - 1.3347242 !
!   1.0072986 !

7   Approximation polynomiale

Tout le monde se souvient de l'expérience de physique qui donne une série de points dans le plan (par exemple des mesures y pour différentes valeurs de la variable x) et on trace la droite (de régression) qui ``approxime'' le nuage de points : il s'agit simplement de trouver la dépendance linéaire et de résumer l'expérience par les 2 valeurs qui caractérisent la droite. Dans notre cas on veut traiter le cas général : ne pas se restreindre à une droite mais calculer le polynôme d'ordre donné qui en est la meilleure approximation du nuage de points.

L'énoncé est donc : pour l'ensemble de points (xi   yi) on cherche un polynôme p(x)=p0 + p1 x + p2 x2 + ... + pn xn de degré n donné qui réalise :

minp0, p1, ... , pn å (yi - p(xi))2

Ceci se formule sous forme matricielle en écrivant :

X=( xn   xn-1   ...   x2   x   1)

P=( pn   pn-1   ...   p2   p1   p0)

Y=V * Pt

on construit ensuite la matrice de Vandermonde (la ligne i est formée des puissances successives de i) et on fait une résolution aux moindres carrés, ce qui donne en Scilab :

-->x=[1 2 3 4 5 6 7 8 9 10]'; //points equidistants

-->n=6;
 
-->y=[1 3 2 0 4 5 9 3 1 -5]';
 
-->V=zeros(length(x),n+1); //matrice nulle
 
-->V(:,n+1) = ones(length(x),1); //on ajoute une colonne de 1
 
-->for j = n:-1:1
-->    V(:,j) = x.*V(:,j+1);
-->end
 
-->[Q,R] = qr(V,0); //Decomposition (Q,R), R triangulaire
 
-->p = R\(Q'*y);
 
-->r = y - V*p;
 
L'opérateur est l'opérateur de résolution d'un système linéaire suivant : x=A \ b est en fait la solution de A.x=b; Dans le cas ou la matrice est singulière (ce qui est le cas ici) la résolution est faite aux moindres carrés c'est-à-dire qu'on obtient x qui minimise | Ax -b | 2. Nous avons à résoudre p=V \ y que l'on remplace par une procédure plus performante avec une décomposition QR de la matrice de Vandermonde.

La figure suivante représente l'approximation de 10 valeurs par des polynômes de degré 4 et 6; la précision s'améliore évidemment avec la croissance du degré.
croissance

8   Conclusion

Il aurait fallu commencer par avouer que le seul cas que l'on sait traiter correctement est le cas linéaire; dans le cas non linéaire on se ramène par différents moyens à une approximation (locale) linéaire. C'est pour cette raison que l'utilisation des matrices est généralisée et que Scilab qui est construit sur la manipulation et les calculs matriciels est très utile. Dans ce qui précède l'optimisation n'est que sous-jacente. Nous verrons plus tard qu'elle devient explicite avec ses propres techniques lorsqu'il faut résoudre des problèmes sous contraintes.

9   Contacts

Site Web de Scilab : http://www-rocq.inria.fr/scilab

Serveur ftp : ftp://ftp.inria.fr/INRIA/Scilab

Adresse email : Scilab@inria.fr

Newsgroup : comp.soft-sys.math.scilab


This document was translated from LATEX by HEVEA.