PC n°5 : Méthode PERT

publicité
Cours ISC – 2014-2015
PC5 : Méthode PERT
PC n°5 : Méthode PERT
Question 1 : Créer le graphe pour l'exemple de construction de maison décrit dans la feuille
Excel "maison1.csv".
On utilise ici l'approche "Activity on Nodes" où les sommets du graphe représentent les tâches et les
arcs représentent les dépendances entre les tâches (slide n°32 du cours).
On rappelle que l'objectif de la méthode PERT est de déterminer entre autres :
 La durée minimale du projet
 Les dates au plus tôt et au plus tard pour chaque tâche afin que le projet ne prenne pas de retard.
 Les tâches qui sont sur le chemin critique
La première étape dans l'élaboration du diagramme PERT consiste à construire le graphique (sommets
et arcs) à partir du fichier "maison1.csv".
Dans ce fichier est écrit un tableau dont chaque ligne représente une tâche du projet (ie. un sommet
du graphe). Chacune de ces tâches est décrite par un code ("A", "B", "C", etc.), une description
textuelle, une liste de tâches prérequises et une durée.
C'est la liste des tâches prérequises qui permet de déterminer les arcs du diagramme PERT.
Par exemple, on peut voir que la tâche "D" (Creusement des fondations) ne peut être réalisée qu'à
partir du moment où les tâches "A" (Etude, réalisation et acceptation des plans) et "B" (Préparation du
terrain) sont achevées. Il y donc une dépendance entre les tâches "A" et "D" et entre les tâches "B" et
"D". Cela se traduit par la présence d'un arc entre les sommets "A" et "D" ainsi qu'entre les sommets
"B" et "D" du diagramme PERT.
code description
D
Creusement des fondations
predecessors duration
A, B
1
Il ne faut pas oublier d'ajouter les tâches fictives de début et de fin de projet (représentées par des
cercles). Les tâches qui n'ont pas de prérequis dans "maison1.csv" ont donc néanmoins cette tâche
fictive comme prérequis. Dans le diagramme, chacune de ces tâches sera donc reliée par un arc à cette
tâche fictive de début de projet.
Réciproquement, si une tâche n'est prérequise pour aucune autre, alors elle sera néanmoins
prérequise pour la tâche fictive de fin de projet. De même, un arc reliera les tâches concernées à la
tâche fictive de fin de projet dans le diagramme PERT.
Voir diagramme complet dans le fichier PERT maison 1.pdf
1
Cours ISC – 2014-2015
PC5 : Méthode PERT
Question 2 : Calculer les dates au plus tôt et au plus tard pour cet exemple.
Les dates au plus tard ne peuvent être calculées qu'une fois que l'on connait la durée minimale du
projet. Or le plus simple pour connaître la durée minimale du projet est de calculer les dates au plus
tôt de chaque tâche.
On commence donc par calculer les dates de début au plus tôt et de fin au plus tôt. Les dates de début
au plus tard et de fin au plus tard seront calculées après.
La procédure de calcul des dates au plus tôt est la suivante :
 On part de la tâche fictive de début de projet qui, par définition, commence à la date 0 et a
une durée nulle (et se termine donc également à la date 0).
 On suit le ou les arc(s) du graphe à partir de ce sommet. Ainsi, la ou les tâche(s) qui dépendent
de cette tâche fictive auront leur date de début au plus tôt égale à la date de fin au plus tôt
celle-ci. En l'occurrence, les tâches "A" et "B" ont donc leur date de début au plus tôt égale à
0.
 On en déduit les dates de fin au plus tôt pour ces tâches-ci. Pour cela il suffit d'additionner la
date de début au plus tôt avec la durée de la tâche. Ici, la tâche "A" s'achève donc au plus tôt
à la date 0+4=4 et la tâche "B" à la date 0+2=2.
 On poursuit ainsi progressivement l'avancement dans le graphe en suivant les dépendances
