• Aucun résultat trouvé

Intégration d'une fonction de coût à la contrainte Disjunctive utilisée en ordonnancement

N/A
N/A
Protected

Academic year: 2021

Partager "Intégration d'une fonction de coût à la contrainte Disjunctive utilisée en ordonnancement"

Copied!
68
0
0

Texte intégral

(1)

Intégration d'une fonction de coût à la

contrainte

Disjunctive utilisée en

ordonnancement

Mémoire

Vincent Martel

Maîtrise en informatique - avec mémoire

Maître ès sciences (M. Sc.)

(2)

Intégration d’une fonction de coût à la contrainte

Disjunctive utilisée en ordonnancement

Mémoire

Vincent Martel

Sous la direction de: Claude-Guy Quimper

(3)

Résumé

La programmation par contraintes est une technique accélérant la recherche de solutions pour des problèmes d’optimisation combinatoire. Ce mémoire porte sur l’application de cette tech-nique en ordonnancement. Le but est d’intégrer une fonction de coût convexe à la contrainte Disjunctive qui régit l’ordre d’exécution d’un ensemble de tâches ne pouvant pas se chevau-cher dans le temps. Dans ce contexte, le coût est perçu comme un retard déterminé par une échéance préférable indiquée pour chaque tâche.

La contribution se traduit en l’introduction de la contrainte DisjunctiveTardiness qui tisse de nouveaux liens entre l’ordre d’exécution des tâches et la somme des retards engendrés. La cohérence de la contrainte est assurée par un algorithme de filtrage. L’algorithme raisonne à partir de la construction d’un réseau de flot pondéré basé sur la fenêtre d’exécution des tâches et leur échéance préférable. Il est implémenté dans un solveur et comparé à une alternative compétitive.

Tel qu’observé, le nouvel algorithme amène un filtrage tangible, mais sa complexité trop élevée empêche d’aboutir à un nouvel état de l’art en pratique. En revanche, plusieurs pistes de solution pour réduire le temps d’exécution sont proposées.

(4)

Abstract

Constraint programming is a technology originating from artificial intelligence that explores a search space to solve combinatorial problems. It uses filtering algorithms to filter the search space and speedup the search of a solution. This master thesis covers an application of this method in scheduling. The goal is to integrate a convex cost function to the Disjunctive constraint that governs the execution order of tasks unable to overlap each other on a time line. In this context, the cost is treated as a delay (tardiness) computed from a due date specified for each task.

The contribution translates in a new constraint named DisjunctiveTardiness that brings a stronger relation between the order in a schedule and the sum of tardinesses. Consistency of the constraint is achieved by a filtering algorithm. The algorithm builds a weighted network flow from the allowed time window of the tasks and their due date. The solution is implemented in a solver.

The experimental results show that the new algorithm applies stronger filtering, but its time complexity is too high to recommend it in practice. To conclude, several potential upgrades are proposed to reduce the execution time.

(5)

Table des matières

Résumé iii

Abstract iv

Table des matières v

Liste des tableaux vii

Liste des figures viii

Remerciements ix

Introduction 1

I Notions préliminaires 3

1 La programmation par contraintes en ordonnancement 4

1.1 Problème combinatoire . . . 4

1.2 Méthode de résolution . . . 6

1.3 Ordonnancement . . . 9

2 Optimisation 15 2.1 Réseaux de flot . . . 15

2.2 Applications pour la contrainte AllDifferent . . . 19

2.3 Applications pour la contrainte CostGCC . . . 22

II Contributions 26 3 Contrainte DisjunctiveTardiness 27 3.1 Définition de la contrainte . . . 27

3.2 Décomposition non satisfaisante . . . 27

3.3 Difficulté et popularité du problème . . . 28

3.4 Relaxation. . . 29

3.5 Filtrage avec l’approche CostGCC. . . 32

3.6 Compression du graphe . . . 37

(6)

4.1 Algorithmes comparés . . . 43

4.2 Comparaison avec instances résolubles . . . 44

4.3 Comparaison avec instances de la littérature . . . 47

4.4 Travaux futurs . . . 49

Conclusion 53 A Glossaire 54 A.1 Notations du mémoire en ordre d’occurrence et groupées par dépendance. . 54

(7)

Liste des tableaux

A.1 Notation du chapitre 1 . . . 54

A.2 Notation du chapitre 1 (suite) . . . 55

A.3 Notation du chapitre 2 . . . 55

A.4 Notation du chapitre 3 . . . 56

(8)

Liste des figures

2.1 Flot valide initial dans un graphe . . . 17

2.2 Graphe résiduel selon le flot initial . . . 17

2.3 Ajout d’un flot sur un chemin augmentant . . . 17

2.4 État des domaines dans l’exemple 1.3 . . . 19

2.5 Réseau de flot encodant l’état des domaines . . . 20

2.6 Graphe résiduel après l’application de Ford et Fulkerson [9] . . . 21

2.7 Réseau encodant l’instance pour CostGCC . . . 24

2.8 Flot optimal illustrant un support pour CostGCC . . . 25

3.1 Filtrage avec le réseau d’ordonnancement avec retards . . . 33

3.2 Graphe compressé pour l’exemple 3.3 avec seulement 3 nœuds de valeur . . . . 38

3.3 Graphe compressé pour l’exemple 3.1 avec au plus 3n − 1 intervalles . . . 40

4.1 Nombre de retours sur trace de DisjunctiveTardiness par rapport à la dé-composition avec branchement en ordre lexicographique . . . 45

4.2 Proportion cumulée d’instances pour lesquelles le nombre de retours sur trace est compétitif à un facteur près - MDT exclu . . . 46

4.3 Proportion cumulée d’instances pour lesquelles le retard de la solution est com-pétitif à un facteur près - COS avec branchement 4.1 . . . 48

4.4 Proportion cumulée d’instances pour lesquelles le temps pour obtenir une solu-tion compétitive est court à un facteur près - COS avec branchement 4.1 . . . . 49

(9)

Remerciements

Loin de moi l’idée d’être la personne la mieux placée, je me permets de témoigner de la géné-rosité incontestable de Claude-Guy Quimper. Génégéné-rosité qui s’est manifestée sous toutes les formes au cours de ma maîtrise. Claude-Guy m’a avant tout donné le goût pour la recherche et m’a confié un projet de son domaine qui lui tient à coeur. Il m’a ensuite fait une place dans son laboratoire et m’a entouré de collègues forts inspirants. Notamment, il m’a donné les moyens de m’investir à fond dans mon projet, privilège duquel je lui serai éternellement reconnaissant. Il m’a donné de précieux conseils dont les applications s’étendent aux péripéties du quotidien. Non exhaustivement, Claude-Guy s’est aussi montré généreux en écoute, en encadrement, en blagues et, surtout, en pistes de solution. En recherche, le nombre d’interrogations n’est pas borné par un polynôme. Heureusement, Claude-Guy est disponible en temps constant. Merci à toi Claude-Guy.

Je tiens tout autant à remercier Mel qui me soutient tous les jours. Elle est pour moi une source d’énergie inépuisable qui m’aide à me dépasser. Elle est aussi un modèle d’excellence en recherche que j’ai peine à suivre. Elle n’arrête jamais de me surprendre. À mon tour de le faire : Mélissa, veux-tu m’épouser ?

Merci aussi à ma famille qui a su prendre soin de moi malgré la distance et à Nath qui est là depuis le tout début de mon aventure à Québec.

Merci à Lewis de m’avoir poussé à faire des études supérieures. J’ai un dépassement de pile à me remémorer toutes les discussions passionnantes et tordantes que nous avons eu sur l’informatique théorique et qu’il est difficile de placer en cette ère de web omniprésent. Merci finalement au micro-ondes défectueux du Pouliot... Jamais je n’aurais pensé qu’un bidule de la sorte marque le début d’une boucle infinie de rassemblements d’amis autour d’une IPA.

(10)

Introduction

La complexité des défis rencontrés dans l’industrie est croissante. La ruée sur l’automatisation des processus d’affaires révèle constamment de nouveaux défis à relever. Qu’ils traitent de confection d’horaire ou de planification de la production dans une usine, ces défis peuvent se manifester sous la forme de problèmes d’ordonnancement. Le cas échéant, le but est d’exé-cuter dans un ordre adéquat un ensemble de tâches régies par des contraintes temporelles. La communauté en optimisation combinatoire se penche quotidiennement sur ces problèmes. Plusieurs méthodes de résolution efficaces ont été développées. Une méthode prometteuse pour le domaine de l’ordonnancement est la programmation par contraintes.

La programmation par contraintes a déjà fait ses preuves pour trouver efficacement des so-lutions optimales à des problèmes d’ordonnancement dont l’objectif est simple tel que la mi-nimisation du temps de complétion de l’ordonnancement (makespan). En revanche, ce n’est pas le cas pour les problèmes d’optimisation où l’objectif est plus complexe. La minimisation du retard impliqué dans un ordonnancement de tâches est un objectif typique. Ce mémoire découle de travaux entamés pour pallier le manque en programmation par contraintes vis-à-vis la résolution de problèmes d’optimisation dans le domaine de l’ordonnancement avec minimisation des retards.

