Upload
trandung
View
221
Download
3
Embed Size (px)
Citation preview
2014-2015
Encadrant : Bertrand LE GAL
Programmation Linéaire Projet SE PR310
DUROU Baptiste
FAUCHER François
LE PAILLEUR Wilfried
TRUCHOT Clément
1
Table des matie res
INTRODUCTION ................................................................................................................................................ 2
CONTEXTE ET APPLICATION ............................................................................................................................. 3
1. CONTEXTE ..................................................................................................................................................... 3
2. CORRECTION D’ERREURS .................................................................................................................................. 3
3. RAPIDITE ....................................................................................................................................................... 4
PROGRAMMATION LINEAIRE : DEFINITIONS .................................................................................................... 5
1. DEFINITION .................................................................................................................................................... 5
2. PROBLEME DU MAXIMUM ................................................................................................................................. 5
3. PROBLEME DU MINIMUM ................................................................................................................................. 6
4. OBTENIR UN PROBLEME SOUS FORME STANDARD .................................................................................................. 6
5. UN EXEMPLE POUR COMPRENDRE ...................................................................................................................... 7
PROGRAMME PRINCIPAL ................................................................................................................................. 9
1. DESCRIPTIONS DES FICHIERS TXT A UTILISER .......................................................................................................... 9
2. ALGORITHME UTILISE ..................................................................................................................................... 10
3. FONCTIONS EN LANGAGE C ............................................................................................................................. 13
4. UTILISATION DU PROGRAMME ......................................................................................................................... 14
TEST DE L’ALGORITHME ................................................................................................................................. 15
1. CHOIX DU SOLVEUR DE REFERENCE ................................................................................................................... 15
2. PREMIERE MACRO DE VERIFICATION : SOLVEUR_UN_PB.XLSM ................................................................................ 15
2.1. Fonctionnement .............................................................................................................................. 15
2.2. Code de la macro solveur_un_pb.xlsm ............................................................................................ 17
VERIFICATION A GRANDE ECHELLE ................................................................................................................. 18
1. MACRO SOLVEUR_PLUSIEURS_PB.XLSM ............................................................................................................ 18
1.1. Fonctionnement .............................................................................................................................. 18
1.2. Code de la macro solveur_plusieurs_pb.xlsm .................................................................................. 19
2. MACRO DE COMPARAISON ............................................................................................................................. 20
3. RESULTATS DES TESTS .................................................................................................................................... 21
PERFORMANCES............................................................................................................................................. 23
1. FONCTION CLOCK() ........................................................................................................................................ 23
2. TIMER TSC .................................................................................................................................................. 24
OPTIMISATION ............................................................................................................................................... 27
1. CODAGE ...................................................................................................................................................... 27
2. TESTS ......................................................................................................................................................... 27
3. ACCES MEMOIRE ........................................................................................................................................... 28
CONCLUSION .................................................................................................................................................. 29
1. ANALYSE DU TRAVAIL ..................................................................................................................................... 29
2. CONCLUSION GENERALE ................................................................................................................................. 29
2
Introduction
L’objectif de ce projet est de réaliser un solveur permettant de résoudre des problèmes de
programmation linéaire. Apres avoir validé le bon fonctionnement du solveur il nous a fallu
l’améliorer en terme d’efficacité et étudier son implantation sur différentes cibles matérielles.
L’ouvrage de base nécessaire à notre compréhension de la programmation linéaire et à la résolution
de problèmes linéaires est la publication de Thomas S. Ferguson (professeur à l’université de
Californie) intitulée « Linear Programming: A Concise Introduction ».
3
Contexte et application
Cette partie a pour but de présenter l’application potentielle de notre projet. En effet, l’objectif
est de pouvoir utiliser la programmation linéaire dans le domaine des communications numériques.
Nous allons donc voir le contexte d’utilisation sans rentrer dans les détails techniques de l’application
qui sont inutiles pour notre travail et car cela nécessiterait un rapport complet pour le présenter.
1. Contexte
Aujourd’hui avec l’augmentation de l’utilisation d’internet et surtout l'accroissement du volume
de donnée consommé le débit des connexions internet est devenu très important. Lors d’un échange
de donnée la fiabilité des informations échangées n’est pas sûre à 100% car il peut y avoir des erreurs
de transmission dues à des interférences ou à du bruit dans le canal de transmission par exemple.
L’opérateur doit en revanche garantir une bonne qualité de service, c’est pour cela qu’il est
nécessaire de vérifier que les données envoyées correspondent bien aux données reçues.
Par exemple l’ajout d’un bit de parité est un moyen simple pour détecter des erreurs. En effet, si la
parité du message ne correspond pas, on peut en déduire qu’il y a un bit incorrect (ou plusieurs). En
revanche il est impossible de savoir lequel est faux pour le corriger et il est nécessaire que le message
complet soit réémis ce qui implique une perte de débit.
Le but de notre application serait de détecter et surtout de corriger les erreurs au moment de la
réception.
2. Correction d’erreurs
La méthode utilise une matrice complète sur plusieurs paquets de données. Lors de la réception
un coefficient de fiabilité compris entre 0 et 1 est associé à chaque bit en fonction du niveau
(puissance) reçu. A partir de la matrice et des ces coefficients cela permet de construire un problème
d’optimisation linéaire.
La résolution du problème permet de déterminer le ou les bits qui ont la plus grande probabilité
d’être faux en tenant compte de leur coefficient associé et de la matrice d’erreur. Cette méthode
n’est pas fiable à 100% car elle se base sur de l’optimisation et sur des probabilités, mais elle
permettrait dans la majeure partie des cas de corriger les erreurs en évitant que les données soient
envoyées une deuxième fois.
4
3. Rapidité
Cette méthode nécessite de résoudre rapidement des problèmes d’optimisation linéaire car si la
durée pour corriger les erreurs est supérieure au temps nécessaire pour transmettre le message
complet la méthode perd tout son intérêt.
C’est le but de notre projet d’optimiser l’efficacité de la résolution de problèmes de programmation
linéaire.
5
Programmation line aire : de finitions
1. Définition
La programmation linéaire également appelée optimisation linéaire consiste à résoudre un
problème mathématique constitué d’une fonction objectif et d’une série d’inéquations. La fonction
objectif est une fonction linéaire à plusieurs variables. La série d’inéquations impose des contraintes
sur ces variables. Le but du problème est de maximiser ou de minimiser la fonction tout en
respectant les contraintes fixées par les inéquations.
Pour les problèmes de programmation linéaire, il y a deux formes standard. On peut donc se réduire
qu’à deux types de résolution : la résolution du problème du maximum et la résolution du problème
du minimum. Tous les problèmes de programmation linéaire peuvent se mettre sous l’une de ces
deux formes standard. Dans les deux formes standard les inconnues doivent toujours être positives.
Lorsqu’on réalise une résolution, on peut être confronté à trois cas :
Le problème admet une solution bornée, il est faisable (Feasible)
Les solutions ne sont pas bornées, il est faisable non borné (Unbounded feasible)
Le problème n’admet pas de solution, il est infaisable (Infeasible)
2. Problème du maximum
Définition d’un problème de maximisation de programmation linéaire :
Figure 1 : Problème du maximum
6
3. Problème du minimum
Définition d’un problème de minimisation de programmation linéaire :
Figure 2 : Problème du minimum
4. Obtenir un problème sous forme standard
Il peut arriver que le problème de programmation linéaire ne soit pas sous forme standard. Dans
ce cas, on peut se toujours se ramener à une des deux formes standards. Lorsque le problème n’est
pas sous forme standard, on distingue deux cas :
Toutes les inéquations ne sont pas dans le même sens (présence de ≤ et ≥)
Présence d’équation en plus des inéquations
Certaines inconnues ne sont pas positives
Si certaines inéquations ne sont pas dans le bon sens, il faut les multiplier par -1 afin de n’obtenir que
des inéquations de même signe (soit que ≤ soit que ≥).
Si le problème présente une équation, il faut utiliser une méthode de substitution. Grâce à
l’équation, on isole une variable, puis on la remplace dans toutes les inéquations. S’il y a plusieurs
équations, il faut réitérer cette méthode.
Pour les inconnues non positives, on les remplace par la différence de deux variables positives Par
exemple pour 𝑥 non positif:
𝑥 = 𝑢 − 𝑣
𝑢 ≥ 0 𝑣 ≥ 0
7
5. Un exemple pour comprendre
Pour voir concrètement en quoi consiste un problème d’optimisation linéaire et ce qu’implique
sa résolution nous allons voir un exemple simple et sa résolution par une méthode graphique.
Considérons le problème suivant :
{
max𝑥,𝑦∈𝑅
𝑓(𝑥, 𝑦) = 4𝑥 + 5𝑥
𝑥 + 𝑦 ≤ 8 (𝑐1)𝑥 + 2𝑦 ≤ 12 (𝑐2)𝑥, 𝑦 ≥ 0
Le but est de trouver le couple (x,y) qui maximise la fonction f en respectant les contraintes. Pour
résoudre ce problème, nous allons faire une résolution graphique.
On trace sur un graphique les droites qui définissent les zones frontières des inéquations puis on ne
conserve que les zones qui sont commune à toutes les contraintes. On obtient la zone en vert sur le
graphique suivant (figure 3) :
Figure 3 : Représentation graphique des contraintes
Il a été démontré que les solutions optimales possibles sont sur les sommets du polygone obtenu.
Nous avons quatre sommets, donc 4 solutions potentielles : (0,0), (0,6), (4,4) et (8,0).
{
f(0,0) = 0
f(0,6) = 30f(4,4) = 36f(8,0) = 32
Donc on peut en déduire que la solution optimale est le couple x=4 et y=4.
8
Remarque 1 : on voit donc facilement avec la méthode graphique les différents cas que l’on peut
avoir lors de la résolution. En effet, si la zone en commun est nulle il n’y a pas de solution, le
problème est infaisable. Si la zone est infinie le problème est alors faisable mais non borné.
Remarque 2 : la résolution graphique est assez simple ici car la fonction n’est composée que de deux
variables. Si nous avions 3 variables il aurait fallu faire une résolution avec un graphique en trois
dimension ce qui tout de suite est plus complexe pour déterminer les sommets du polyèdre. Un
problème de dimension 4 ou plus est impossible à résoudre graphiquement.
Or notre algorithme doit être en mesure de résoudre des problèmes de dimension 30 000 donc il
nous faut une méthode de résolution plus adaptée.
La méthode que nous avons choisie est la méthode du Simplex.
9
Programme Principal
1. Descriptions des fichiers txt à utiliser
Notre programme peut générer des fichiers problèmes aléatoirement et les résoudre ou alors
résoudre des problèmes spécifiques. Dans les deux cas les fichiers problèmes sont des fichiers txt
comme le suivant.
Figure 4: Fichier problème
Sur la première ligne :
1 ou 0 pour un problème du maximum ou du minimum
Nombre de colonnes de la matrice (en comptant la colonne des contraintes)
Nombre de lignes dans la matrice (nombre d’équations + une ligne pour la fonction à
maximiser ou minimiser)
Ensuite, il s’agit de la matrice contenant les inéquations et la fonction de maximisation (ou de
minimisation).
Avec l’exemple ci-dessus, on a le problème :
−3𝑥1 − 9𝑥2 − 2𝑥3 − 1𝑥4 + 9𝑥5 − 10𝑥6 ≤ −7
−3𝑥1 − 8𝑥2 + 6𝑥3 + 0𝑥4 + 3𝑥5 − 6𝑥6 ≤ 9
−4𝑥1 − 1𝑥2 + 10𝑥3 − 2𝑥4 − 1𝑥5 + 0𝑥6 ≤ 4
𝑀𝐴𝑋:−10𝑥1 − 4𝑥2 + 6𝑥3 − 6𝑥4 − 7𝑥5 − 10𝑥6
On remarque que les coefficients de la dernière ligne sont multipliés par -1 entre le problème réel et
le tableau d’entrée du programme.
10
2. Algorithme utilisé
L’algorithme retenu est la méthode du simplex. Cette méthode utilise une matrice dite tableau
du simplex, voir figure 5. Elle est initialisée avec les conditions du problème, ainsi que la fonction
linéaire à maximiser ou minimiser.
Figure 5 - Tableau du simplex, avec A une matrice
L’algorithme consiste à trouver un pivot, transformer la matrice à l’aide de la « méthode du
rectangle » et recommencer ces deux opérations tant qu’on peut trouver un pivot. S’il n’y a pas de
pivot, il y a 3 possibilités :
une solution est trouvée
il n’y a pas de solution au problème
le problème n’est pas borné, la solution est donc infinie.
La méthode du rectangle est définie comme suivant :
Figure 6 - méthode dite du rectangle
Avec p le pivot, q la case à modifier, r sur la ligne du pivot et c sur la colonne du pivot. On va donc
dans un premier temps modifier toutes les valeurs de la matrice qui ne sont ni sur la ligne ni sur la
colonne du pivot avec la formule q – (rc/p), ensuite modifier la ligne du pivot avec r/p et la colonne
du pivot avec –c/p, et enfin modifier la case du pivot avec 1/p.
Pour trouver un pivot, il y a deux cas à considérer :
Si toutes les valeurs de la colonne b sont positives
Si au moins une des valeurs de la colonne b est strictement négative
Si les b sont positifs :
Prendre n’importe quelle colonne avec la valeur de la ligne –c négative. Soit j0 cette colonne,
avec –cj0 < 0.
Pour les lignes i de la colonne j0 pour les quelles ai,j0 > 0, choisir i0 tel que le ratio bi/ai,j0 soit le
plus petit. S’il y a des égalités, peu importe le i0 choisi parmi ces valeurs.
Pivoter autour de ai0,j0.
11
Figure 7 - Exemple du cas b positifs
Dans l’exemple de la figure 7, les pivots possibles sont entourés. Plusieurs pivots possibles peuvent
en effet être trouvés pour un même tableau.
S’il n’est pas possible de trouver une colonne avec –ci < 0, alors la résolution est terminée et une
solution est trouvée.
Si l’on a trouvé –cj0 < 0 mais que l’on ne peut pas trouver de ai,j0 > 0, alors le problème est faisable
mais non borné.
Si au moins un bi est négatif :
Choisir la ligne avec le premier bi négatif, bk < 0.
Prendre n’importe quelle case négative sur la ligne k, ak,j0 < 0. (Le pivot sera dans la
colonne j0)
Comparer bk/ak,j0 et bi/ai,j0 pour lesquels bi ≥ 0 and ai,j0 > 0, et choisir i0 tel que ce ratio soit
le plus petit (i0 peut être égal à k). S’il y a des égalités, peu importe le i0 choisi parmi ces
valeurs.
Pivoter autour de ai0,j0.
Figure 8 - Exemple du cas bi négatif
Dans l’exemple de la figure 8, les pivots sont entourés. Plusieurs pivots sont également possibles
pour un même tableau.
Si l’on a trouvé bk < 0, mais que l’on ne peut pas trouver de ak,j0 < 0, alors le problème est infaisable, il
n’y a pas de solution.
12
Si la résolution est finie est qu’il y a une solution, elle peut être lue grâce à la matrice obtenue. Ci-
dessous un exemple pour illustrer la lecture de la solution.
Minimiser 3y1 − 2y2 + 5y3 avec yi ≥ 0 et
− y2 + 2y3 ≥ 1
y1 + y3 ≥ 1
2y1 − 3y2 + 7y3 ≥ 5.
Ci-dessous la mise en tableau et le premier pivot correspondant à ce problème.
Figure 9 - Exemple de résolution d'un problème de minimisation - premier pivot
On remarque que y2 et x1 ont permuté. Ce sont ces permutations qui vont permettre de lire le
résultat de la résolution.
Figure 10 - Exemple de résolution d'un problème de minimisation - problème résolu
Ici toutes les valeurs de la dernière ligne sont positives, on a donc trouvé une solution. La solution
peut être lue en associant les yi de la première ligne avec les valeurs de la dernière ligne. On obtient
donc y1=0, y2=2/3 et y3=1, avec une valeur de minimum lue dans la case en bas à droite, égale à 11/3.
Pour un problème de maximum, les résultats sont à lire sur les lignes et non les colonnes, avec les x
correspondants.
13
3. Fonctions en langage C
Tout d’abord, nous avons créé une structure de données pour pouvoir stocker la matrice et les
informations nécessaires pour résoudre le problème :
Nous avons ensuite défini les fonctions de l’algorithme principal :
La fonction « pivot » appelle les deux autres fonctions, et retourne 0 quand il n’y a plus de pivot. Une
boucle sur cette fonction tant qu’elle ne retourne pas 0 permet donc de résoudre un problème
donné en utilisant la méthode du simplex vue précédemment.
Nous avons également des fonctions secondaires :
struct Matrix_ {
int max; // 0 : minimum, 1 : maximum
double **tab; // matrice
int length;
int width;
int *row_sol_min; // contient les x et les y à permuter pour lire les
solutions
int *column_sol_max; // contient les x et les y à permuter pour lire
les solutions
int solution_type; // 0 : init value, 1 : no_solution, 2 :
unbounded feasible, 3 : solution_ok
};
int pivot(Matrix * matrix); // modifie la matrice en effectuant un pivot
complet, retourne 0 quand il n’y a plus de pivot
double find_pivot(Matrix * matrix, int * p_length, int * p_width); //
retourne la valeur du pivot et ses index à partir de la matrice
void rectangle_method(Matrix * matrix, int p_length, int p_width, int
length_to_calculate, int width_to_calculate); // methode du rectangle,
modifie la valeur du coefficient à calculer dans la matrice
void display_matrix(Matrix * matrix); // affiche la matrice dans le terminal
void save_to_file(Matrix * matrix, char * file_name); // sauvegarde la
matrice dans un fichier texte
void load_from_file(Matrix * matrix, char * file_name); // charge la matrice
à partir d'un fichier texte
void random_generator(int length_size, int width_size, int borne_inf, int
borne_sup, int min_max, char *filename); // génère aléatoirement une matrice
avec les paramètres donnés en argument
void print_solutions(Matrix * matrix); // affiche les solutions dans le
terminal
void save_solution_to_file(Matrix * matrix, char * file_name); // sauvegarde
les solutions dans le terminal
14
Les fonctions « display_matrix » et « print_solutions » permettent d’afficher des informations sur le
terminal quand le programme s’exécute. Il est ainsi possible de voir l’évolution de la matrice après
chaque pivot.
Les fonctions « save_to_file », « load_from_file » et « save_solution_to_file » permettent d’interagir
avec un fichier texte et ainsi garder en mémoire un problème et sa solution, pour ensuite pouvoir la
comparer avec un autre solveur, comme nous le verrons dans la prochaine partie.
« random_generator » permet de générer un problème aléatoire en fonctions de paramètres comme
la taille du problème à générer, le type (maximisation ou minimisation) et les bornes inférieures et
supérieures des coefficients.
4. Utilisation du programme
Afin d’utiliser notre programme il faut veiller à créer le dossier « dossier_test_1 » dans le même
répertoire que l’exécutable.
Après exécution du programme, on trouve les fichiers problèmes et ceux des solutions associées
dans ce dossier.
Pour modifier les conditions de génération des fichiers problèmes ainsi que leur nombre, il faut
modifier les arguments de la fonction « random_generator » dans le code.
15
Test de l’algorithme
Afin de vérifier le bon fonctionnement de notre algorithme, nous avons dû trouver un
programme de référence afin de comparer les résultats. Cela permet de s’assurer que nous
fournissons toujours les bons résultats avant de commencer le travail d’optimisation.
1. Choix du Solveur de référence
Il existe de nombreux solveur en ligne mais leur utilisation n’est pas forcement simple de
manière automatique. En effet le formatage des données en entrée n’est pas forcément le même
que celui de notre programme. Il fallait donc rentrer les valeurs du problème une par une dans les
solveurs en ligne. Nous nous sommes donc tournés vers des solveurs intégrés aux logiciels de calculs
à notre disposition. Les logiciels Matlab et Excel permettent de résoudre les problèmes de
programmation linéaire.
Matlab possède une fonction permettant de résoudre les problèmes du minimum. Il s’agit de la
fonction linprog. Excel dispose d’un module solveur qui permet de résoudre les problèmes du
maximum et du minimum. Le solveur d’Excel étant plus simple d’utilisation et permettant de
résoudre les deux types de problèmes (maximum et minimum), il a donc été choisi pour être la
référence lors de notre projet. De plus, Visual Basic permet de programmer Excel afin d’automatiser
des traitements et de ne pas avoir à rentrer et paramétrer le solveur à la main.
2. Première macro de vérification : solveur_un_pb.xlsm
2.1. Fonctionnement
La première macro Excel réalisée permet de résoudre un problème donné au format de notre
programme principal. Lors de l’exécution de la Macro, le fichier problème est demandé au format txt.
Ce fichier contient les informations suivantes :
Problème du maximum ou du minimum
Les inéquations
La fonction de minimisation ou de maximisation
Ensuite la macro fournit le résultat et l’affiche dans une feuille de calcul. On obtient une feuille telle
que celle présentée figure 11 :
16
Figure 11 : Feuille résultat de solveur_un_pb.xlsm
On peut lire les résultats dans les cellules surlignées en jaune. La ligne quantité correspond aux
valeurs des inconnues à trouver. La cellule résultat indique la valeur de la fonction de maximisation
(ou minimisation pour un problème du minimum). La ligne Z correspond aux coefficients de la
fonction de maximisation (ou minimisation).
Cette macro a permis de vérifier le bon fonctionnement de notre algorithme sur certains exemples et
de le corriger si des écarts étaient constatés. Cependant, pour valider notre algorithme, cette macro
n’était pas suffisante car il faut automatiser la résolution des problèmes. Une autre macro a donc été
développée afin de résoudre plusieurs centaines de problèmes à la suite.
Il faut cependant noter que le solveur Excel ne peut pas résoudre des problèmes de dimensions trop
importantes. Il peut y avoir au plus 200 inconnues et 100 inégalités.
Figure 12 : Limite du solveur Excel
variable x1 x2 x3 x4 x5 x6
quantite 0 0 0,4 0 0 0,62
Z -10 -4 6 -6 -7 -10
Résultat -3,8
equation calc ligne contrainte
ligne1 -3 9 -2 -1 9 -10 -7 -7
ligne2 -3 -8 6 0 3 6 6,12 9
ligne3 -4 -1 10 -2 -1 0 4 4
17
2.2. Code de la macro solveur_un_pb.xlsm
Cette macro est divisée en deux parties. La première partie consiste à préparer et remplir la
feuille de calcul. Il faut lire le fichier texte indiqué par l’utilisateur, remplir les cellules avec tous les
coefficients du fichier texte, calculer les équations dans les cellules de la colonne cellules calc ligne et
calculer la fonction de maximisation(ou minimisation) dans la cellule résultat.
La seconde partie de la macro consiste à exécuter le solveur Excel. Il suffit d’indiquer au solveur les
équations calculées dans la première partie de la macro.
18
Ve rification a grande e chelle
Pour cette partie des problèmes du minimum et du maximum ont été testés. Les observations
faites sont valables pour les deux types de problèmes.
1. Macro solveur_plusieurs_pb.xlsm
1.1. Fonctionnement
La première macro Excel a été modifiée afin de pouvoir tester un nombre très important de
problèmes à la suite. Les principales modifications sont donc l’écriture des résultats dans des fichiers
textes afin de pouvoir comparer les solutions obtenues avec Excel et avec notre programme.
Cette macro doit être exécutée dans le dossier où se situent les problèmes à résoudre. Les problèmes
doivent être nommés de la façon suivante :
tab_random000
tab_random001
…
tab_random999
Cette macro peut résoudre au maximum 1000 problèmes à la suite. Lors de l’exécution on demande
à l’utilisateur le nombre de problèmes à résoudre. Pour chaque problème qui a été traité un fichier
texte est généré dans le même répertoire que la macro. Le nom des fichiers correspondent aux
noms des problèmes :
sol_excel_random000
sol_excel_random001
…
sol_excel_random999
19
Figure 13 : Liste des fichiers du dossier de test
Si une solution est trouvée le fichier sol_excel_randomXXX.txt contient les différentes valeurs de X et
la valeur de la fonction de maximisation (ou minimisation obtenue). Si le solveur ne trouve pas de
solution, le fichier txt contient « No solution. ».
Figure 14 : Fichier solution généré par Excel
1.2. Code de la macro solveur_plusieurs_pb.xlsm
Il s’agit du même code que la macro « solveur_un_pb.xlsm » auquel a été ajouté une boucle sur
tous les fichiers problèmes. Pour chacun des fichiers problèmes, on génère un fichier txt avec les
solutions à la suite de l’exécution du solveur Excel.
20
2. Macro de Comparaison
Une macro de comparaison des fichiers txt résultat a été réalisée pour comparer les solutions
générées par le solveur Excel et celui que nous avons développé. Le comparateur a été développé en
Visual Basic afin d’avoir un temps de développement réduit par rapport au C. En effet, le
comparateur n’a pas de contrainte de temps d’exécution donc il n’était pas nécessaire de le réaliser
en C.
Le principal problème de la comparaison est de gérer les arrondis car les deux solveurs peuvent
obtenir la même solution mais avec des arrondis différents. On ne peut donc pas utiliser un simple
comparateur de fichier.
Comme pour la macro du solveur, cette macro doit être lancée dans le répertoire des fichiers à
comparer. On demande à l’utilisateur le nombre de fichier à traiter. Les fichiers à comparer doivent
s’appeler pour les solutions d’Excel :
sol_excel_random000
sol_excel_random001
…
sol_excel_random999
Pour les solutions de notre programme :
sol _random000
sol _random001
…
sol _random999
Le résultat de la comparaison est écrit dans le fichier resultat_comparaison.txt. Les fichiers
différents sont listés dans resultat_comparaison.txt. Si tous les fichiers solutions sont
identiques, le fichier contient « Tout est OK ».
Figure 15 : Fichier de comparaison
21
Pour que deux fichiers soient considérés comme identiques, il faut qu’ils contiennent le même
nombre de lignes. Ensuite il faut que les valeurs de chaque X et du résultat soient les mêmes avec
une précision de 6 chiffres après la virgule. Pour la comparaison toutes les valeurs sont arrondies à 6
décimales. Par exemple les deux fichiers suivants sont considérés comme égaux.
Figure 16 : Solution de notre solveur
Figure 17 : Solution du solveur Excel
3. Résultats des Tests
Une fois notre programme terminé et les macros réalisées nous avons pu lancer plusieurs
centaines de tests assez rapidement. En comparant les résultats nous avons pu nous assurer de la
robustesse de notre algorithme.
Nous avons pu observer que notre algorithme et le solveur d’Excel ne trouvent pas de solution
bornée dans les même cas et que lorsqu’une solution bornée est trouvée, les deux solveurs renvoient
la même solution.
Le seul cas où les deux solveurs ne renvoient pas la même solution bornée est le cas où cette solution
n’est pas unique. C’est-à-dire que toutes les contraintes sont respectées et que le résultat de la
fonction de maximisation (ou minimisation) est exactement le même dans les deux cas. Par exemple,
le problème suivant présente deux solutions équivalentes :
22
𝑀𝐴𝑋 ∶ 2𝑥1 + 9𝑥2 + 4𝑥3 + 5𝑥4 + 4𝑥5 + 3𝑥6 + 0𝑥7 + 10𝑥8 + 10𝑥9
3𝑥1 + 4𝑥2 + 10𝑥3 + 1𝑥4 + 1𝑥5 + 5𝑥6 + 5𝑥7 + 3𝑥8 + 9𝑥9 <= 2
4𝑥1 + 4𝑥2 + 8𝑥3 + 8𝑥4 + 3𝑥5 + 1𝑥6 + 5𝑥7 + 3𝑥8 + 3𝑥9 <= 1
6𝑥1 + 1𝑥2 + 0𝑥3 + 9𝑥4 + 2𝑥5 + 7𝑥6 + 2𝑥7 + 7𝑥8 + 9𝑥9 <= 3
4𝑥1 + 8𝑥2 + 1𝑥3 + 4𝑥4 + 10𝑥5 + 7𝑥6 + 7𝑥7 + 3𝑥8 + 4𝑥9 <= 10
Figure 18 : Solution différentes mais équivalentes
Dans cet exemple les solutions de gauche et de droite sont différentes mais respectent toutes les
deux les contraintes tout en fournissant un résultat de 3.33333 pour la fonction de maximisation. Les
deux solutions sont donc valides.
Les cas où il n’existe pas de solution bornée sont les même pour les deux solveurs. Notre programme
différencie le cas où la solution n’est pas bornée (problèmes Unbounded feasible) et le cas où le
problème n’est pas solvable. Tandis que le solveur d’Excel ne permet pas cette distinction de manière
évidente. On n’a donc pas pu vérifier la robustesse de la distinction entre les problèmes Unbounded
feasible et les problèmes non solvables.
Pour conclure, les différentes macros ont permis de valider notre algorithme. On est donc sûr que les
solutions renvoyées par notre algorithme sont valides. Lors que notre programme ne renvoie pas de
solution, l’étape de vérification nous a assuré qu’on est bien, soit dans le cas d’un problème
Unbounded feasible, soit dans un cas d’un problème sans solution. Les remarques établies
précédemment sont valables à la fois pour les problèmes du maximum et du minimum.
23
Performances
Une fois notre algorithme validé à l’aide du solveur Excel et du script de comparaison, il nous
a fallu mesurer les performances de notre algorithme afin d’en déterminer les parties critiques et
celles pouvant être optimisées. En effet, notre algorithme fait appel à des fonctions identiques qui
sont exécutées séquentiellement (multiples méthodes des rectangles). Ces dernières pourraient être
exécutées en parallèle afin d’accélérer le temps de résolution des problèmes.
Nous avons eu recours à deux types de mesures temporelles :
- La fonction clock(),
- Le timer RDTSC.
1. Fonction clock()
Dans un premier temps, nous avions besoin d’avoir une idée du temps d’exécution de notre
programme pour résoudre un problème. La fonction clock() incluse dans la librairie standard time.h
nous a permis d’avoir une estimation du temps nécessaire à la résolution d’un problème. Cette
fonction s’utilise de manière très simple : il suffit de stocker les valeurs renvoyées par l’appel de cette
fonction dans deux variables déclarées avant et après la portion de code que nous souhaitons
mesurer. En faisant la différence entre ces deux variables et en divisant le résultat obtenu par la
constante système correspondant à la fréquence du processeur (CLOCKS_PER_SEC) on obtient le
résultat du temps d’exécution en secondes.
Figure 19 : Utilisation de la fonction clock()
double time_start = clock();
/************************
Portion de code à mesurer
************************/
double time_stop = clock();
double delta_sec = (time_stop-time_start)/CLOCKS_PER_SEC;
printf("Temps : %lf s\n", delta_sec);
24
Nous avons testé cette fonction sur différentes tailles de matrices pour avoir une idée des temps de
résolution.
i5 4200M (2,5 GHz) Temps d’exécution d’une matrice (s)
Matrice 30 X 30 < 0,01
Matrice 300 X 300 0,07
Matrice 3000 X 3000 30
Tableau 1 : Mesure du temps d'exécution en secondes
Nous remarquons que les temps de résolution augmentent de manière exponentielle avec la taille de
la matrice. Ceci était attendu car avec l’augmentation de la taille de la matrice, le nombre de
recherches de pivots et le nombre de méthodes des rectangles augmente fortement.
Cependant, ces temps ne sont pas précis. En effet, dès que la matrice est de petite taille, il est
courant de voir un temps de 0 secondes. Ce problème réside dans la fonction clock(). Cette dernière
n’est pas très précise. De plus lorsqu’on divise le delta par la fréquence du processeur on commet
une erreur car dans les PC actuels la fréquence du processeur s’adapte en fonction de la charge CPU.
Cette dernière est donc variable. Notre mesure est donc une mesure très approximative.
Notre but étant de mesurer des temps qui peuvent être très faibles (comme le temps d’exécution
d’une seule méthode du rectangle) cette fonction n’est pas adaptée. Elle ne nous permet que d’avoir
une idée du temps d’exécution global.
2. Timer TSC
Pour avoir à des mesures plus précises nous avons utilisé un timer présent dans tous les
processeurs à architecture x86 depuis le Pentium : le timer TSC (Time Stamp Counter). Notre
algorithme ayant été développé sur des machines utilisant des processeurs Intel, nous avons pu
utiliser ce timer. En revanche, ce dernier n’est pas disponible sur les cartes possédant des
processeurs à architecture ARM.
Une fonction assembleur permet de lire la valeur sur 64 bits du compteur. Cette fonction est
cependant différente selon le compilateur utilisé.
Pour un compilateur GNU comme MinGW utilisé dans Qt, on peut utiliser la fonction suivante :
Figure 20: Fonction RDTSC du compilateur GNU (Qt)
Tandis que pour le compilateur Windows MSVC, il faut plutôt utiliser la fonction suivante :
#include <unistd.h>
static __inline__ unsigned long long timer() { unsigned hi, lo;
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long) lo) | (((unsigned long long) hi) <<
32);
}
25
Figure 21 : Fonction RDTSC du compilateur MSVC (Visual Studio)
Cette fonction renvoie un nombre de ticks d’horloge. De la même manière que précédemment, il
suffit donc d’encadrer la portion de code que nous souhaitons mesurer avec des appels de la
fonction timer().
Cela nous permet d’obtenir une mesure précise temporelle afin de pouvoir mesurer les gains lors de
l’optimisation. Les résultats des mesures sont donc, non pas en secondes, mais en ticks d’horloge ce
qui nous permet de savoir quelles parties du programmes sont gourmandes en ressources et
critiques.
Nous avons réalisé des mesures pour différentes tailles de matrices :
Tableau 2 : Mesures des ticks d’horloge pour chaque partie de l’algorithme
Matrice 30 X 30 Valeurs entre -10 et 30 Ticks
(moyenne) Temps d’exécution (ticks
d’horloge)
1 méthode du rectangle 60
4 752 804
Toutes les méthodes du rectangle
12000
Fonction find pivot() 1200
Matrice 300 X 300 Valeurs entre -10 et 30 Ticks
(moyenne) Temps d’exécution (ticks
d’horloge)
1 méthode du rectangle 70
162 799 282
Toutes les méthodes du rectangle
1050000
Fonction find pivot() 6500
Matrice 3000 X 3000
Valeurs entre -10 et 30 Ticks
(moyenne) Temps d’exécution (ticks
d’horloge)
1 méthode du rectangle 73
63 106 912 246
Toutes les méthodes du rectangle
113 000 000
Fonction find pivot() 72 560
// processor: x86, x64
#include <stdio.h>
#include <intrin.h>
#pragma intrinsic(__rdtsc)
unsigned long long timer()
{
unsigned __int64 i;
i = __rdtsc();
//printf_s("%I64d ticks\n", i);
return(i);
}
26
Ce tableau nous a permis de déterminer quelle partie de notre algorithme était la plus gourmande en
ressources et donc quelle partie pouvait être optimisée.
On observe que les méthodes des rectangles sont les étapes les plus souvent effectuées. Cependant,
dans notre version après chaque exécution de la fonction qui trouve le pivot, toutes les méthodes
des rectangles sont faites séquentiellement. Compte-tenu de l’algorithme utilisé ces dernières
pourraient être exécutées en parallèle. Ceci est confirmé par le fait qu’une seule méthode du
rectangle ne demande que très peu de temps pour être exécutée.
Nous avons donc décidé d’optimiser notre algorithme pour améliorer son efficacité temporelle. Pour
cela, nous avons choisi d’exécuter les calculs sur plusieurs threads. Cette optimisation a été réalisée
avec l’aide de la librairie OpenMP qui permet de paralléliser des portions de codes.
27
Optimisation
1. Codage
Comme nous l’avons vu précédemment, il n’est possible de paralléliser les calculs que pour
l’exécution des méthodes du rectangle. En effet, modifier une valeur de la matrice avant une autre
n’a pas d’importance dans notre algorithme. Cette parallélisation est réalisée avec une directive
OpenMP :
Figure 22 : Directive Pragma
Le nombre de threads que nous souhaitons créer est choisi grâce à la ligne :
omp_set_num_threads(X); // remplacer X par le nombre de threads
2. Tests
Afin de vérifier le bon fonctionnement de nos améliorations et de valider notre optimisation
nous avons dû effectuer une série de tests.
Pour cela, nous avons utilisé un PC équipé d’un processeur Intel i5 4200M possédant 2 cœurs
physiques donc 4 logiques. Dans ce cas le gain de performance maximal est de 4.
Cependant, en testant les temps d’exécution de l’algorithme en utilisant 1 thread ou 4 threads on
observe que les gains plafonnent à 2. Le tableau ci-après (tableau 3) donne les résultats pour
plusieurs configurations matérielles :
#pragma omp parallel for private(i,j ) //gain de 2 pour 4 threads
for (i = 0; i < matrix->length; i++){ // calcule les rectangles
if (i != p_length){
for (j = 0; j < matrix->width; j++){
if (matrix->tab[i][p_width] != 0 && j !=
p_width)
//timer1 = timer();
rectangle_method(matrix, p_length,
p_width, i, j);
//timer2 = timer();
//printf("rect %llu \n ", timer2 - timer1);
}
}
}
28
Matrice 3000 X 3000, Valeurs [-10 ---- 30]
Architecture Temps d’exécution 1 thread (s) Temps d’exécution 4 thread (s) Gain
i5 4200M (2,5 GHz) 32,6 17,04 1,91
51,6 26,32 1,96
46,9 23,57 1,99
32,15 16,09 2,00
43,7 21,93 1,99
i7 3632QM (2,2 GHz)
27,81 13,5 2,06
28,64 14,2 2,02
20,36 11 1,85
23,61 12,7 1,86
38,31 20,5 1,87
Tableau 3 : Gains de performances
3. Accès mémoire
Nous avons mesuré en détail le temps d’exécution des différentes étapes d’une méthode des
rectangles. Nous avons ainsi relevé les temps donnés dans le tableau ci-dessous (tableau 4) :
Méthode du rectangle 30 X 30 300 X 300 3000 X 3000
Lecture 77 61 70
Calcul 23 20 18
Ecriture 20 18 20
Total 120 97 115
Tableau 4 : Accès mémoire
On remarque que les temps d’accès à la mémoire (lecture et écriture des données) représentent
environ 80% du temps d’exécution d’une méthode des rectangles. Le temps de calcul ne représente
quant à lui que 20% du temps.
Ceci semble être à l’origine d’un gain plus faible que prévu.
29
Conclusion
1. Analyse du travail
Durant ce projet nous avons dû à de nombreuses reprises revoir nos attentes et repenser
l’étalement du projet dans le temps. En effet, initialement, nous avions prévu de passer peu de
temps à comprendre et implémenter (coder) l’algorithme et de passer l’essentiel du temps à
optimiser et implanter l’algorithme sur différentes architectures matérielles.
Cependant, la phase de codage a été plus compliquée que prévu et nous avons donc passé beaucoup
de temps à débugger notre programme. De plus, il nous a fallu valider notre algorithme en vérifiant
sa fiabilité. Pour cela, nous avons utilisé un solveur Excel permettant de résoudre les problèmes
linéaires. L’implémentation des scripts de comparaison et de test nous a pris beaucoup de temps,
temps que nous n’avions pas pris en compte lors de la phase de planification du projet.
Figure 23 : Répartition du temps
2. Conclusion générale
Ce projet nous a permis de nous confronter à une problématique réelle d’un ingénieur. En effet,
de par sa complexité, son étalement dans le temps et les problèmes rencontrés, ce projet nous a
donné une idée plus précise de ce qui nous sera demandé en entreprise dans le futur. Ainsi, nous
avons vu que le cahier des charges a dû être modifié (phase d’implantation raccourcie). Nous avons
par ailleurs apprécié pouvoir mener un projet dans son intégralité, du cahier des charges à
l’implantation et aux tests sur cible.
9%
17%
28%
28%
11%
7%
Répartition du temps passé sur le projet
Compréhension del'algorithme
Codage de l'algorithme
Phase de Debuggage
Validation
Analyse des performances
Optimisation et implantationsur cible