entre les tâches (les arcs du graphe). Si une tâche a plusieurs tâches prérequises alors sa date
de début au plus tôt sera égale à la plus grande date de fin au plus tôt de ses prérequis. Par
exemple, la tâche "D" ne peut commencer que lorsque "A" et "B" sont achevées. Or "A"
termine au plus tôt à la date 4 et "B" à la date 2 donc "D" ne peut commencer au plus tôt qu'à
la date 4.
=max(2,4)
+ durée
Ainsi, le calcul les dates au plus tôt pour toutes les tâches du projet permet de conclure que la durée
la plus courte du projet est de 17 unités de temps. Cette date correspond à la date de fin au plus tôt de
la tâche fictive de fin de projet.
La procédure de calcul des dates au plus tard est similaire à la procédure de calcul des dates au plus
tôt avec quelques différences :
 On part de la tâche fictive de fin de projet plutôt que de la tâche fictive de début. Les dates au
plus tard et au plus tôt de la tâche fictive de fin de projet sont identiques par définition.
 On parcourt progressivement le graphe en sens inverse. Pour une tâche donnée on calcule
d'abord la date de fin au plus tard puis on soustrait la durée de la tâche pour obtenir la date
de début au plus tard.
 La date de fin au plus tard d'une tâche donnée est prise égale à la plus petite date de début au