Un problème combinatoire est composé de variables et de contraintes à satisfaire. Pour un problème d’optimisation, une fonction objectif est nécessaire pour quantifier la qualité d’une solution. Cette fonction doit être minimisée ou maximisée. La programmation par contraintes tire son efficacité de ses algorithmes de filtrage assurant la cohérence des contraintes tout au long de la résolution. La stratégie utilisée pour résoudre un problème d’optimisation est avant tout de trouver une solution satisfaisante. La valeur objectif de cette solution sert de référence pour la suite. Le raisonnement se poursuit pour déterminer si la solution est optimale. Si le raisonnement mène à une solution dont la valeur objectif est meilleure, cette dernière devient la nouvelle référence. Le raisonnement se poursuit tant qu’il peut exister de meilleures solutions. Prouver qu’une solution est optimale occupe une bonne proportion du temps de résolution. Par conséquent, pour éviter de chercher inutilement, la stratégie doit intégrer le calcul d’une borne sur la valeur objectif. Une méthode courante consiste à extraire cette borne d’une solu-tion optimale pour une relaxasolu-tion du problème original. Dans le contexte de l’ordonnancement

(11)

non préemptif, cela revient à déduire le retard minimal d’un ordonnancement préemptif sa-tisfaisant. Une contribution du mémoire est l’intégration à la programmation par contraintes de nouveaux raisonnements sur la relation entre l’ordre d’exécution de tâches et le retard en-gendré. La façon de procéder est de définir une nouvelle contrainte globale dont l’algorithme de filtrage applique ces raisonnements. Les travaux se concentrent sur le cas où les tâches ne peuvent pas se chevaucher dans le temps et où le retard impliqué par l’ordonnancement croit selon une fonction convexe.

Le mémoire est séparé en deux parties. La première partie présente les notions nécessaires à la compréhension de la contribution. D’abord, le premier chapitre donne une définition mathématique d’un problème de satisfaction de contraintes et d’un problème d’optimisation sous contraintes. Ces définitions sont reprises pour introduire les problèmes d’ordonnancement avec ressources unaires. Une section décrit comment la programmation par contraintes peut servir à accélérer la recherche de solutions pour ce type de problème. Les algorithmes de filtrage assurant la cohérence de contraintes sont intrinsèques à cette technique. Différents algorithmes de filtrage spécialisés pour les problèmes d’ordonnancement sont décrits. Cet état de l’art sert de base pour la suite. Ensuite, le second chapitre présente les réseaux de flot comme un outil efficace pour résoudre des problèmes d’optimisation fondamentaux. Deux applications des réseaux de flots à la programmation par contraintes sont illustrées pour appuyer leur pertinence. Une qui demande de résoudre le problème du flot maximum et l’autre, le problème du flot maximum à coût minimum.

La deuxième partie englobe la contribution issue des travaux de recherche. Le chapitre 3 couvre la nouvelle contrainte comblant un manque en programmation par contraintes. Un algorithme de filtrage basé sur un réseau de flot pondéré est donné. La contrainte encode un problème difficile à résoudre. L’algorithme doit alors résoudre une variante relaxée du problème. Des ajustements au réseau sont également nécessaires pour que l’algorithme s’exécute en temps polynomial. Finalement, le chapitre 4 évalue la performance de la solution proposée. La force du filtrage de l’algorithme et son temps d’exécution sont deux métriques à considérer. Une discussion accompagne les résultats et une réflexion sur des travaux futurs est exposée.

(12)

Première partie

(13)

Chapitre 1

La programmation par contraintes en

ordonnancement

Plusieurs techniques existent pour résoudre les problèmes d’optimisation combinatoire. Le mémoire se concentre sur une en particulier, soit la programmation par contraintes. Ce chapitre introduit les notions de base de cette technique. L’utilisation de contraintes génériques pour modéliser les problèmes rend la méthode expressive. Il y a notamment des applications dans le domaine de l’ordonnancement. Pour illustrer ce fait, des contraintes pertinentes encodant différents problèmes d’ordonnancement sont présentées.

1.1

Problème combinatoire

Un problème combinatoire est un problème mathématique dont la recherche de solutions s’effectue dans un espace de recherche discret. Un problème d’optimisation combinatoire est un problème combinatoire auquel s’ajoute une fonction objectif qu’on cherche à minimiser ou maximiser. Cette fonction quantifie la qualité d’une solution.

Un exemple de problème d’optimisation combinatoire est celui de l’ordonnancement de la pro-duction dans une usine. Dans le carnet de commandes, l’usine a plusieurs produits à livrer. Elle doit attendre l’arrivée des matières premières avant de démarrer la fabrication d’un pro-duit. Elle doit terminer la fabrication avant une échéance prescrite. En aucun cas on ne peut se permettre de surcharger la ligne de production. Il faut donc étaler la production dans le temps. La livraison d’un produit au-delà de son échéance entraîne une pénalité proportionnelle au retard. Dans ce contexte, un critère d’optimisation est de minimiser les pénalités de retard pour l’ensemble de la production.

(14)

1.1.1 Définitions

Les problèmes d’optimisation combinatoire traités dans ce mémoire sont avant tout des pro-blèmes de satisfaction de contraintes et des propro-blèmes d’optimisation sous contraintes.

Problème de satisfaction de contraintes

Un problème de satisfaction de contraintes (CSP) est composé de variables X1, . . . , Xn et

de contraintes C1, . . . , Cm. Chaque variable Xi est associée à un domaine de valeurs que l’on

dénote dom(Xi). Les valeurs des domaines peuvent être énumérées ou définies par un intervalle. Une variable doit éventuellement prendre une valeur de son domaine, ce qui consiste en son affectation. Une affectation regroupe des variables affectées avec leur valeur. Elle est partielle si elle concerne un sous-ensemble des variables et complète si elle concerne toutes les variables du problème.

Une contrainte définit une relation sur un sous-ensemble de variables que l’on appelle sa portée. L’arité d’une contrainte est la cardinalité de sa portée. Une contrainte binaire est une contrainte d’arité 2, une contrainte tertiaire d’arité 3 et ainsi de suite. Une contrainte est un ensemble de tuples de même dimension que son arité. Une affection des variables satisfait une contrainte si les valeurs affectées aux variables de la portée forment un tuple appartenant à la contrainte. Une contrainte peut être définie en extension, qui est une énumération explicite des tuples, ou en compréhension, c’est-à-dire une expression mathématique qui définit l’appartenance d’un tuple à une contrainte.

Exemple 1.1. Un exemple est la contrainte tertiaire Ou pour des variables booléennes.

Ou(A, B, C) = {(f, f, f ), (f, v, v), (v, f, v), (v, v, v)} hdéfinition en extensioni = {(A, B, C) | A ∨ B ⇔ C} hdéfinition en compréhensioni

Parmi les huit affectations complètes possibles pour trois variables booléennes, la contrainte Ou n’en admet que quatre.

La plupart du temps, un problème est modélisé avec plus d’une contrainte. Au final, il y a une solution au problème s’il existe une affectation complète satisfaisant toutes les contraintes.

Problème d’optimisation sous contraintes

Un problème d’optimisation sous contraintes est un CSP auquel on greffe une fonction objectif. La fonction dépend des variables du problème. De ce fait, il convient souvent d’introduire une nouvelle variable dont le domaine est l’image de la fonction. Par la suite, il suffit de préciser s’il faut minimiser ou maximiser cette variable.

(15)

1.1.2 Complexité de la résolution

Comme l’ordonnancement de la production dans une usine, de nombreux problèmes rencontrés en pratique sont classés comme Complets. Le CSP SAT, premier problème reconnu NP-Complet, en est un bon exemple. Ce problème de décision demande de confirmer la satisfiabilité d’une formule booléenne. Il n’y a pas, à ce jour, d’algorithme s’exécutant en temps polynomial connu pour le résoudre. Par conséquent, bien des instances de CSPs ne sont pas résolues en un temps raisonnable. En revanche, il existe plusieurs techniques accélérant la recherche de solutions. La technique préconisée dans ce mémoire est la programmation par contraintes.

1.2

Méthode de résolution

Une méthode efficace pour résoudre les problèmes d’optimisation combinatoire est la program-mation par contraintes. Dans ce domaine, la communauté contribue de plusieurs manières. Une partie travaille sur des modèles élégants pour représenter des problèmes de tous les jours alors que d’autres élaborent de nouveaux algorithmes qui servent d’outils aux modélisateurs. Une troisième partie développe des solveurs de contraintes qui se chargent d’interpréter les modèles et gèrent la recherche de solutions pour un problème ciblé.

1.2.1 Filtrage

