TP 5/6 : Refactoring d'un composant logiciel (2/3 et 3/3)
Important : Consultez les modalités de rendu.
Deuxième TP
Le type des contraintes
Gérer les solutions
À ce stade, nous affichons les solutions sur la sortie standard. Nous allons améliorer ce point.
- comptage
- mémorisation (avec un record pour chaque affectation)
- affichage (ou pas)
Gérer les stratégies
- un travail de simplification du problème à faire avant la recherche,
- le choix de la prochaine variable à instancier (findVariable),
- la manière d'explorer le domaine de cette variable (le calcul de step),
- le travail de vérification à faire après chaque exploration du domaine.
- Créer une interface IStrategy avec les quatre actions évoquées.
- Créer une classe DefaultStrategy qui implémente les choix par défaut.
- Utiliser l'interface et la classe afin de simplifier le solveur.
- Créer une deuxième classe pour la stratégie reduceAndCheckIntervalsStrategy.
La réduction d'intervalles
La réduction d'intervalles est très importante afin de diminuer l'espace de recherche et d'identifier des impasses (intervalles vides). Nous allons isoler cette étape afin de la simplifier et la tester.
- Créer une classe Reducer qui travaille sur un problème à résoudre et propose une méthode de réduction (méthodes à récupérer depuis le solveur).
- Créer un test unitaire afin de vérifier la réduction d'un ensemble de contraintes (par exemple A+B=1000, A+B=2000).
- Créer un test unitaire afin de vérifier la réduction de la contrainte multiplicative.
- La réduction de la contrainte X <> Y n'est pas prévue. Cette réducation n'est possible que si l'un des deux variables est fixée et si sa valeur correspond au min/max de l'autre variable. Voila trois exemples qui peuvent servir à construire un test unitaire.
X in [10,20], Y in [20,20] ---> X in [10,19], Y in [20,20] X in [10,10], Y in [10,11] ---> X in [10,10], Y in [11,11] X in [15,15], Y in [15,15] ---> X in [], Y in []
- Associer à chaque variable la liste de ses contraintes.
- Associer à chaque variable un observateur (interface Consumer<Variable>) qui va être informé de la réduction du domaine (méthode reduce).
- Maintenir, au niveau du Reducer, un ensemble de contraintes à étudier (en observant les variations de domaine des variables).
Troisième TP
La stratégie alwaysReduce
Nous allons maintenant construire une stratégie qui consiste à réduire le problème à chaque étape.
- Contrairement aux autres stratégies, cette dernière est susceptible de modifier plusieurs domaines à chaque étape. Un travail de sauvegarde/restauration est donc nécessaire. Prévoir une classe Backup qui se charge d'une sauvegarde et de l'opération de restauration associée.
- Il faut sans doute modifier l'interface IStrategy pour prendre en compte cette spécificité.
- Proposer une implémentation de cette stratégie et vérifier sur un problème que cette stratégie est bien plus efficace.
private Solver makeQueens(int n) { var solver = new Solver(); var queens = new Variable[n]; for (int i = 0; i < n; i++) { queens[i] = solver.newVar("R" + i).domain(1, n); for (int j = 0; j < i; j++) { solver.addConstraint(queens[i], "<>", queens[j]); solver.addConstraint(queens[i], "+", i, "<>", queens[j], "+", j); solver.addConstraint(queens[i], "-", i, "<>", queens[j], "-", j); } } solver.reduceAndCheckIntervalsStrategy(); return solver; }
Un problème d'optimisation
Cet exercice est optionnel mais très intéressant !
Nous allons maintenant changer le fonctionnement de notre solveur en ajoutant la minimisation de la valeur d'une variable. Par exemple, pour le problème ci-dessous, la plus petite valeur de COST est 330 pour X=70 et Y=30.
0 <= X, X <= 70, 0 <= Y, Y <= 70, X + Y = 100, COST = (3 * X) + (4 * Y)
- Préparer une classe OptimizationStrategy qui hérite de AlwaysReduceStrategy.
- Cette stratégie va conserver la valeur courante minimale de la variable à minimiser (variable qu'elle doit donc connaître).
- A chaque solution, cette valeur est éventuellement mise à jour.
- A chaque réduction, cette stratégie va réduire le domaine de la variable pour ne pas autoriser des valeurs plus grandes que celles déjà trouvées. Ainsi, à chaque étape, nous améliorons notre objectif.
- Amélioration : Comment faire pour ne plus renvoyer les solutions non-optimales ?
Agent 1 Agent 2 Agent 3 Agent 4 tâche 1 : 8 5 9 9 tâche 2 : 4 2 6 4 tâche 3 : 7 3 7 8
public Solver buildAssignmentProblem(int[][] costs) { var solver = new Solver(); int nbTasks = costs.length; int nbAgents = costs[0].length; Variable[][] matrix = new Variable[nbTasks][nbAgents]; // create nbTasks * nbAgents variables 0..1 for (int t = 0; t < nbTasks; t++) { for (int a = 0; a < nbAgents; a++) { matrix[t][a] = solver.newVar("T" + t + "A" + a).domain(0, 1); } } // each task have one agent var zero = solver.newConstant(0); for (int t = 0; t < nbTasks; t++) { var sum = zero; for (int a = 0; a < nbAgents; a++) { sum = solver.expression2(sum, "+", matrix[t][a]); } solver.addConstraint(sum, "=", 1); } // each agent have at most one task for (int a = 0; a < nbAgents; a++) { var sum = zero; for (int t = 0; t < nbTasks; t++) { sum = solver.expression2(sum, "+", matrix[t][a]); } solver.addConstraint(sum, "<=", 1); } // compute cost var cost = zero; for (int t = 0; t < nbTasks; t++) { for (int a = 0; a < nbAgents; a++) { cost = solver.expression2(cost, "+", matrix[t][a], "*", costs[t][a]); } } var varCost = solver.newVar("cost"); solver.addConstraint(cost, "=", varCost); solver.setVerbose(false); // Code a adapter pour minimiser cost .... .... return solver; }
Modalités de rendu vis etulab
- Le rendu de votre projet Solver est à faire pour le jeudi 24 octobre 2024 à 23h59. Il faudra que le dépôt git sur etulab soit à jour et que Jean-Luc Massat (identifiant massat) soit membre du projet avec des droits au moins égal à reporter.
- L'ajout de membre se fait via manage / members dans le menu de votre projet sur etulab, puis en cliquant sur le bouton invite members.
- Votre projet devra contenir à sa racine un fichier README.md contenant les informations sur ses membres et les emprunts réalisés. Le format du fichier devra être le suivant :
# Solver ## Membres du projet - NOM Prénom du premier membre - NOM Prénom du deuxième membre (si applicable) ## Description des emprunts - Utilisation de ChatGPT pour les classes : `Main.java`, ... - ...