Problème avec Cached: je n'ai pas d'instance Traversable ni monad, donc je ne peux pas faire ce que je veux avec des valeurs de type Cached (IO ()) que je récupère en sortie de simulation.
Réfactoring et simplification: Voir README.md. En pratique et résumé:
- utiliser haskell pour construire des executables qui font les simulations et les statistiques et écrire les fichiers de données au format gnuplot,
- utiliser gnuplot pour générer les figures,
- orchestrer tout ça avec make.
Prochaine étape: réaliser en haskell tous les executables spécifiés dans README.md, section "F: Haskell -> File".
Écrire commande "haskfile histosteps".
Fini commande histosteps.
En cours: écrire commande mean-std-l2-vs-nsimus, arrêté sur le calcul de moyenne et écarts types
Fini commande MeanStdL2VsNSimu
Prochaine étape, avant d'implémenter le reste des commandes, écrire le Makefile pour générer toutes les figures déjà disponibles: steps et l2 vs nsimus
Problème dans l'écriture des fichiers histo: ils contiennent la même chose.
Résolu: dans la fonction HaskFile.writeListWith, j'utilise liftA2 sur des listes, ce qui combine chaque élément de la première liste (fichiers) à chaque élément de la seconde (histo). J'ai résolu le problème en utilisant des ZipList.
Prochaine étape: générer la figure L2 vs NSimus avec make.
Écrire les règles du Makefile pour générer la figure L2 vs NSimus.
Écrit la règle pour la figure, les règles implicites pour les simulations et les règles pour la génération automatique des spécifications de simulation.
Problème: les règles implicites pour les simulations ne se déclenchent pas si les fichiers en dépendances (les spécifications de simulation) n'existent pas. Or, comme je voudrais que ceux-ci soit générés à la demande, automatiquement déclanchées quand je demande un résultat de simulation, ça ne marche pas.
Prochaine étaple: Générer les spécifications de simulations hors du makefile (avec un script et à l'aide des templates) pour être sûr qu'elles existent avant d'appeler make.
Générer les spécifications de simulations hors du makefile.
Créé une règle setup
dans le makefile qui s'occupe de générer la structure des répertoires nécessaires et les spécifications de simulations. Il faut executer make setup
avant de pouvoir executer make
.
Problème dans le fichier de statistiques pour L2 vs Nsimus. Vérifier les calculs.
Terminé la génération de la figure L2 vs NSimus. Reformattage des fichiers de données gnuplot pour utiliser la fonction de légende automatique (une ligne entre guillements au début de chaque bloc de données).
Prochaine étape: Réfléchir aux autres figures à générer. benchmark de la parallèlisation, etc.
Utilisation de fichiers sentinels dans le makefile pour éviter de tomber en enfer quand on execute make en parallèle.
Prochaine étape: réfléchir aux autres figures à générer, nettoyer le dossier report/.
Implementer echantillonnage LHS des valeurs par (parallelisme), N / K (N correspond au nombre de simulation par iteration, ), V (la variance du temps de simulation).
En cours, traduction de MonAPMC en pseudo code dans README.md. Reprendre au calcul des poids dans la fonction step
Traduire MonAPMC en pseudo code dans README.md.
Première ébauche de traduction terminée. Il faudra plus tard uniformiser la notation et écrire les formules en latex.
Reprendre implémentation échantillonnage LHS des valeurs par (parallelisme), N / K (N correspond au nombre de simulation par iteration, ), V (la variance du temps de simulation).
Avant l'échantillonnage LHS, faire des tests plus petits: cas uniques pour illustrer les hypothèses sur l'efficacités de MonAPMC en choisissant les valeurs de paramètres (voir README). Pour réaliser ces expériences, il faut coder un simulateur d'execution d'algo en parallelisme pour mesurer le temps que prendrait un algo lancé en parallèle mais sans parallélisme. Ça me permettra de simuler l'execution d'un algo de 1 à 100 cœurs ou plus, pour pouvoir notamment mieux tester les cas ou le nombre de simulations de modèle par iteration est proche du nombre de cœurs (pour 1000 cœurs).
Implémenté une manière de mesurer le temps d'execution dans le module Execution
avec les fonctions evaluate
et force
. À tester.
Il reste aussi à implémenter le simulateur de parallelisme simScheduler
dans le même module.
Implémenter le simulateur de parallelisme puis tester la fonction simEasyPar
.
Simulateur EasyPar implémenté et testé.
En cours: implémentation du simulateur pour PlasticPar
À vérifier: est-ce qu'il est important de conserver l'ordre des xs et ys avant et après le scheduler? Si oui, revoir les simulateurs.
J'ai changé l'algorithme d'execution dans simPlasticPar pour qu'on puisse passer un état initial et que celui-ci soit mieux divisé pour créer les premiers runners. Tester et reporter à l'algo d'execution runPlasticPar, scanPlasticPar et éventuellement l'implém scala.
Fini d'implémenter et de tester le simulateur simPlasticPar.
Prochaine étape:
- vérifier le problème de conservation de l'ordre des simulations dans simEasyPar.
- adapter la gestion de l'état initial dans run/scanPlasticPar et dans mgo.
Corrigé simEasyPar pour conserver l'ordre entre les entrées et les sorties du scheduler.
Supprimé l'utilisation de scanPlasticPar pour garder uniquement l'algo implémenté dans simPlasticPar. Le premier devrait être mis à jour avec le dernier si il faut utiliser la fonction.
Prochaine étape: faire les simulations les graphes nécessaire pour visualiser la valeur de L2 en fonction du parallélisme, du ratio du nombre de simulations par rapport au nombre de cœurs, et de la variance de la durée de simulation. On fait varier un paramètre à la fois selon des valeurs choisies, et les valeurs par défaut des paramètres sont: n=5000, nAlpha=500, pAccMin=0.01, parallel=1, stepSize=1, stopSampleSize=4500.
Préparé le makefile pour faire les nouvelles simulations: simu + stats + figure.
Refactoré le code haskell pour utiliser simEasyPar et simPlasticPar partout. Les tests passent.
À faire:
- coder en haskell l'executable qui sert à calculer les stats L2 vs time pour les replications (voir la règle du makefile pour
files_stat_l2_vs_time_k
) - coder le script gnuplot
Revoir l'organisation du code haskell pour refléter l'organisation en "définitions/valeurs" - "fichiers" comme dans le makefile.
Codé les stats L2 vs Time pour des replications de steps, créé l'executable correspondant et intégré au Makefile dans la section Stat L2 vs time.
Faire le script gnuplot pour Générer la figure L2 vs time K.
Scrit fait et figure Générée.
Trouver un cas où MonAPMC est vraiment plus efficace que APMC (voir cas idéal dans le readme.)
Automatisé la création des fichiers de specification de simulation dans input/simu avec le Makefile
et le script util/populate_simu_specs.sh
.
Les noms des paramètres d'APMC et MonAPMC n et nAlpha sont confus. Adaptation des noms pour faire directement référence à la taille de l'échantillon (nAlpha) et le nombre de simulation effectuées à chaque étape (nGen).
Changé le dernière valeur de K dans Fig L2 vs Time K pour 100 au lieu de 10. Il a fallu aussi adapter la valeur de stepMax pour que les deux algos fassent au max le même nombre de simulations.
Pour sampler une distribution gamma (utilisé dans le modèle avec variance du temps d'exécution), je n'ai trouvé que la bibliothèque statistics qui utilise System.Random.MWC (il y a aussi Data.Random mais je ne sais pas à quoi correspondent les paramètres de la distribution gamma). J'utilise cette bibliothèque dans Distribution.gammaRandomSample. Pour l'instant, la méthode doit créer une nouvelle seed à chaque appel à partir d'un générateur StdGen, ce qui est lent et/ou peu robuste statistiquement puisqu'on perd les propriétés du générateur MWC. TODO: il faudra passer à Problème: un générateur MWC est mutable, on ne peut donc pas l'utiliser dans plusieurs threads en même temps, attention à la parallelisation. (En préparation de l'utilisation de MWC partout, je l'ai mis aussi dans Distribution.normalRandomSample.)
Le script util/populate_simu_specs.sh
n'est pas facile à adapter pour les noms de fichiers plus complexes avec les nouveaux modèles (..._modelToyTimeVar_1_1_...
). Pour les traiter plus facilement, j'ai écrit un executable en haskell qui parse les noms de fichiers pour construire la spec désirée.
Implémentation des modèles au temps d'execution variable ToyTimeBias et ToyTimeVar. ToyTimeBias est paramêtré avec la moyenne et la variance pour pouvoir faire varier cette dernière en gardant constant le temps moyen d'execution d'un modèle, et donc le temps total d'execution des modèles.
La figure L2 vs Time K V montre comme on s'attendait qu'APMC offre un avantage sur APMC quand à la fois le parallelisme et la variance de temps de calcul sont élevés.
J'ai complété la figure L2 vs Time K V en montrant aussi le cas pour chaque algo où le temps d'execution du modèle est dépendant des valeurs de paramètres de l'algo (ToyTimeBias), ce qui pourrait introduire un biais. La Figure Fig Steps Bias montre aussi l'histogramme de l'échantillon final pour APMC, MonApmc avec nGen = 40 et MonAPMC avec nGen = 1. Pas de biais visible.
Refactoring du Makefile pour que les paramètres qui contrôlent chaque figure soient mieux localisés ensemble et ne pas avoir à changer plusieurs parties du fichier (fichiers de stats et fichiers de simus par exemples) quand on veut modifier une figure. Pour cela, j'ai créé une unique liste des simulations à laquelle les différentes parties du fichier se réfèrent en utilisant la fonction make foreach
. Harmonisation du format des recettes en "input, output, sentinel"
Dans, L2 vs time K V, le L2 est élevé pour le cas ou le temps d'execution du modèle est biaisé. C'est que le L2 est calculé par rapport à une distribution théorique gaussienne! Il faut corriger ça.
Corrigé la distribution de référence pour le calcul du L2 avec le modèle uniforme ToyTimeBias. On retrouve bien des valeurs de L2 attendues dans la figure L2 vs time K V.
Attention à bien recompiler les executables haskell quand je retouche du code, et à bien prendre les nouvelles versions.
La version scan de simEasyPar prend beaucoup de mémoire en gardant toutes les itération, ce n'est pas nécessaire quand on veut just le résultat du run. J'ai créé simEasyParScan et simEasyParRun pour pouvoir choisir.
TODO:
- faire la même chose avec simEasyPar et tester si ça change l'utilisation de la mémoire
- ajouter le critère stepMax directement aux fonctions
sim*
plutôt que de les traiter avec take stepMax dans Experiment.
Corrigé aussi la même fuite mémoire dans simEasyPar en créant deux versions simEasyParScan qui conserve les données de chaque iteration et simEasyParRun qui ne les conserve pas.
L'utilisation de fichiers de spécification de simulation dans input/simu/
est trop rigide, nécessite de gérér une longue liste de simulations dans le makefile pour que ces fichiers soient générés quand nécessaire, et de créér ces fichier quand je veux lancer une simulation automatiquement ou à la main. J'ai éliminé l'utilisation de fichier input, remplacer par un string qui spécifie la simulation passé aux commandes haskfile run
, haskfile steps
, ...
Lors d'un run MonAPMC consomme constamment autant de mémoire que APMC en consomme lors des pics. Est-ce que MonAPMC garde en mémoire toute la population? Il semble plutôt que ça soit du au parallélisme de MonAPMC. L'empreinte mémoire est grande et constante quand le parallelisme est grand (100) et nGen petit (40), mais plus petite et en pics quand le parallelisme est petit (4) est nGen grand (1000). MonAPMC maintient un nombre d'états complets de l'algorithme égal au le niveau de parallélisme et chacun contient un échantillon de taille nAlpha (500). C'est normal que l'empreinte mémoire totale soit plus importante quand le parallélisme est grand. Il faut noter qu'en situation de parallélisation à distance, l'empreinte mémoire sera aussi divisée entre les différents nœuds en parallèle.
Lancé les simulations LHS sur grille.
Ajouté la contraintes que nAlpha soit strictement positif. Quand nAlpha est nul, on obtient une erreur "vector index out of bounds." Ça n'a pas de sens qu'nAlpha puisse être nul, ça voudrait dire qu'on veut obtenir un échantillon de taille nulle. On autorise nGen a être nul par contre. Ça n'a pas vraiment de sens non plus, mais on s'en sert dans la fonction step de MonAPMC: on fixe nAlpha = nGen puis nGen = 0 avant d'appeler la fonction stepOne d'APMC, qu'on réutilise pour générer les particules initiales (jusqu'à ce que la taille de l'échantillon dans l'état de MonAPMC atteigne nAlpha).
On ne commence à voir un effet de la variance du temps d'execution du modèle sur
le Time Ratio seulement pour de grandes valeurs par rapport au temps moyen
d'execution, c'est-à-dire quand l'écart-type est au moins égal à meanRunTime,
donc quand la variance est au moins égale à meanRunTime ** 2
. Par exemple:
- CompParams 1000 1000 0.05 100 100 1 100 ((100 * 1) ** 2) -> time ratio = 1.47292893931291
- CompParams 1000 1000 0.05 100 100 1 100 ((100 * 2) ** 2)) -> time ratio = 2.4873363930645107
- CompParams 1000 1000 0.05 100 100 1 100 ((100 * 3) ** 2)) -> time ratio = 3.6797020447571023
Ça explique peut-être pourquoi on ne voit pas d'effet net dans Fig Time Ratio Effects LHS. Dans l'échantillonnage LHS, la variance et le temps moyen d'execution varient tous les deux entre 0 et 100. La variance est donc souvent en dessous du seuil où on commence à observer un effet (pour meanRunTime = 10, c'est 100, pour meanRunTime = 20, c'est 400...).
Un écart-type de temps d'exécution au moins égal au temps moyen de calcul ne me semble pas réaliste. On peut donc en rester là avec cette histoire de l'effet de la variance. Le résultat principal pour MonAPMC reste que le time ratio augmente en sa faveur avec le parallélisme.
Todo: Changer Stats Comp Test Cases pour ne plus faire varier varRunTime mais le fixer à 1/10 du meanRunTime. Faire des réplications et calculer le gain de temps simplement. Pas besoin de Fig Comp Test Cases.