En programmation par contraintes, des algorithmes de filtrage sont intégrés à chaque contrainte d’un CSP. Ces algorithmes interagissent avec les variables de la portée en retirant de leur do-maine les valeurs empêchant de satisfaire la contrainte. À ce moment, il devient pertinent pour un état des domaines de fournir un indicateur de la satisfiabilité d’une contrainte. Pour ce faire, il faut introduire différents degrés de cohérence pour une contrainte. Un algorithme de filtrage est notamment caractérisé par le degré de cohérence qu’il applique sur les domaines des variables touchées par sa contrainte. Pour démontrer une certaine cohérence, on cherche avant tout un support. Un support est une affectation des variables de la portée respectant les critères de son degré de cohérence correspondant. La cohérence de bornes et la cohérence de domaine sont deux indicateurs standards en programmation par contraintes.

Cohérence de domaine

La cohérence de domaine demande de s’assurer que toute valeur présente dans un domaine ne remet pas en question la satisfaction de la contrainte. Un support de domaine pour une contrainte C est un tuple de C dont chaque valeur vi appartient au domaine de sa variable correspondante : vi ∈ dom(Xi). C est cohérente de domaine si chacune des valeurs dans le

(16)

Cohérence de bornes

La cohérence de bornes ne requiert que de s’attarder à la valeur minimale et la valeur maximale qu’une variable peut prendre. Elle est bien adaptée pour les variables dont le domaine est représenté par un intervalle. Un support d’intervalle pour une contrainte C est un tuple de C dont chaque valeur vi ∈ [min(dom(Xi)), max(dom(Xi))]. C est cohérente de bornes si pour

chaque valeur min(dom(Xi)) et max(dom(Xi)), il existe un support d’intervalles pour C.

Exemple 1.2. Soit la contrainte arithmétique Double(X, Y ) = {(X, Y ) | Y = 2X} avec dom(X) = {1, 2} et dom(Y ) = {1, 2, 3, 4}. Un algorithme de filtrage appliquant la cohérence de bornes ne filtrera que la valeur 1 de dom(Y ). En revanche, un autre algorithme appliquant la cohérence de domaine filtrera les valeurs 1 et 3. Cet exemple illustre d’ailleurs qu’appliquer la cohérence de domaine assure également la cohérence de bornes.

Dépendamment du problème encodé par la contrainte, il est plus dur de concevoir un algo-rithme appliquant la cohérence de domaine. Un algoalgo-rithme appliquant la cohérence de domaine a souvent un temps d’exécution plus lent qu’un algorithme appliquant la cohérence de bornes. En revanche, appliquer la cohérence de domaine filtre davantage l’espace de recherche. Il y a donc un compromis à faire entre la quantité de filtrage souhaitée et le temps pour accomplir ce filtrage.

Propagation

Un changement de l’état des domaines engendré par un algorithme de filtrage remet en ques-tion le degré de cohérence du reste des contraintes. Pour revenir au même niveau de cohérence, il faut propager le changement. Cette propagation de contraintes consiste à appeler les algo-rithmes qui filtrent les domaines des variables touchées par le changement. L’opération est répétée jusqu’à ce que l’état des domaines demeure inchangé. Autrement dit, lorsque toutes les contraintes sont cohérentes.

1.2.2 Modélisation

La définition d’un CSP est reprise pour effectuer la modélisation. Le problème est modélisé en déterminant les variables, leur domaine et les contraintes qui lui donnent une représentation mathématique.

Choix des contraintes

Dépendamment de sa complexité, il existe souvent plus d’un modèle pour un même problème. Cependant, pour qu’un modèle soit le moindrement efficace, il doit contenir un nombre de contraintes borné par un polynôme du nombre de variables. Ce nombre dépend notamment du choix des contraintes.

(17)

Un choix judicieux de contraintes augmente les performances au moment de la résolution du modèle. Il arrive qu’un problème complexe puisse se décomposer en sous-problèmes. Dans ce cas, pour faciliter la modélisation, des contraintes globales sont favorisées. Ces contraintes, élaborées pour des sous-problèmes particuliers, unissent plusieurs contraintes primitives et se faisant encadrent mieux la résolution. Effectivement, en tenant compte de l’interaction entre plusieurs contraintes, il est possible d’intégrer de meilleurs algorithmes de filtrage à travers le processus de résolution. De par leur rôle, la plupart sont d’arité arbitraire. En revanche, tous les solveurs n’incluent pas l’ensemble des contraintes globales dans leur catalogue. Au final, le modélisateur doit en tenir compte pour que le modèle conçu soit traitable.

Exemple 1.3. Un sous-problème fondamental est celui exigeant que certaines variables en-tières prennent une valeur différente. C’est le cas lorsque l’on modélise un problème d’ordon-nancement où les tâches, dont les temps de début sont encodés par des variables, ne peuvent démarrer au même moment. Soit ce problème contraignant les variables X1, X2 et X3, une

modélisation triviale incorporerait les contraintes d’inégalité binaires X1 6= X2, X2 6= X3 et

X3 6= X1. Cependant, en programmation par contraintes, il se trouve qu’une contrainte

glo-bale dénommée AllDifferent est spécifiquement conçue pour ce problème. Cette contrainte n-aire encode le problème en agissant sur l’ensemble des variables impliquées. Un modèle alter-natif peut simplement incorporer la contrainte AllDifferent(X1, X2, X3). Dans un contexte

où dom(X1) = dom(X2) = {1, 2} et dom(X3) = {2, 3}, une remarque pertinente est que les

valeurs 1 et 2 peuvent exclusivement être assignées aux variables X1 et X2. Par déduction, la valeur 2 peut être filtrée de dom(X3). C’est le raisonnement global que AllDifferent fait.

Ce n’est pas le cas en se penchant individuellement sur la satisfiabilité de chaque contrainte d’inégalité. Ainsi, lorsque le problème est formulé à l’aide de contraintes globales, l’espace de recherche est réduit davantage et l’on économise en temps de calcul.

1.2.3 Recherche de solutions

La recherche de solutions est gérée par un solveur de contraintes. C’est un logiciel qui aide à décrire des problèmes combinatoires et les résout avec diverses techniques de programmation par contraintes. Il permet d’introduire plusieurs types de variables (entier, booléen, ensemble ou réel). Chaque solveur inclut son propre catalogue de contraintes applicables aux variables. Il est également possible de configurer la recherche, c’est-à-dire d’indiquer avec des heuristiques les régions de l’espace de recherche susceptibles de contenir des solutions.

L’espace de recherche est fondamentalement une arborescence. Chaque nœud est lié à une affectation partielle et chaque feuille à une affectation complète. Le solveur débute la recherche à la racine où l’affectation est vide. Le but est de trouver des affectations complètes qui satisfont toutes les contraintes du modèle. L’exploration d’un nœud enfant (ou branchement) consiste en l’affectation d’une variable supplémentaire. Cette affectation implique un changement de l’état des domaines et déclenche par conséquent une propagation de contraintes. Avec ce

(18)

mécanisme, le solveur s’assure d’explorer des régions de l’espace de recherche cohérentes avec les contraintes. De plus, les feuilles auxquelles il aboutit sont nécessairement des solutions au problème.

Il arrive que la propagation de contraintes vide complètement le domaine d’une variable. À ce moment, les domaines sont dans un état tel qu’il est impossible de satisfaire la contrainte responsable de l’événement. Cette impasse est causée par la dernière variable affectée. Le retour à un état satisfaisant se fait en remontant l’arborescence et en choisissant une autre affectation. Cette technique s’agit d’un retour sur trace. Le solveur en profite également pour filtrer la valeur responsable de l’impasse. À la fin de la recherche, si le solveur n’a pas exploré de feuille, alors il n’y a pas de solution au problème.

1.3

Ordonnancement

Depuis les dernières années, le domaine de l’ordonnancement gagne en popularité chez la communauté en programmation par contraintes. De nombreux solveurs incluent maintenant dans leur catalogue des contraintes globales spécifiquement conçues pour la résolution de problèmes d’ordonnancement.

1.3.1 Définitions

Un problème d’ordonnancement demande de régir l’ordre d’exécution de n tâches. Il convient de référer aux tâches avec un ensemble ordonné I = {1, . . . , n}. Le problème présenté à la section 1.1 est un exemple classique de problème d’ordonnancement. Dans le contexte du mémoire, les tâches s’exécutent toutes sur la même ressource. Par conséquent, elles ne peuvent pas se chevaucher dans le temps. Les instances de ces problèmes sont dites disjonctives. Aussi, il n’est souvent pas permis d’arrêter l’exécution d’une tâche une fois qu’elle est débutée. Il est alors question d’ordonnancement non préemptif. Sans perte de généralité, une tâche renferme les caractéristiques suivantes :

— S ; la variable indiquant à quel instant la tâche débute (starting time) — p ; son temps de traitement (processing time)

— est = min(dom(S)) ; le plus petit instant auquel la tâche peut débuter (earliest starting time)

— d ; l’échéance préférable à laquelle la tâche devrait être terminée (due time)

