Buts : Il s'agit ici de compléter le codage des bactéries à flagelle unique en leur permettant de percevoir les nutriments, de se diviser et de muter.
Avec le mode de déplacement que vous avez codé à l'étape précédente, une bactérie à flagelle unique n'avance que droit devant elle. Elle ne peut compter que sur le pur hasard (des nutriments sur sa trajectoire) pour se nourrir en se déplaçant. S'il y peu de sources de nutriments, ses chances de survie sont faibles.
Nous allons maintenant améliorer ce modèle en rendant la bactérie sensible à un gradient de concentration des nutriments.
Au lieu de modéliser des milliers de petites particules émises par une source de nutriments, nous considérerons qu'il y a un gradient de concentration des nutriments proportionnel à la distance au centre de la source. Les bactéries à flagelle unique auront une perception de ce gradient qui pourra induire des changements de direction et influencer leur mode de déplacement.
La perception du gradient depuis une position donnée p se fera par le biais du calcul d'un score attaché à p.
Le score de p par rapport à une source de nutriments s se calcule comme:
score(p) = taille(s) / (distance(p,centre(s))puissance
S'il y a plusieurs sources de nutriments, le score de p sera le cumul des scores pour chaque source de nutriments.
Ainsi le score sera d'autant plus élevé que l'on est près de plus nombreuses ou de plus grosses sources de nutriments.
Commencez par ajouter à Laboratory une méthode getPositionScore(const Vec2d&) retournant le score d'une position donnée.
[Question Q4.1] Comment devez-vous modifier CultureDish pour mettre en oeuvre cette fonctionnalité ? Répondez à cette question dans votre fichier REPONSES puis implémentez les fonctionnalités suggérées.
L'attribut puissance fait partie des éléments qu'il est prévu de pouvoir contrôler via l'interface graphique. Il s'agit du paramètre "Gradient exponent" qui s'affiche dans l'interface graphique au-dessous de "Temperature".
Vous procéderez donc de la même manière que vous l'avez déjà fait pour les températures : ajout de méthodes increaseGradientExponent(), decreaseGradientExponent() et getGradientExponent() placées aux bons endroits. Vous aurez également à invoquez ces méthodes de façon appropriée dans Application.hpp. Pensez aussi à adapter la gestion de la touche 'C' (lorsque le fichier de configuration est rechargé il faut recalculer puissance, selon les mêmes modalités que lors de la construction).
Pour améliorer sa chance d'accès aux nutriments une bactérie à flagelle unique essayera de basculer (à intervalles de temps réguliers) pour changer sa direction de déplacement.
La probabilité de basculement de départ est une propriété de la bactérie. Cette probabilité va évoluer en fonction de la perception du gradient (c'est-à-dire le score) : plus cette perception est grande et plus la probabilité de basculer va faiblir.
Ainsi, si la bactérie se déplace dans une direction la menant à des nutriments elle aura de moins en moins de chance de basculer. Inversement, si sa perception du gradient faiblit, ses chances de basculer augmenteront.
Il vous est maintenant demandé de compléter la méthode de déplacement de la bactérie à flagelle unique de sorte à ce que, à la suite des traitements existants, il y ait :
Voici une version très simple, que vous pourrez améliorer plus tard :
Soit ancien_score le score de la bactérie lors du pas de simulation précédent et score son score courant.
La probabilité p de basculer est calculée selon une loi de croissance exponentielle:
p = 1 - exp(-t/lambda)où t est le temps écoulé depuis le dernier basculement et lambda est le poids associé à la péjoration ou à l'amélioration du score.
Cette fonction garantit que, la probabilité de basculer est d'autant plus grande que le temps écoulé entre deux basculements est important et que le score s'est péjoré entre deux déplacements de la bactérie.
[Question Q4.2] Quel(s) attributs suggérez-vous d'ajouter à la représentation des bactéries (à flagelle unique ou génériques) pour permettre la mise en oeuvre cet algorithme ? Comment initialiser ces attributs et à quels endroits devez-vous les mettre à jour ?
Explicitez et justifiez vos choix dans votre fichier REPONSES.
De multiples variantes sont possibles :
Pour vous permettre d'expérimenter plusieurs variantes, vous pouvez utiliser les valeurs associées à ["tumble"]["algo"] dans le fichier de configuration.
Par exemple, si la valeur associée à ces étiquettes est "single random vector" exécuter la stratégie 1 ci-dessus, si la valeur est "best of N" choisir la seconde etc.
Pour la stratégie 1 il suffirait d'appeler une fois cette fonction pour mettre en oeuvre le changement de direction.
Pour la stratégie 2, on en génère N (par exemple 20) et on retient celle telle que le Vec2D de coordonnées position_bactérie + direction_aléatoire a le meilleur score.
Intégrez maintenant le basculement au déplacement de la bactérie à flagelle unique.
Nous abordons maintenant les dernières fonctionnalités liées aux bactéries de façon générale, à savoir la division et la mutation. Nous considérons pour simplifier que toutes les bactéries peuvent se diviser et muter selon des modalités que l'on peut considérer comme étant les mêmes pour toutes. Lors de la division, la bactérie va se scinder pour produire une autre bactérie identique (méthode Bacterium* clone());
La méthode de mutation d'une bactérie (quelle qu'elle soit) consiste simplement à appeler la méthode mutation sur chacun de ses paramètres mutables. Cela peut induire des mutations effectives de certains paramètres (mais pas forcément).
[Question Q4.3] Dans quelle classe proposez-vous d'ajouter la méthode de mutation ? Explicitez et justifiez votre choix dans votre fichier REPONSES.
Vous considérerez qu'une bactérie à flagelle unique a pour propriétés mutables :
Pour mettre en place la gestion des propriétés mutables, vous commencerez par ajouter à votre hiérarchie de classes de bactéries les méthodes addProperty(const string&, MutableNumber) et getProperty(const string&) permettant d'ajouter à l'ensemble des paramètres mutables numériques de la bactérie une valeur numérique mutable donnée ou de retrouver une valeur mutable associée à une clé donnée.
[Question Q4.4] Dans quelle classe proposez-vous d'ajouter ces méthodes ? Explicitez et justifiez votre choix dans votre fichier REPONSES et codez les méthodes suggérées.
auto paire = une_map.find(key);Si aucune entrée n'a pour clé key alors paire contiendra la valeur une_map.end() (nous reverrons tout cela en détail dans notre cours sur la librairie standard).
Modifiez le constructeur de la classe MonotrichousBacterium de sorte à ce qu'il lui ajoute les paramètres mutables souhaités comme suit :
La méthode de division est la même pour toutes les bactéries :
La méthode clone doit donc réaliser une copie polymorphique. L'exercice 1 de la série 21 vous donne des indications à ce propos.
[Question Q4.5] Où choisissez-vous de placer la méthode de division d'une bactérie ? Répondez à cette question dans votre fichier REPONSES en justifiant votre choix.
Complétez maintenant la méthode Bacterium::update de sorte qu'après le test de collision avec les nutriments il y ait appel à la méthode de division (laquelle appellera la méthode de mutation).
Nous avons vu à l'étape précédente que modifier la taille d'une collection alors que l'on est en train d'itérer dessus est potentiellement problématique. A l'étape précédente le risque était lié à la suppression des éléments morts ou épuisés. Nous avons ce même problème maintenant mais en raison de l'ajout de nouvelles bactéries lors du clonage.
Pour éviter ce problème nous vous suggérons de d'abord stocker les bactéries créées par clonage dans un vector annexe (attribut de la classe). Vous pourrez fusionner ce vector avec la collection de bactéries avant d'itérer dessus. Ainsi toutes les bactéries obtenues par clonage lors du pas de simulation précédent pourront être intégrées à la simulation).
La fonction append fournie dans Utility.hpp permet d'ajouter le contenu d'un vector à un autre (par exemple append(v1, v2) permet d'ajouter à v2 tous les éléments de v1 en les insérant dans l'ordre à la fin de v2).
Vous devriez voir croître et décroître les populations : la population croît car les bactéries se divisent. Elles deviennent parfois trop nombreuses pour les nutriments disponibles et la population décroît alors. Au bout d'un certain temps, des mutations de couleur devraient être observables:
|
← ici la simulation est faite avec ce contenu du fichier app.json (le fichier app_original.json correspond aux données fournies au départ dans l'archive, si vous souhaitez restituer ce contenu) |
On ne peut tester ici visuellement que la mutation de la couleur. Dans la suite du projet vous ajouterez à votre programme le dessin de courbes d'évolution de certains paramètres et il deviendra plus facile d'observer l'évolution des paramètres mutables.