plus tard des tâches dont elle est prérequise. Par exemple, dans le cas de maison1, la date de
fin au plus tard de la tâche "A" est égale à la plus petite date de début au plus tard entre les
tâches "C", "D" et "E". En l'occurrence la date de fin au plus tard de "A" est donc égale à 4. En
2
Cours ISC – 2014-2015
PC5 : Méthode PERT
effet, la tâche "A" ne peut pas se terminer plus tard qu'à la date 4 car la tâche "E" (qui dépend
de "A") ne peut commencer au plus tard qu'à la date 4.
- durée
=min(4,7,9)
Question 3 : Donner une caractérisation de la notion de chemin critique et déterminer le
chemin critique sur l'exemple.
Le chemin critique est caractérisé par l'ensemble des tâches dont les dates au plus tôt sont égales aux
dates au plus tard. Ces tâches sont critiques car elles n'ont aucune marge de manœuvre temporelle.
Tout retard dans l'exécution d'une de ces tâches entrainera nécessairement un retard sur la fin de
projet.
Pour maison1 le chemin critique correspond donc aux tâches A, E, H et J.
Voir diagramme complet dans le fichier PERT maison 1.pdf
Question 4 : Proposer un algorithme de calcul des dates au plus tôt et compléter le
programme "mypertcalculator.py" avec cet algorithme (méthode "calculateEarlyDates" de
la classe Calculator).
Pour le calcul des dates au plus tôt, on propose un algorithme récursif.
Pour chaque tâche du projet on compare sa date de début au plus tôt avec la date de fin au plus tôt
de tous ses prédécesseurs. On rectifie la date de début au plus tôt si elle est inférieure à la date de fin
au plus tôt d'un prédécesseur.
3
Cours ISC – 2014-2015
PC5 : Méthode PERT
Voici le détail de l'implémentation :
def CalculateEarlyDates(self, project):
for task in project.tasks.values(): #boucle d'initialisation sur toutes les tâches du projet
task.earlyStart = 0
task.earlyFinish = task.earlyStart + task.duration
for task in project.tasks.values(): #Pour chaque tâche on appelle la sous-méthode récursive
self._CalculateEarlyDates(task)
def _CalculateEarlyDates(self, task):
#fonction appelée récursivement
for pred in task.predecessors:
#On boucle sur tous les prédécesseurs de la tâche en question
if pred.code != "source": #On appelle récursivement la méthode jusqu'à ce qu'on arrive à la tâche "source"
self._CalculateEarlyDates(pred)
if pred.earlyFinish > task.earlyStart: #On rectifie en cas d'incohérence
task.earlyStart = pred.earlyFinish
task.earlyFinish = task.earlyStart + task.duration
Question 5 : Proposer un algorithme de calcul des dates au plus tard et compléter le
programme "mypertcalculator.py" avec cet algorithme (méthode "calculateLateDates" de
la classe Calculator).
L'algorithme de calcul des dates au plus tard fonctionne sur un principe identique que l'algorithme de
calcul des dates au plus tôt. La boucle d'initialisation des tâches est adaptée. Le prérequis pour que
l'algorithme fonctionne est d'avoir déjà calculé les dates au plus tôt.
Voici le détail de l'implémentation :
def CalculateLateDates(self, project):
self.CalculateEarlyDates(project) #le préalable est de calculer les dates au plus tôt
for task in project.tasks.values(): #boucle d'initialisation sur toutes les taches du projet
task.lateStart = task.earlyStart
task.lateFinish = INFINITY
if task.code == "target":
task.lateFinish = task.lateStart
for task in project.tasks.values(): #Pour chaque tâche du projet on appelle la sous-méthode récursive
self._CalculateLateDates(task)
def _CalculateLateDates(self, task):
for succ in task.successors: #On boucle sur tous les successeurs de la tâche en question
if succ.code != "target":
#On appelle récursivement la méthode jusqu'à ce qu'on arrive à la tâche "target"
self._CalculateLateDates(succ)
if succ.lateStart < task.lateFinish: #On rectifie en cas d'incohérence
task.lateFinish = succ.lateStart
task.lateStart = task.lateFinish - task.duration
Question 6 : Proposer un algorithme permettant d'extraire les tâches se trouvant sur un
chemin critique et compléter le programme "mypertcalculator.py" avec cet algorithme
(méthode "ExtractCriticalTasks" de la classe Calculator).
Déterminer si une tâche est critique ou non est relativement aisé. Il suffit de regarder si les dates au
plus tôt et les dates au plus tard sont identiques. Si tel est le cas, alors la tâche est sur le chemin critique.
4
Cours ISC – 2014-2015
PC5 : Méthode PERT
Pour chaque tâche on calcule la marge de manœuvre (slack). Si elle est égale à zéro alors on ajoute la
tâche à la liste des tâches critiques.
On donne le détail du code :
def DetectCriticalTasks(self, project):
CriticalTasks = []
#On initialise une liste vide qui va contenir toutes les tâches critiques
for task in project.tasks.values():
#On boucle sur toutes les tâches du projet pour calculer la marge de manœuvre (slack)
task.slack = task.lateStart - task.earlyStart
if task.slack == 0:
#si la marge de manœuvre est nulle alors il s'agit d'une tâche critique
CriticalTasks.append(task)
#On ajoute la tâche à la fin de la liste
print([t.code for t in CriticalTasks]) #affichage de la liste à l'écran
Question 7 : Appliquer ces différentes méthodes sur les deux projets décrits dans les feuilles
de calcul "maison1.csv" et "maison2.csv".
Lorsque l'on applique ces méthodes sur le projet maison1 on obtient les mêmes résultats que ce qui
avait été calculé à la main. Cette constatation est rassurante sur le fait que l'algorithme proposé (et
son implémentation) est dépourvu de bogue. Il ne s'agit cependant pas d'une preuve.
L'algorithme peut alors être testé sur le projet maison2. Le diagramme PERT obtenu peut être visualisé
en pièce jointe.
Voir PERT maison 2.pdf
Une vérification manuelle des résultats rassure à nouveau sur la justesse du programme.
La date de fin de projet au plus tôt pour maison2 est 34 unités de temps.
Le chemin critique est A, B, C, D, G, H, I, L, N, O, P et Q.
Question 8 (optionnelle) : Jusqu'à présent, nous avons supposé que le modèle est correct et
en particulier qu’il ne contient pas de boucle. Compléter la méthode « DetectLoops » de la
classe « Reader » de façon à détecter la présence éventuelle de boucles dans le modèle.
Tester cette méthode en ajoutant une boucle dans l’un des deux projets.
Il y a présence d’une boucle lorsque, par exemple, une tâche B a comme prédécesseur (direct ou
indirect) une tâche A mais que cette même tâche A a également B comme prédécesseur (direct ou
indirect). Une telle situation constitue un blocage car on ne peut pas démarrer B tant que A n’est pas
terminé mais on ne peut pas commencer A tant que B n’est pas terminé.
L’objectif ici est de vérifier, dès la lecture du fichier d’entrée, que le modèle est correct de par l’absence
de boucle. La méthode « DetectLoops » a pour mission de détecter la présence d’une boucle et le cas
échéant d’afficher la boucle en question à l’écran.
On propose ici aussi un algorithme récursif pour parvenir à ce résultat. Le principe est de parcourir
pour toutes les tâches du projet, leurs prédécesseurs. Les prédécesseurs des prédécesseurs sont
parcourus de façon récursive. Si à un moment donné on retombe sur la tâche de départ alors cela
signifie qu’il y a présence d’une boucle.
Pour mettre en place cette stratégie, on utilise des marqueurs (flags) sur chacune des tâches. Pour
chaque tâche il y a trois marqueurs possibles :