— lct = max(dom(S)) + p ; l’instant limite auquel la tâche doit être terminée (latest com-pletion time)

Une solution au problème consiste à trouver une valeur satisfaisante pour chaque temps de début Si. Les tâches ne peuvent pas débuter à n’importe quel moment. Soit I l’ensemble de toutes les tâches d’un problème, un ordonnancement séquentiel proposé doit respecter les

(19)

fenêtres d’exécution. Autrement dit, pour chaque tâche i, Si∈ [esti, lcti− pi]. Il y a aussi les

caractéristiques relatives à un ensemble de tâches. Soit un ensemble de tâches Ω ⊆ I, alors : — est = mini∈Ωesti

— lctΩ = maxi∈Ωlcti

— h = lctI − estI; la taille de la fenêtre d’exécution globale ou l’horizon

— p=P

i∈Ωpi

En optimisation, chaque tâche se terminant après son échéance d implique un retard. Dans ce contexte, obtenir un ordonnancement valide n’est pas suffisant. Généralement, il est préférable qu’il n’y ait aucun retard. Cependant, ce n’est pas toujours possible. Par conséquent, pour indiquer une préférence sur la solution, le problème vient avec une fonction objectif qu’il faut optimiser. Pour éviter les retards, ces fonctions sont conçues pour exiger que les tâches se terminent le plus tôt possible. De plus, elles sont souvent convexes. Effectivement, les fonctions convexes ont des propriétés qui peuvent être exploitées et suffisent pour exprimer la plupart des problèmes concrets [25]. En ordonnancement, minimiser le makespan ou le retard pondéré sont des fonctions objectifs notamment pertinentes.

Minimisation du makespan

Pour un ensemble I de tâches à ordonnancer, minimiser le makespan, noté Cmax, revient

simplement a retenir les solutions valides dont l’ensemble des tâches se termine le plus tôt possible. Soit Ci = Si + pi le temps de complétion d’une tâche i, Cmax = maxi∈ICi est

l’instant auquel toutes les tâches de l’ensemble sont terminées. Il faut donc minimiser Cmax.

Il s’agit d’une stratégie globale visant à diminuer l’impact négatif des retards. Il n’est pas nécessaire de définir une échéance particulière pour chaque tâche. L’idée est simplement de vouloir terminer au plus vite. L’avantage du makespan est qu’il s’agit d’une métrique simple qui est facile à modéliser et parfois à optimiser. Cependant, cette métrique a pour désavantage de ne pas exécuter au plus tôt les tâches du problème, si l’une d’entre elles est condamnée à s’exécuter tardivement. Ainsi, seule la tâche la plus tardive est exécutée au plus tôt.

Minimisation du retard pondéré

Dans un contexte où l’exécution d’une tâche engendre un coût proportionnel à son retard, une préférence serait de vouloir minimiser le retard pondéré. Le retard d’une tâche i, noté Ti, est

la quantité de temps entre son échéance et son temps de complétion : Ti = max(0, Ci− di).

Pour pondérer le retard, il faut introduire un poids wi pour chaque tâche. Le coût associé à l’ordonnancement d’une tâche i est donc wi· Ti. Le retard pondéré d’un ensemble de tâches

est défini par Tw,I =P

i∈I(wi· Ti). Ainsi, minimiser le retard pondéré demande de ne retenir

(20)

1.3.2 Contraintes

Les contraintes suivantes sont utiles pour modéliser les problèmes d’ordonnancement. La liste est non exhaustive. Elle traite notamment des contraintes pertinentes dans le contexte du mémoire.

AllDifferent

Cette contrainte assure que chaque variable de sa portée prenne une valeur différente. Elle a été introduite par Régin [26].

AllDifferent([X1, . . . , Xn]) = {(x1, . . . , xn) | xi 6= xj∀ i 6= j} (1.1)

Elle est fondamentale dans le domaine. Elle apparaît naturellement dans les problèmes impli-quant des permutations selon van Hoeve [14]. Il existe un algorithme en temps linéaire pour la cohérence de bornes. Cependant, López-Ortiz et collab. [19] précisent que leur version en O(n log n) est plus performante en pratique. De plus, Régin [26] donne un algorithme pour la cohérence de domaine en temps O(n2d2) tel que d la taille du plus grand domaine.

Par déduction, AllDifferent s’avère utile pour les problèmes d’ordonnancement. Particu-lièrement, si toutes les tâches ont un temps de traitement unitaire, il suffit d’encoder le temps auquel s’exécute une tâche par une variable Xi de la portée de AllDifferent. De cette manière, un support pour la contrainte représente directement un ordonnancement valide.

Global Cardinality Constraint (GCC)

Il s’agit d’une autre contrainte plus générale. Dans ce cas-ci, le nombre de fois qu’une valeur doit être assignée à une variable est prescrit par un intervalle [l, u]. Cette borne est spécifiée pour des valeurs arbitraires. Dans la définition suivante, #(v, τ ) représente le nombre de fois que la valeur v se retrouve dans un tuple τ .

GCC([X1, . . . , Xn], [v1, . . . , vd], [l1, . . . , ld], [u1, . . . , ud])

= {(x1, . . . , xn) | 1 ≤ i ≤ d, ∀ vi : li≤ #(vi, (x1, . . . , xn)) ≤ ui} (1.2)

Dans un contexte d’ordonnancement, avec l’intervalle [l, u], il y a là une notion de ressource disponible à chaque instant pertinent. GCC généralise la contrainte AllDifferent si l’in-tervalle est [0, 1] pour toutes les valeurs. D’ailleurs, Régin [23] l’introduit quelque temps après AllDifferent. Il fournit un algorithme qui applique la cohérence de domaine en temps O(n2d). Un peu plus tard, Quimper et collab. [22] donnent un algorithme plus efficace pour la même cohérence en temps O(n1.5d) et un algorithme en temps linéaire pour la cohérence de bornes [21].

(21)

CostGCC

CostGCC est une extension de GCC une fois de plus amenée par Régin [24]. Dans ce contexte, chaque affectation implique un coût. Le coût est donné par la fonction cost. cost(X, x) indique le coût engendré par l’affectation d’une valeur x à la variable X. Le coût peut être un entier arbitraire. Pour que la contrainte soit satisfaite, la GCC qu’elle encode doit être satisfaite. De plus, il ne faut pas que la somme des coûts dépasse un certain seuil variable H. Ci-bas, v, l et u sont des vecteurs de dimension d.

CostGCC([X1, . . . , Xn], v, l, u, cost, H) = GCC([X1, . . . , Xn], v, l, u) ∩ {(x1, . . . , xn) | n X i=1 cost(Xi, xi) ≤ H} (1.3)

Pour appliquer la cohérence de domaine, l’algorithme de Régin [24] utilise un algorithme de plus court chemin qui dépend de la taille d’un graphe construit à partir de l’instance. Soit W une borne supérieure pour la fonction cost et P (n, d, W ) le temps requis pour appliquer l’algorithme de plus court chemin, une variante notable de l’algorithme de Régin [24] s’exécute en temps O(nP (n, d, W )).

Disjunctive

Cette contrainte est spécialisée pour assurer que des tâches de temps de traitement arbitraires ne s’exécutent pas en même temps. De plus, il est exigé que la solution soit non préemptive. Pour n tâches, il est désiré que les temps auxquels débutent les tâches soient représentés par les variables S1, . . . , Sn. Pour cette version de la contrainte, les temps de traitement p1, . . . , pn

sont constants.

Disjunctive([S1, . . . , Sn], [p1, . . . , pn])

= {(s1, . . . , sn) | si+ pi ≤ sj∨ sj+ pj ≤ si∀ i 6= j} (1.4)

Carlier [4] est le premier à en faire mention dans la littérature. Jusqu’à maintenant, il s’agit de la contrainte la plus notoire pour modéliser une grande partie, si ce n’est pas la totalité, des problèmes d’ordonnancement considérés dans ce mémoire. Elle généralise d’ailleurs la contrainte AllDifferent si tous les pi sont unitaires.

Complexité pour appliquer la cohérence de bornes

L’expressivité de la contrainte permet d’encoder un problème d’ordonnancement à elle seule. Cependant, une faiblesse réside dans la faisabilité d’appliquer un certain degré de cohérence, et ce, en un temps raisonnable. Effectivement, le problème de déterminer s’il existe un or-donnancement qui satisfait Disjunctive est NP-Complet. Il s’agit du problème SS1 traité par Garey et Johnson [10]. Or, trouver un support d’intervalle pour appliquer la cohérence de

(22)

bornes demande de résoudre le problème de recherche correspondant, ce qui est NP-Difficile. Donc, pour décrire le filtrage envisageable, la communauté s’est résorbée à définir des règles de filtrage relaxées plus spécifiques à l’ordonnancement.

Règle du time-tabling Cette règle nécessite de raisonner sur la partie fixe des tâches. Le premier algorithme de filtrage est donné par Le Pape [18]. Pour une tâche i, si lcti− esti <

2pi, soit lorsque dom(Si) est suffisamment petit, il est possible d’affirmer que la tâche doive

s’exécuter dans l’intervalle [lcti− pi, esti+ pi). Cette affirmation implique un filtrage sur la

fenêtre d’exécution d’une autre tâche j qui s’exécuterait à travers cet intervalle. lcti− esti< 2pi∧ lcti− estj < pi+ pj ⇒ est0j = max(estj, esti+ pi)

hdéfinition du time-tablingi (1.5) Ci-haut, est0j est la nouvelle borne déduite. La propriété de symétrie du problème permet également de définir une version de la règle filtrant le lct . L’algorithme le plus rapide applique la règle en temps linéaire [8].

Règle du edge-finding Cette règle demande de faire des déductions sur l’ordre d’exécution des tâches. Carlier et Pinson [5] sont les premiers à fournir un algorithme de filtrage. Soit Ω un sous-ensemble des tâches et une tâche i /∈ Ω, s’il n’est pas possible d’exécuter la tâche i dans la fenêtre d’exécution [est, lctΩ] sans causer de débordement, alors la tâche i doit commencer

après l’ensemble des tâches dans Ω.

estΩ∪{i}+ pΩ∪{i}> lctΩ⇒ est0i= max(esti, max

Θ⊆Ω(estΘ+ pΘ))

hdéfinition du edge-findingi (1.6) Pour savoir s’il y a débordement, il faut déterminer le temps de complétion minimal d’un ensemble de tâches. maxΘ⊆Ω(estΘ+ pΘ) est une borne adéquate pour ce temps de complétion.

Comme le time-tabling, il existe une version de la règle pour filtrer le lct. Pour n tâches, Vilím [28] donne un algorithme considérant tous les sous-ensembles Θ pertinents en temps O(n log n).

Règle de l’overload checking Cette règle valide la satisfiabilité de la contrainte Dis-junctive. Elle est intrinsèque au filtrage amené par la règle du edge-finding . Soit Ω un sous-ensemble des tâches, si le temps de traitement des tâches est plus grand que la taille de la fenêtre d’exécution admissible, alors les tâches débordent et il n’y a pas de solution selon l’état des domaines.

estΩ+ pΩ > lctΩ ⇒ incohérence hdéfinition de l’overload checkingi (1.7)

Comme pour la règle du time-tabling, Fahimi et Quimper [8] donne un algorithme en temps linéaire. Puisque la règle du edge-finding s’exécute à peine plus lentement que l’overload che-cking (O(n log n) plutôt que O(n)) et qu’il domine l’overload cheche-cking, en pratique, on favorise généralement l’edge-finding .

(23)

StockingCost

Houndji et collab. [16] ont spécifiquement conçu la contrainte StockingCost pour modéliser

des problèmes de planification de production en usine. Concrètement, StockingCost encode n commandes à produire sur une machine. Toutes les commandes ont un temps de traitement unitaire. La machine ne peut pas produire plus de c commandes à la fois. Contrairement à la contrainte Disjunctive, il est permis d’exécuter des tâches en parallèle tant que la ressource le permet. Chaque commande i doit être traitée avant une date de livraison di. Si une commande

est traitée à un instant xi ≤ di, il y a un coût d’entreposage di− xi qui lui est associé. Comme

CostGCC, il ne faut pas que la somme des coûts dépasse un certain seuil variable H.

StockingCost([X1, . . . , Xn], [d1, . . . , dn], H, c) = {(x1, . . . , xn) | ∀ xi : #(xi, (x1, . . . , xn)) ≤ c, xi ≤ di, n X j=1 (dj− xj) ≤ H} (1.8)

CostGCC est une généralisation de StockingCost si l’intervalle est [0, c] pour l’ensemble des valeurs et que la fonction cost encode le coût d’entreposage. Dans ce cas, cost(Xi, xi) =

cost(di, xi) = di− xi. Aussi, avec une ressource unaire (c = 1), StockingCost peut être

vue comme une extension de AllDifferent à laquelle on ajoute la notion de coût lié à une affectation. Elle est donc une meilleure alternative aux modèles qui comportent une AllDif-ferent et une contrainte d’inégalité agissant sur les mêmes variables. L’algorithme de filtrage de Houndji et collab. [16] applique la cohérence de bornes en temps linéaire.

Cette contrainte encode le problème où l’on cherche à minimiser les coûts d’entreposage des commandes produites avant leur livraison. Cet objectif est symétrique à celui de minimiser le retard pondéré dont la pondération est neutre pour toutes les commandes. StockingCost se prête donc bien aux problèmes d’ordonnancement lorsque les temps de traitement sont unitaires.

(24)

Chapitre 2

Optimisation

Une tâche importante d’un algorithme de filtrage appliquant une cohérence spécifique est de trouver un support pour la contrainte visée. Même si la contrainte n’en a pas l’aspect en soi, la recherche du support se réduit parfois à un problème d’optimisation. Plusieurs problèmes de satisfaction et d’optimisation associés à une contrainte globale peuvent se modéliser à l’aide d’une structure abstraite appelée réseau de flot. Ce chapitre est spécifiquement dédié à cette structure. Les fondements y sont présentés ainsi que les applications sur les contraintes globales vues jusqu’à maintenant.

2.1

Réseaux de flot

Les réseaux de flot sont des graphes spécialisés. Ils sont couramment utilisés pour représenter divers problèmes d’optimisation. Dans le cas de certaines contraintes en ordonnancement, la recherche d’un support passe par la résolution du problème du flot maximum dans un réseau de flot. De plus, lorsque la contrainte encode un coût lié à une solution, il faut résoudre le problème plus général, soit le problème du flot maximum à coût minimum.

2.1.1 Définition

Les réseaux de flot sont des graphes orientés (N , A) dont chaque arête (u, v) ∈ A admet la circulation d’un flot f quantifié. La quantité nette de flot circulant entre deux nœuds u et v est désignée par f (u, v). Si f (v, u) = 0 ∀ u, v ∈ N , alors le flot est nul. Pour chaque arête (u, v) est défini un poids c(u, v) représentant sa capacité. La capacité indique la quantité maximale de flot qui peut circuler sur l’arête. La capacité est nécessairement positive. L’absence d’une arête dans le graphe implique que la capacité est nulle pour cette paire de nœuds disjoints. Aussi, deux nœuds sont désignés pour jouer un rôle particulier. Ils sont nommés la source et le puits. La source s du réseau ne peut qu’émaner du flot. Elle n’accepte pas de flot entrant. Le puits a quant à lui un rôle symétrique. Il n’accepte pas de flot sortant.

(25)

2.1.2 Validité du flot

Pour traiter des problèmes avec les réseaux de flots, il convient de s’intéresser à la notion de flot valide. Un flot valide respecte certaines contraintes. Ces contraintes permettent de faire le rapprochement entre le réseau de flot et le problème concret qu’il traite. D’abord, la quantité de flot sur une arête ne peut dépasser la capacité : f (u, v) ≤ c(u, v). Ensuite, une autre restriction est d’imposer une quantité nette au flot circulant entre les nœuds. Par conséquent, pour une quantité de flot entre deux nœuds, il y a une quantité égale à l’opposé de flot qui circule en direction inverse. Autrement dit, f (u, v) = −f (v, u). Par conséquent, il est à noter que si (u, v) ∈ A et (v, u) /∈ A, alors f (v, u) ≤ c(v, u) = 0 et f (u, v) ≥ 0. Finalement, il y a conservation du flot d’un nœud à l’autre. Cependant, pour admettre d’autres flots que le flot nul, cette contrainte ne s’applique pas à la source et au puits du réseau : P

u∈Nf (u, v) =

P

w∈Nf (v, w) ∀ v ∈ N \ {s, t}.

Dans un flot valide f circule globalement une valeur de flot précise que l’on dénote |f |. Dans ce cas, puisque l’ensemble du flot provient de la source et culmine vers le puits, la valeur |f | =P

u∈Nf (s, u) =

P

u∈Nf (u, t).

2.1.3 Graphe résiduel

Pour un flot f dans un réseau (N , A), il existe un graphe résiduel (N , Af). Une arête ap-partient au graphe résiduel (u, v) ∈ Af si et seulement si elle n’est pas saturée, soit lorsque

f (u, v) < c(u, v). Soit cf(u, v) = c(u, v) − f (u, v) la capacité résiduelle, on a donc la relation

(u, v) ∈ Af ⇔ cf(u, v) > 0. Une notion induite par la définition de la capacité résiduelle

est la récupération de flot : si (u, v) ∈ A, (v, u) /∈ A et f (u, v) > 0, alors (v, u) ∈ Af, car

cf(v, u) = c(v, u) − f (v, u) = 0 − (−f (u, v)) = f (u, v) > 0. Donc, même si l’arête (v, u)

n’appartient pas au graphe (N , A), elle peut se retrouver dans le graphe résiduel.

Le graphe résiduel est utile pour modifier un flot existant sans compromettre sa validité. Cette opération se fait en choisissant un chemin composé d’un ensemble d’arêtes du graphe résiduel dans lequel un flot additionnel peut être envoyé. Cette définition du chemin est suffisante dans la mesure où l’on ne traite pas de chemins passant plus d’une fois par une même arête. À ce moment, pour chaque arête (u, v) de ce chemin C, la valeur de f (u, v) est augmentée de k unités pendant que celle de f (v, u) est diminuée de la même quantité telle que k ≤ min(u,v)∈Ccf(u, v).

Pour conserver un flot valide, deux types de modifications sont admissibles. La première est l’ajout d’un flot à travers un cycle dans le graphe résiduel, c’est-à-dire un chemin qui débute et aboutit sur le même nœud. La valeur du flot |f | est alors inchangée. La deuxième modification consiste à envoyer un flot sur un chemin reliant la source et le puits. Cette modification engendre un changement de la valeur du flot en fonction du sens du chemin. Un chemin est dénommé chemin augmentant s’il débute à la source et culmine au puits. L’envoi de k unités de flot sur un chemin augmentant fait passer la valeur du flot à |f0| = |f | + k.

(26)

s u v w t 1/2 3/3 2/3 1/2 3/3

Figure 2.1 – Flot valide initial dans un graphe

s u v w t 1 1 3 2 1 1 1 3

Figure 2.2 – Graphe résiduel selon le flot initial

s u v w t 2 3 1 2 2 3

Figure 2.3 – Ajout d’un flot sur un chemin augmentant

Exemple 2.1. La figure 2.1montre un graphe avec un flot initial f dont |f | = 1. La fraction sur une arête (u, v) désigne le flot circulant par rapport à la capacité : f (u,v)/c(u,v). La figure2.2

montre le graphe résiduel correspondant. Le poids sur une arête désigne la capacité résiduelle cf(u, v). Ce graphe illustre mieux les modifications possibles. Effectivement, le graphe résiduel

met en évidence les arêtes dont la capacité n’est pas saturée. Une modification envisageable est de pousser un maximum de 1 unité de flot sur le chemin (s → u → v → t). La figure 2.3

montre le graphe résiduel une fois la modification faite. Puisque la modification est appliquée sur un chemin augmentant, la valeur du flot est maintenant |f0| = |f | + 1 = 2.

2.1.4 Problème du flot maximum

Le problème du flot maximum consiste, pour un réseau de flot donné, à construire un flot valide dont la valeur est maximale, noté fmax. Une propriété intéressante du flot maximum est qu’il

(27)

n’existe pas de chemin augmentant dans son graphe résiduel correspondant (Théorème 6.4 dans Ahuja et collab. [1]). Plusieurs techniques différentes pour résoudre ce problème sont présentes dans la littérature. Un algorithme simple basé sur la recherche de chemins augmentant dans le graphe résiduel est l’algorithme de Ford et Fulkerson [9]. L’algorithme débute avec le flot nul. De façon itérative, l’algorithme trouve un chemin augmentant en performant une fouille dans le graphe résiduel. Tant qu’un tel chemin C existe, l’algorithme ajoute min(u,v)∈Ccf(u, v)

unités de flot sur ce chemin. Comme les modifications ne s’appliquent que sur des chemins augmentant, le flot augmente à chaque itération. L’algorithme s’arrête lorsqu’il n’existe plus de chemin augmentant. Sans stratégie de fouille particulière, l’algorithme s’exécute en temps O(|A| · |fmax|). D’autres algorithmes, basés sur la même idée, se démarquent en choisissant

judicieusement le chemin augmentant pour maximiser l’augmentation de la valeur du flot. C’est le cas de l’algorithme ShortestAugmentingPath de Ahuja et collab. [2] qui s’exécute en temps O(|A||N |2).

2.1.5 Problème du flot maximum à coût minimum

Parfois, un réseau de flot n’est pas suffisant pour encoder le problème. C’est le cas lorsqu’une solution au problème engendre un coût et qu’on ne veut pas que ce coût dépasse un certain seuil. Pour modéliser les coûts, une avenue est d’ajouter pour chaque arête (u, v) un poids w(u, v) et de définir un coût cost(u, v) = w(u, v) · f (u, v) engendré par la circulation d’une quantité de flot. Aussi, pour demeurer cohérent avec la notion de flot net, w(u, v) = −w(v, u). Le poids w est dénommé le coût résiduel. Le coût d’un flot cost(f ) correspond à la somme des coûts de chaque arête. Le problème du flot maximum à coût minimum consiste à construire un flot valide dont la valeur est maximale et dont le coût engendré par le flot est minimal. C’est le problème que Régin [24] doit résoudre pour trouver un support de domaine à la contrainte CostGCC.

De nombreuses stratégies existent pour résoudre le problème. Klein [17] en fournit une per-tinente dénommée CycleCancelingAlgorithm qui procède en deux étapes. L’algorithme résout d’abord le problème du flot maximum sans tenir compte des coûts. Il procède ensuite à la modification du flot en un flot au coût optimal pour vérifier que la solution ne dépasse pas le seuil prescrit. Pour ce faire, il procède à une fouille pour trouver des cycles ayant un coût négatif et pousse une quantité maximale de flot sur ces cycles. La modification engendre l’addition d’un coût négatif au coût total. Pour chaque cycle C détecté, le coût diminue de min(u,v)∈Ccf(u, v) · P(u,v)∈Cw(u, v). Lorsqu’il n’y a plus de cycle de coût négatif,

l’algo-rithme s’arrête et le flot est optimal. L’algol’algo-rithme s’exécute en temps O(|A|cu) tel que c et u sont respectivement le coût maximal et la capacité maximale d’une arête dans le réseau. Un avantage particulier de cette approche est que pour trouver un support à la contrainte CostGCC, l’algorithme échoue rapidement s’il n’y pas d’affectation respectant la contrainte GCC correspondante.

(28)

2.2

Applications pour la contrainte AllDifferent

Une application pertinente des réseaux de flot est la recherche d’un support de domaine pour la contrainte AllDifferent. Cette recherche peut se réduire au problème du flot maximum. L’idée est que le réseau encode l’état des domaines et qu’un flot maximum pour ce réseau encode un support pour la contrainte. Il s’agit d’une partie du raisonnement de Régin [23] qui donne un algorithme appliquant la cohérence de domaine pour AllDifferent.

2.2.1 Réduction au problème du flot maximum

La première étape de réduction consiste à représenter l’état des domaines dans un graphe biparti. Un graphe est biparti s’il est possible d’obtenir deux partitions de ses sommets telles qu’aucune arête ne relie deux nœuds d’une même partition. Une partition regroupe les variables X = {X1, . . . , Xn} et l’autre les valeurs possibles V =Sni=1dom(Xi). Dans ce graphe, une arête

sert à indiquer qu’une valeur est dans le domaine d’une variable : (Xi, v) ∈ A ∀ v ∈ dom(Xi).

Ensuite, le graphe est transformé en réseau de flot. Il faut ajouter une source s reliée aux variables par les arêtes (s, Xi) et un puits t relié aux valeurs par des arêtes (v, t). La direction

des arêtes va de la source vers le puits. Finalement, dans le réseau, il est désiré qu’un flot passant par une variable indique qu’elle a une seule affectation. Aussi, un flot passant par une valeur indique qu’elle est affectée à une seule variable. Pour satisfaire ces critères, il suffit de fixer la capacité des arêtes à 1.

X2 X1 X3 1 2 3

Figure 2.4 – État des domaines dans l’exemple1.3

Exemple 2.2. Le graphe biparti de la figure 2.4représente l’état des domaines dans l’exemple

1.3. La partition de gauche regroupe les variables de la portée et celle de droite les valeurs possibles.

(29)

s X2 X1 X3 1 2 3 t 1 1 1 1 1 1 1 1 1 1 1 1

Figure 2.5 – Réseau de flot encodant l’état des domaines

La figure 2.5 montre le réseau de flot construit à partir du graphe précédent. Le poids sur les arêtes indique leur capacité.

L’application de l’algorithme de Ford et Fulkerson [9] dans le réseau résultant construit un flot maximum illustrant un support de domaine pour AllDifferent. Un flot maximal dont la valeur est inférieure à l’arité de la contrainte implique qu’il n’existe pas de support de domaine pour la contrainte. Cette illustration est également pratique pour déduire tous les autres supports de domaine pour la contrainte. Pour obtenir un support de domaine à partir d’un autre, il suffit de pousser un flot non nul à travers un cycle dans le graphe résiduel. Puisque la modification touche un cycle, le flot est toujours maximum et le graphe résiduel illustre un nouveau support distinct.

2.2.2 Filtrage à l’aide du support

Dans certains cas, les raisonnements découlant de l’analyse d’un support pour une contrainte induisent un filtrage appliquant la cohérence correspondante. Pour les problèmes modélisés par des graphes, la détermination des composantes fortement connexes est un outil d’analyse utile. Un graphe dirigé est dit fortement connexe si pour chaque paire ordonnée de ses nœuds, il existe un chemin reliant ces nœuds. Une composante fortement connexe dans un graphe est un sous-graphe fortement connexe tel qu’il n’est pas possible de lui ajouter un nœud sans le démunir de sa propriété de forte connexité. L’algorithme de Tarjan [27] est reconnu pour son efficacité à déterminer les composantes fortement connexes dans un graphe dirigé. Il s’exécute en temps O(|N | + |A|).

Dans son raisonnement, Régin [26] démontre que les composantes fortement connexes per-mettent de déterminer si une affectation possible est cohérente de domaine. Il faut que, dans

(30)

le graphe résiduel, toutes les valeurs n’appartenant pas à la même composante fortement connexe d’une variable soient retirées du domaine de cette dernière. Ce critère doit être validé pour toutes les affectations possibles qui ne sont pas présentes dans le support trouvé. Suite à ce filtrage, la contrainte AllDifferent est cohérente de domaine.

s X2 X1 X3 1 2 3 t −1/0 −1/0 −1/0 −1/0 0/1 0/1 −1/0 0/1 −1/0 −1/0 −1/0 −1/0

Figure 2.6 – Graphe résiduel après l’application de Ford et Fulkerson [9]

Exemple 2.3. En appliquant l’algorithme de Ford et Fulkerson [9] sur le graphe de la figure

2.5, on obtient le graphe résiduel à la figure2.6. La fraction sur une arête (u, v) désigne le flot circulant par rapport à la capacité : f (u,v)/c(u,v). Le graphe illustre le support de domaine X1 =

1, X2 = 2 et X3= 3. Le partitionnement des nœuds selon leur appartenance aux composantes

fortement connexes est {s}, {X1, X2, 1, 2}, {X3}, {3} et {t}. Pour atteindre la cohérence de

domaine, il faut retirer la valeur 2 du domaine de X3 puisque cet élément ne se retrouve pas

dans la même composante fortement connexe. La valeur 3 demeure dans le domaine même si elle ne se retrouve pas dans la même composante fortement connexe puisqu’elle est utilisée par le support. Après l’exécution de l’algorithme de Régin [26], dom(X1) = dom(X2) = {1, 2} et

dom(X3) = {3}. Dans une solution, X3 doit nécessairement être affectée à la valeur 3. Cette

analyse plus poussée est l’avantage concret de AllDifferent par rapport aux contraintes individuelles mentionnées dans l’exemple 1.3.

L’idée derrière l’algorithme de Régin [26] est que si une variable Xi et une valeur v appar-tiennent à la même composante fortement connexe, alors il existe un cycle passant par Xi et v. Donc, en poussant une unité de flot sur le cycle, on obtient nécessairement un autre support de domaine pour la contrainte AllDifferent dont la valeur vi assignée à Xi est égale à v.

(31)

2.3

Applications pour la contrainte CostGCC

Dans la même optique, un support de domaine pour la contrainte CostGCC peut être construit en réduisant le problème à celui du flot maximum à coût minimum. Pour ce faire, Régin [24] étend le raisonnement appliqué pour la contrainte AllDifferent. Pour ne

s’at-tarder qu’aux applications en ordonnancement, cette section se limite au cas particulier où le nombre minimal de fois lv qu’une valeur v peut être assignée est 0, même si l’algorithme de

Régin supporte le cas où lv est positive.

2.3.1 Réduction au problème du flot maximum à coût minimum

La réduction est basée sur celle décrite à la section2.2.1 pour la recherche d’un support pour la contrainte AllDifferent. Une différence notable est que la source s est plutôt reliée aux valeurs V, les variables X au puits t et une arête relie une valeur v à une variable X si v ∈ dom(X). Cette variante transposée est utile pour appliquer le filtrage défini par Régin [24]. Une autre différence est la capacité des arêtes reliant s aux valeurs. En lien avec la définition de CostGCC, pour chaque valeur vdcontrainte à être assignée un nombre maximal de fois ud,

la capacité sur l’arête correspondante est c(s, vd) = ud. Ainsi, une nouvelle notion introduite

est que la quantité de flot passant par une valeur indique le nombre de fois que celle-ci est assignée à une variable. Finalement, pour gérer les coûts définis par la fonction cost, le poids d’une arête reliant une valeur v à une variable X est w(v, X) = cost(X, v). Le poids est nul pour le reste des arêtes du réseau.

Une fois le réseau construit, l’application de l’algorithme de Klein [17] annulant les cycles négatifs détermine un flot optimal. Pour que CostGCC soit satisfaite, il faut qu’un flot non nul passe par toutes les variables de la portée. De plus, le coût du flot cost(f ) doit être inférieur ou égal au seuil H prescrit, c’est-à-dire cost(f ) ≤ max (dom(H)). À ce moment, le flot illustre un support de domaine et il est possible de filtrer en conséquence.

2.3.2 Filtrage à l’aide du support

L’application de la cohérence de domaine pour la contrainte CostGCC peut être vue en deux étapes [24]. La première étape applique la cohérence de domaine pour la contrainte GCC

correspondante. Le filtrage basé sur les composantes fortement connexes présenté pour la contrainte AllDifferent est repris ici. Cette étape est requise pour s’assurer, pour chaque affectation possible non utilisée par le support, qu’il existe un cycle passant par l’affectation. La seconde étape tient compte des coûts et du seuil prescrits. Le coût du flot optimal sert de borne inférieure pour le seuil H. Comme mentionné pour la contrainte AllDifferent, une arête (v, X) symbolise une affectation X = v à valider. De plus, pousser une unité de flot sur un cycle passant par l’arête (v, X) donne un support pour l’affectation X = v. Bien que ce test soit suffisant pour AllDifferent et GCC, ce n’est pas assuré pour CostGCC.

(32)

Effectivement, pousser une unité de flot sur un cycle engendre un coût rc qui vient s’ajouter pour donner le coût du nouveau du flot cost(f0) = cost(f )+rc. Or, pour que le flot f0 illustre un support incluant l’affectation à valider, il faut une fois de plus que cost(f0) ≤ max(dom(H)). Pour démontrer l’existence de ce flot f0, Régin [24] prescrit, à partir du flot optimal, qu’il suffit de pousser un flot sur un cycle engendrant le plus petit coût, soit le coût réduit. En effet, puisque f est optimal, il n’existe pas d’autre support avec un coût inférieur. Par conséquent, rc ≥ 0 et si cost(f0) > max(dom(H)), il est certain qu’il n’existe pas de support incluant l’affectation testée X = v. C’est donc de là que vient la motivation de construire un flot optimal.

Les cycles engendrant des coûts réduits, soit les cycles optimaux, peuvent être déterminés en utilisant un algorithme de plus court chemin selon les poids wi définis dans le graphe résiduel. L’utilisation d’un algorithme de plus court chemin est déterminante dans la complexité de l’algorithme de filtrage. Pour cette raison, il est important de minimiser son nombre d’appels. Plusieurs algorithmes de plus court chemin calculent les plus courts chemins d’un nœud de départ vers tous les autres nœuds du graphe. De ce fait, l’idée est de calculer, pour chaque arête (v, X) dans le graphe résiduel, le cycle optimal passant par cette arête. Soit les variables X et les valeurs V, l’ensemble des cycles optimaux passant par les affectations à valider peut être calculé de deux façons :

1. Faire |X | appels à l’algorithme de plus court chemin avec chaque variable comme nœud de départ. Pour un cycle passant par (v, X), le coût réduit engendré est la somme des coûts résiduels d’un plus court chemin C = (X → . . . → v) ajouté au coût résiduel de (v, X). Autrement dit, rc =P

(a,b)∈Cw(a, b) + w(v, X).

2. Dans le graphe transposé, faire |V| appels à l’algorithme de plus court chemin avec chaque valeur comme nœud de départ. Le coût réduit engendré par un cycle optimal passant par (X, v) est alors rc =P

(a,b)∈Cw(a, b) + w(X, v) tel que C = (v → . . . → X)

est un plus court chemin dans le graphe transposé.

Les deux techniques ci-dessus permettent de calculer le coût du cycle optimal passant par n’importe quelle arête (v, X) avec un nombre limité d’appels à l’algorithme de chemin le plus court. En ordonnancement, il y a moins de variables que de valeurs. Il est donc préférable d’opter pour la première méthode.

La complexité de l’algorithme est dominée par les |X | appels à l’algorithme de plus court che-min. En utilisant l’algorithme de Bellman-Ford, la complexité en temps devient O(|X |2|V|2).

D’ailleurs, en faisant circuler le flot des valeurs aux variables, Régin [24] évite d’avoir à trans-poser le réseau lors du filtrage. Ci-haut, v, l et u sont des vecteurs de taille d. Pour appliquer la cohérence de domaine, l’algorithme de Régin [24] utilise un algorithme de plus court chemin qui dépend de la taille de l’instance. Soit W le coût maximal retourné par cost et P (n, d, W ) le temps requis pour appliquer cet algorithme de plus court chemin, l’algorithme de Régin [24] est en temps O(nP (n, d, W )). Ainsi, l’algorithme de filtrage applique la cohérence de domaine

(33)

pour les variables Xi et la cohérence de borne pour le seuil H.

Exemple 2.4. Soit une instance pour la contrainte d’arité 3 définie comme suit : — CostGCC([X1, X2, X3], v = [1, 2, 3], l = [0, 0, 0], u = [2, 1, 1], cost, H)

— dom(X1) = {1, 2} avec cost(X1, a) = a

— dom(X2) = {1, 2} avec cost(X2, a) = a

— dom(X3) = {2, 3} avec cost(X3, a) = a − 2

— dom(H) = [0, 3] t X2 X1 X3 1 2 3 s 1 1 1 1(1) 1(1) 1(2) 1(2) 1(0) 1(1) 2 1 1

Figure 2.7 – Réseau encodant l’instance pour CostGCC

Le réseau de la figure2.7encode l’instance. La capacité et le coût de chaque arête sont indiqués selon le format capacité(coût).

Appliquer l’algorithme de Klein [17] construit un flot optimal dont le coût est de 2. Le flot est représenté dans le graphe résiduel de la figure 2.8. Le poids des arêtes illustre la capacité résiduelle et le coût résiduel. Tel qu’expliqué, si le coût d’une arête est w, son coût résiduel demeure w dans le graphe résiduel, mais devient −w si cette arête est inversée. Le coût du flot cost(f ) = 2 permet de fixer la borne inférieure de dom(H) à 2. Dans cet exemple, les composantes fortement connexes n’induisent pas de filtrage. Cependant, un filtrage selon les coûts est possible.

Le support illustré par le flot est {X1 = 1, X2 = 1, X3 = 2}. Par conséquent, les affectations

à valider sont X1 = 2, X2 = 2 et X3 = 3. Un appel à l’algorithme de plus court chemin pour

chaque variable révèle que le cycle optimal (X1 → 1 → s → 3 → X3 → 2 → X1) passant par

(2, X1) engendre un coût réduit de −1 + 1 + 0 + 2 = 2. Puisque 2 + 2 > max(dom(H)) = 3, la

valeur 2 doit être filtrée de dom(X1). Un raisonnement similaire mène à la même conclusion

(34)

t X2 X1 X3 1 2 3 s 1 1 1 1(−1) 1(−1) 1(2) 1(2) 1(0) 1(1) 2 1 1

(35)

Deuxième partie

(36)

Chapitre 3

Contrainte DisjunctiveTardiness

Tel qu’exposé, un réseau de flot s’avère efficace pour la conception d’algorithmes de filtrage en ordonnancement. Dans ce chapitre, cet outil de modélisation est repris pour introduire une nouvelle contrainte globale. La contrainte, dénommée DisjunctiveTardiness, tient mieux compte, pour un ordonnancement donné, de l’interaction entre la contrainte Disjunctive et la minimisation du retard pondéré où la pondération du retard est la même pour l’ensemble des tâches. Le problème encodé par la contrainte est énoncé. Une idée de sa difficulté est présentée. Un algorithme de filtrage pour la contrainte, basé sur une relaxation, est donné. Des liens sont faits entre les notions amenées et l’état de l’art de l’ordonnancement. Finalement, pour rendre l’algorithme applicable en pratique, différentes variantes sont introduites.

3.1

Définition de la contrainte

DisjunctiveTardiness généralise la contrainte Disjunctive pour tenir compte des re-tards engendrés par l’ordonnancement de tâches. Le retard d’une tâche i est donné par Ti = max(0, Ci − di) = max(0, Si + pi − di). Ainsi, soit l’ensemble de tâches I, pour que

la contrainte soit satisfaite, il ne faut pas que la somme des retards TI ne dépasse un certain

seuil T . TI est équivalent à la somme des retards pondérés où la pondération wi = 1 pour

toute tâche i dans I. Dans la définition, I est représenté par les trois vecteurs en paramètre.

DisjunctiveTardiness([S1, . . . , Sn], [p1, . . . , pn], [d1, . . . , dn], T ) = Disjunctive([S1, . . . , Sn], [p1, . . . , pn])∩{(s1, . . . , sn) | n X i=1 max(0, Si+ pi− di) ≤ T } (3.1)

3.2

Décomposition non satisfaisante

En programmation par contraintes, l’alternative actuelle pour simuler DisjunctiveTardi-ness est de la décomposer telle que définie. Il en résulte une contrainte Disjunctive en

(37)

conjonction avec une contrainte d’inégalité entre la somme des retards et le seuil T . Pour la contrainte Pn

i=1max(0, Si+ pi− di) ≤ T , un algorithme de filtrage commun consiste à filtrer

la borne supérieure des temps de début. Pour ce faire, l’algorithme teste, pour chaque tâche i, si son retard maximal additionné au retard minimal des autres tâches I \ {i} ne dépasse pas le seuil T . Si c’est le cas, la borne supérieure de Si est réduite à la plus grande valeur telle que le test n’échoue plus. Puisque le retard est non décroissant, cet algorithme applique la cohérence de domaine efficacement. Cependant, l’approche demeure non satisfaisante dans la mesure où il y a absence de raisonnement sur les interactions entre les contraintes. Pour la suite, cette approche naïve, notée NDT, servira de base pour évaluer la performance des

solutions proposées.

Exemple 3.1. Un exemple simple avec deux tâches permet d’illustrer une faiblesse de la décomposition NDT par rapport à la contrainte DisjunctiveTardiness. Soit une instance pour la contrainte avec deux tâches comme suit :

DisjunctiveTardiness([Si avec dom(Si) = [0, 1], Sj avec dom(Sj) = [2, 3]],

[pi = 2, pj = 1],

[di = 0, dj = 3],

T avec dom(T ) = [0, 3])

En ce qui a trait aux contraintes, l’état des domaines est déjà cohérent avec la contrainte Disjunctive. C’est également le cas pour la contrainte d’inégalité, car :

max(dom(Si))+pi−di+ min(dom(Sj))+pj−dj = (1+2−0)+(2+1−3) ≤ 3 = max(dom(T ))

min(dom(Si))+pi−di+ max(dom(Sj))+pj−dj = (0+2−0)+(3+1−3) ≤ 3 = max(dom(T ))

Ainsi, dans cet exemple, les contraintes sont cohérentes et n’engendrent pas de filtrage. Il y a pourtant un filtrage envisageable. Selon l’état des domaines, il n’y a pas de support avec Si = 1 pour la contrainte DisjunctiveTardiness. Effectivement, si la tâche i commence au

temps Si = 1, alors son retard est Ti = 3. Puisque le retard total TI = Ti+ Tj ne peut pas

pas dépasser 3, la tâche j ne peut pas être en retard et elle doit donc commencer au temps Sj = 2. Cependant, cet ordonnancement viole la contrainte Disjunctive, car les deux tâches

se chevauchent au temps 2. La valeur 1 doit donc être filtrée du domaine de Si. Il s’agit là

d’un type de raisonnement qu’une contrainte globale comme DisjunctiveTardiness apporte à un modèle.

3.3

Difficulté et popularité du problème

Le problème encodé par la contrainte DisjunctiveTardiness est un cas particulier du pro-blème demandant de trouver un ordonnancement séquentiel et non préemptif dont le retard

Figure

Figure 2.1 – Flot valide initial dans un graphe
Figure 2.4 – État des domaines dans l’exemple 1.3
Figure 2.5 – Réseau de flot encodant l’état des domaines
Figure 2.6 – Graphe résiduel après l’application de Ford et Fulkerson [ 9]
+7

Références

Documents relatifs

— La commission du Conseil national pour la revision dé Ja loLftur les fabriques est convoquée encore une fgja à Berne pour le 24 février, mais simplement&lt;jiftur

On cherche à minimiser le coût total encouru par le transport du flot des sources (sommets d’offre) vers les puits (sommets de demande)...

Identifier le cycle créé par l’ajout de (i,j), ainsi que l’arc (p,q) du cycle qui doit être enlevé afin de demeurer dans le domaine

Afin de déterminer si la solution est optimale, et dans la négative l’arc qui entrera dans la base, les variations de l’objectif sont calculées pour chaque arc hors-base :.. L’arc

Par contre dans la recherche des extremums relatifs, les endroits où la dérivée est nulle et les endroits où la dérivée n’existe pas.. sont des bons endroits

Par contre dans la recherche des extremums relatifs, les endroits où la dérivée est nulle et les endroits où la dérivée n’existe pas. sont des bons endroits

Contrairement à ce que l’on vient de voir dans le cas d’une action engendrée par un seul automorphisme, on va montrer que, dans un sens qui va être précisé maintenant, un

Communiqué par P. — Ce papier étudie le problème d''optimisation suivant : trouver le poids minimal d'une structure dans le cas où la fréquence fondamentale est fixée et où Von a