« to-visit » (valeur par défaut) signifie que l’on n’a pas encore cherché à vérifier si cette tâche
fait partie ou non d’une boucle.
“visited” signifie que l’on est en train de vérifier si la tâche fait partie d’une boucle.
5
Cours ISC – 2014-2015

PC5 : Méthode PERT
“OK” signifie que l’on est sûr que la tâche ne fait pas partie d’une boucle.
Ainsi, si lors du parcours des prédécesseurs on tombe sur une tâche marquée « visited » alors on a
affaire à une boucle. Inversement, si l’on ne trouve que des tâches marquées « OK » alors on est sûr
que la tâche en question n’est pas part d’une boucle. On applique la procédure de façon récursive sur
les tâches marquées « to-visit ».
Le détail de l’implémentation est la suivante :
def DetectLoops(self, project):
for task in project.tasks.values():
task.flag = "to-visit"
for task in project.tasks.values():
self._DetectLoops(task, [])
print("No loop detected")
for task in project.tasks.values():
task.flag = None
# initialising all flags to "to-visit"
# the _DetectLoops method is run for all the tasks in the project
# if a loop is detected the program will stop here
# resetting flags to None
def _DetectLoops(self, task, stack): # recursive method
if task.flag == "visited":
# if the flag has already been visited it means that there is a loop !
stack = stack[stack.index(task.code):] # removing all irrelevant tasks in the stack before it is displayed
self.RaiseError("There is a loop: %s ! There might be other loops. Exiting procedure..." %(stack))
elif task.flag == "OK":
# if the flag is "OK" it means that this task has already been checked
return
elif task.flag == "to-visit": # if the task has not been validated then all its predecessors must be checked
task.flag = "visited"
stack.append(task.code) # this task is potentially part of a loop
for predecessor in task.predecessors:
self._DetectLoops(predecessor, stack) # if a loop is detected the program will stop here
task.flag = "OK"
# if all predecessors are "OK" then this task is "OK" too
stack.remove(task.code) # thus this task can be removed from the stack as we know it is not in a loop
return
else:
self.RaiseError("Unexpected flag ! Could not check loops.")
Cette méthode a été testée sur le modèle maison2 – Loop1.csv avec succès. Le diagramme
correspondant à ce modèle est visible dans le fichier PERT maison 2 - Loop1.pdf.
There is a loop: ['E', 'Z', 'Y', 'X'] ! There might be other loops. Exiting procedure...
L’algorithme a été testé sur d’autres boucles pour tester sa robustesse (absence de faux négatifs). Il a
aussi été vérifié par l’exemple qu’il n’y a pas de faux positifs (détection d’une boucle alors qu’il n’y en
a pas).
6
Téléchargement