Skip to content
Snippets Groups Projects
CHANGES.md 25.2 KiB
Newer Older
# 9 janvier 2025
Modification des sorties `[u]` pour avoir, à chaque tour,
le nombre de vendeurs viables, par type de matière
première vendue.

BERNARD Stephan's avatar
BERNARD Stephan committed
# 8 janvier 2025
Sorties des traces des simulations, pour les flux de matières premières, les clients et les vendeurs (agrégées au moment des changements de stratégies et en fin de simulation).
BERNARD Stephan's avatar
BERNARD Stephan committed

# 27 novembre 2024
Refonte du modèle : introduction des matières premières
extraites et recyclées.

# 27 septembre 2024
Ajout du calcul de pondération en fonction du nombre de clients
depuis le début de la simulation (POND_MODE 4), ce qui n'est pas
le nombre de ventes (POND_MODE 1).

Ce mode converge très difficilement.

# 24 septembre 2024
Correction d'un bug : la pondération petits / gros vendeurs
omettait la soustraction de la pondération de ce vendeur
à la somme des pondérations, lorsqu'il n'y avait pas affaire
avec ce vendeur.


# 17 septembre 2024
Version utilisée pour les diagrammes de la réunion du 24/09/24.

Qualité est devenue continue. 

Disparition du seuil, qui devient identique pour tous les vendeurs.

Modifications sur les pondérations :

- La fidélité est désactivée. Ainsi on peut faire tourner le modèle
  sans contraintes et ajouter les biais pour observer les changements.
- Une pondération qui favorise les plus petits vendeurs et les plus gros
  est activable. Pour le moment basée sur le nombre de ventes depuis
  le début de la simulation.  
  Cette pondération est celle d'une courbe (bx - a)² + c
  (de mémoire. c'est pê ax-b…), les paramètres a,b,c sont dans
  la fonction init() des clients.
- Et on peut désactiver pour n'avoir qu'un tirage aléatoire.

Il est possible d'avoir la trajectoire dans les sorties (c'est vite
très verbeux, il faut limiter le nombre de simus).

Tout ça se commande à partir du fichier congig.h qui devient un peu
touffu. Mais il est très documenté.

Le tracé de la matrice de transition permet un tracé de courbe de
chaleur. Les paramètres ont un peu changé (c'est documenté
tant bien que mal).

# 3 septembre 2024
BERNARD Stephan's avatar
BERNARD Stephan committed
Version GPU.

Le code a été réécrit en se basant sur les librairies CUDA.

La structure du code a été changée. La boucle principale est dans `main.cu`.

Les sorties sont sous la même forme que précédemment à quelques petites
modifications près ; on retrouve les lignes `[i]`, `[t]`, `[b]` et `[q]`.

Un *seuil_min* a été ajouté, qui peut être mis à 0.

## Compilation et exécution
Pour commencer, éditer le fichier `config.h` qui contient les paramètres
des simulations. Le fichier est commenté, en espérant que ces commentaires
soient suffisamment compréhensibles.

La distribution de la population des clients est dans `stats.h` qui
contient le tableau de la répartition des revenus par ménage en 2018.

```bash
# Compilation
nvcc main.cu -o ccsyl

# Exécution avec sortie à chaque tour
./ccsyl | tee resultats.txt | grep '^\[t'
```

# Pistes d'amélioration
L'exécution sollicite la carte graphique et un coeur du processeur.
On se rend compte que la partie du processeur comprend ce qui n'est
pas parallélisable, et qu'il constitue un nouveau goulot d'étranglement.

Donc toute optimisation à ce niveau a un impact important.
En particulier il y a quelques additions de tableaux qui pourraient
faire l'objet d'un algorithme de type reduce. Celui-ci est proposé
dans les outils CUDA.


# 23 août 2024
## Nouvelle branche
Le commit d'aujourd'hui sera suivi de la création d'une
nouvelle branche ; le modèle commençant à se préciser,
des simulations en masse sont à prévoir.

Le présent code parallélise les simulations
sur les différents cœurs à l'aide de la librairie openmp,
mais étant donné que le code des clients est plutôt
simple, il serait beaucoup plus efficace de le paralléliser
sur un GPU. C'est ce qui sera fait sur la branche master,
tandis que la v1 contient le code actuel sur openmp.

## Matrices de transition
L'étude des classes de qualité, de marges et de seuils
se voit étoffée du tracé des matrices de transition.

Sur l'axe vertical, on a les classes à l'état initial,
et sur l'axe horizontal on a les classes à la fin
de la simulation. Seules les simulations ayant convergé
sont représentées.

Celles-ci sont calculées et tracées dans
[[classes/matrice_transition.scm]] :

```sh
cd classes/

emacs matrice_transition.scm
# Il faut renseigner V : nombre de vendeurs,
# Q : nombre de classes de qualité, et _COL_A_AFFICHER
# (_COL_Q : qualité, _COL_M : Marges et _COL_S : seuils),
# puis :

xzcat ../v5q4-100k-cs0.5.txt.xz | \
  guile -s matrice_transition.scm gnuplot norm | gnuplot
```

La configuration n'est pas des plus ergonomiques, désolé,
je n'avais pas fait de scheme depuis longtemps.

Ici on a utilisé l'argument *gnuplot*, qui génère une sortie
qui peut être directement pipée sur gnuplot
pour la visualisation. Sans l'argument *gnuplot*, on obtient
simplement la matirce sur la sortie standard.

L'argument *norm* normalise la matrice ligne par ligne.
Il est également disponible sans le mode *gnuplot*.

La lecture des arguments est rigide : il faut donner
les arguments dans l'ordre et exactement comme attendu.

BERNARD Stephan's avatar
BERNARD Stephan committed
# 5 août 2024
## Fidélité relative
(Voir le chapitre “choix du vendeur” au *21 juin 2024*)

Plutôt que d'utiliser le coefficient de fidélité d'un client
pour un vendeur, qui ne tient pas compte de ce coefficient
pour les autres vendeurs (et pour le même client), il semble
plus intéressant de considérer la probabilité que ce vendeur
soit choisi en priorité par ce client.

C'est ce qu'on appelle la fidélité relative du client pour
ce vendeur.

## Classes de qualités
Le projet est de donner des résultats par « classe de qualité ».
On veut, dans les sorties, avoir l'effectif des vendeurs pour chaque qualité.  
Exemple :
BERNARD Stephan's avatar
BERNARD Stephan committed

| *qualité* | 1 | 2 | 3 | 4 |
|----------:|--:|--:|--:|--:|
|    Simu 1 | 2 | 0 | 1 | 2 |
|    Simu 2 | 1 | 1 | 2 | 1 |
|    Simu 3 | 1 | 2 | 2 | 0 |
|       (…) |   |   |   |   |

On dira que  (2, 0, 1, 2) est la classe de qualités de la première simulation.
On espère, sur un nombre de simulations suffisamment important, voir
s'il y a des aggrégats et des manques dans les classes de qualités.
À noter qu'on pourra tenter la même chose sur d'autres valeurs que la qualité
(il suffira de les discrétiser). Dans un premier temps on discrétisera
marge et seuils en autant de valeurs que la qualité.
### Sorties
Dans les sorties, les résultats par classes de qualités
sont donnés par les lignes commençant par `[q]`.
Parce qu'il est intéressant de comparer la distribution des classes de qualités
avant et après la simulation, les classes de qualité de la population telle
qu'elle est répartie au début de la simulation sont écrites aussi, dans les
### Nombre de simulations
Si on prend Q = nombre de classes de qualité et V = nombre de vendeurs,
le nombre de combinaisons possibles n'est pas trivial.

Si on note $N_V^Q$ le nombre de classes pour $V$ vendeurs
et $Q$ qualités possibles, on a :

* $N_V^1 = 1$
* $N_1^Q = Q$
* $N_V^Q = N_{V-1}^Q + N_V^{Q-1}$

Pour expliquer cette dernière formule, prenons par exemple $D_4^3$
*(désolé pour l'absence de rigueur dans la notation, il a été choisi de*
*privilégier la compréhension visuelle)* :

$$D_4^3 = \begin{matrix}
(0 & 0 & 4)\\
(0 & 1 & 3)\\
&\vdots&\\
(3 & 1 & 0)\\
(4 & 0 & 0)
\end{matrix}$$

En ajoutant une colonne (donc une qualité), on obtient
*(la séparation en deux est là pour bien distinguer deux parties,*
*mais c'est à lire ligne par ligne, de la même façon qu'au-dessus)* :

$$D_4^{3+1} = \begin{matrix}\begin{bmatrix}
0 & (0 & 0 & 4)\\
0 & (0 & 1 & 3)\\
\vdots & &\vdots&\\
0 & (3 & 1 & 0)\\
0 & (4 & 0 & 0)
\end{bmatrix}\\
\begin{Bmatrix}
1 & 0 & 0 & 3\\
\vdots & &\vdots&\\
1 & 3 & 0 & 0\\
2 & 0 & 0 & 2\\
\vdots & &\vdots&\\
\end{Bmatrix}\end{matrix}$$

Dans la partie entre crochets [], on a simplement une colonne de 0
devant $D_4^3$ (on a laissé les parenthèses exprès pour reconnaître $D_4^3$).
Quant à la partie entre accolades, si on soustrait 1 à toutes les valeurs
de la première colonne, on a exactement $D_3^4$.

Une démonstration plus rigoureuse reste à produire (la propagation
récursive d'une hypothèse d'absurdité semble tendre les bras).

Compte-tenu de l'écriture récursive du calcul du nombre de classes, son écriture
dans un langage à parenthèses va de soi. Je ne résiste pas à l'envie 
d'en copier-coller le code (en *scheme*, donc) :

```clisp
(define card
  (lambda (v q)
    (if (= q 1)
        1
        (if (= v 1)
            q
            (+ (card v (1- q)) (card (1- v) q))))))
```

Pour 5 vendeurs et 4 qualités, on a donc 56 classes de qualité.
Pour 10 vendeurs on passe à 286 classes.

Le code pour compter les combinaisons de classes est dans un répertoire
`classes/`. Il est composé de `classes.scm` qui contient les outils
pour générer ou compter les combinaisons de classes et de `classes_main.scm`
qui permet de lire les fichiers résultats de simulations.

### Combinaisons de classes

Les classes de qualités ne sont pas équiprobables. Par exemple, s'il n'y a
qu'une façon d'obtenir la classe (0 5 0 0) (tous les vendeurs vendent la
qualité 2), il y a plusieurs façons d'obtenir la classe (4 0 0 1) :
soit le vendeur 1 vend la qualité 4 et les autres vendent la qualité 1,
soit c'est le vendeur 2 qui vend la qualité 4 et les autres la qualité 1, ….

On a en tout 5 possibilités d'obtenir la classe (4 0 0 1) contre une seule
d'obtenir (0 5 0 0). On dira qu'il y a 1 combinaison pour la classe (0 5 0 0)
et 5 combinaisons pour la classe (4 0 0 1).

Dans le processus de simulation, les tirages aléatoires rendent équiprobables
chaque combinaison. Par conséquent les classes de qualité ne le sont pas.
On associe donc aux sorties le nombre de combinaisons à chaque classe de qualité.

### Simulations

Voici les paramètres du dernier commit d'aujourd'hui (14 août 2024), à partir
desquels on donne les premières interprétations :

- On fait 10240 simulations, soit 10 fois le nombre de combinaisons de classes.
- On a 5 vendeurs et 250 clients. C'est peu, notamment pour le nombre de
  vendeurs, mais il s'agit avant tout de tester.
- On a 4 qualités possibles, et on discrétisera les seuils et marges en 
  autant de classes.
- Les vendeurs changent de stratégie après 10 déficits successifs.
- On termine la simulation après 100 itérations sans déficit, et on limite
  le nombre de pas de temps à 2100 (2000, mais on refait 100 itérations sans
  changement de stratégie commerciale avant d'afficher les résultats).  
  On a augmenté le nombre d'itérations sans déficit pour s'assurer d'une vraie
  stabilisation du système avant d'écrire les résultats.
- Les pondérations des vendeurs sont plafonnées à 50 et un vendeur qui change
  de stratégie commerciale distribue aléatoirement 2 points de pondération
  aux clients.
- Le coefficient de survie des vendeurs est de 0,5. Celui-ci sert à déterminer
  le seuil maximum, qui est égal à la somme des budgets des clients multiplié
  par le coefficient de survie et divisé par le nombre de vendeurs.
- La marge maximum des vendeurs est de 10 (le coût de fabrication étant égal
  à la qualité, donc entre 1 et 4).
- Le budget des clients est distribué selon la répartition du plafond de revenus
  par ménage et par centiles en 2018. Le budget d'un client est strictement
  supérieur au plus bas coût de fabrication.
- Les valeurs initiales des vendeurs sont tirées aléatoirement. Précédemment,
  ceux-ci étaient initialisés aux valeurs maximales. On est passé à un tirage
  aléatoire pour plusieurs raisons :
  * vérifier dans l'état initial qu'on obtient bien les valeurs théoriques
    d'effectifs de classes,
  * observer les différences entre les résultats en fin de simulation
    et les valeurs moyennes pour faire émerger des phénomènes,
  * gommer la prime à l'état initial qui résulte d'une initialisation toujours
    identique.

### Premiers résultats

Étant toujours en cours de développement de l'outil de simulation, il s'agit
ici simplement d'observations effectuées sur des simulations effectuées avec
des jeux de paramètres allégés. Il est trop tôt pour en tirer des conclusions.

Néanmoins, on a pu observer :

- une tendance à converger plus facilement dans les classes de faibles qualités
  (et donc de faibles coût de fabrication),
- cette même tendance est encore plus forte en ce qui concerne les seuils,
- en revanche, les classes de marges moyennes sont favorisées.
# 21 juin 2024
## État actuel
On a un truc qui tourne et qui donne des résultats, et on explore.

## Fonctionnement
### Vendeurs
On a $\gamma + 1$ (actuellement 5) vendeurs qui vendent des ***trucs***.

Le vendeur $i$ vend des trucs qui ont :

- une certaine **qualité** $q_i$, ($q_i \in \{1,2,3,4\}$);
- un **coût** de production $c(q_i)$ (égal à la qualité : produire un truc
  de qualité 1 coûte 1฿[^1], 2฿ pour la qualité 2, *etc…*).

- Sur un truc, le vendeur $i$ se fait une **marge** $m_i$,
- et du coup il le vend à un certain **prix** : $p_i = m_i + c(q_i)$.

[^1]: Le symbole ฿ est le symbole monétaire du Baht thaïlandais, utilisé en remplacement du symbole du *Balle familier* (qui prend une paire de barres) qui n'est pas dans les polices UTF-8 puisqu'il n'existe pas et c'est bien dommage.


### Pijaca

*Pijaca* (ou Пијаца, prononcer « piyatsa ») signifie *marché* en serbe, dans le
[second sens donné par le Larousse](https://www.larousse.fr/dictionnaires/francais/march%C3%A9/49391),
à savoir « Réunion de commerçants ambulants qui, à jours fixes,
vendent dans un lieu dépendant du domaine public des produits comestibles,
des articles ménagers, vestimentaires, etc. ».

C'est pour ne pas le confondre avec les 7 autres sens français du mot
qu'on utilise le mot Serbe.

Notre pijaca est un marché aux trucs.

Voici les règles du pijaca :

- Tous les vendeurs sont présents.
- Tous les clients sont présents.
- Les vendeurs ont suffisamment de stock de truc pour répondre
  à toutes les sollicitations des clients.
- Si un client ne fait pas affaire, c'est parce qu'aucun vendeur
  n'est en capacité de satisfaire sa demande (le fonctionnement
  des clients est décrit plus loin).
- Lorsqu'un client sollicite un vendeur, si celui-ci est en mesure
  de satisfaire sa demande alors la transaction se fait,
  **au prix du vendeur**.

À l'issue du pijaca, tous les clients qui peuvent être satisfaits
ont fait affaire.


### Vendeurs (suite)

À la fin d'un jour de pijaca, les vendeurs ont vendu
un certain nombre de trucs. Si le vendeur $i$
a vendu $N_i$ trucs, il a fait :

- un **chiffre d'affaires** $Ca_i = N_i * p_i$,
- un **bénéfice** $B_i = N_i.m_i$.

Pour que le vendeur $i$ soit *viable* (c'est-à-dire qu'il peut vivre
de son activité commerciale), il faut que son bénéfice soit supérieur
à un **seuil** $S_i$.

On considère qu'il existe un fond autogéré des vendeurs (le FAVEUR)
pour les vendeurs déficitaires. Le règlement du FAVEUR dit qu'au bout
d'un certain nombre (10) de jours de pijaca successifs de déficit,
le vendeur change sa stratégie commerciale. Quand il en a changé,
il faut le même nombre de jours de déficit successifs pour en changer
à nouveau.

#### Stratégie commerciale

Un changement de stratégie commerciale consiste à changer
une et une seule de ces trois valeurs :

- qualité,
- marge,
- ou seuil.

Le choix de la valeur à changer se fait de façon aléatoire,
avec un tirage équiprobable.

### Clients

Un client $j$ à un **besoin** $n_j$, qui est un nombre de trucs
qu'il souhaite aquérir à chaque jour de marché. Actuellement
$n_j = 1, \forall j$.

Ce même client a un **budget** $b_j$.

Tous les clients peuvent s'acheter au moins un truc
qui serait vendu au coût de fabrication de la qualité
la moins chère. On a donc défini : $\forall j, b_j > c(1)$.

#### Choix du vendeur

À chaque jour de pijaca, les clients choisissent l'ordre dans
lequel ils vont voir les vendeurs. Dès qu'un vendeur est en
mesure de satisfaire le besoin du client en respectant son budget,
l'affaire est faite et le client ne va pas voir d'autre vendeur
ce jour-là.

Le choix de l'ordre des vendeurs qu'un client va voir se base
sur un vecteur d'évaluation des vendeurs par le client,
qu'on appelle **vecteur des pondérations**. On le note
$\overrightarrow{P_j} = (p_{j,0}\;;\;p_{j,1}\;;\;…)$.

Initialisé à $\overrightarrow{0}$, le principe est que lorsque
le client $j$ rencontre le vendeur $i$, alors $p_{j,i}$ est :

- incrémenté si le client et le vendeur font affaire,
- décrémenté sinon.

Le prochain vendeur que le client $j$ va rencontrer est issu
d'un tirage aléatoire pondéré par les valeurs $p_{j,i}$.  
Si $\overrightarrow{P_j} = \overrightarrow{0}$ alors on
effectue un tirage équiprobable.

Si le client $j$ choisit de rencontrer le vendeur $i$ et que
celui-ci ne peut le satisfaire, alors $p_{j,i}$ est
décrémenté, et on effectue un nouveau tirage en considérant le vecteur
$\overrightarrow{P_j}$ dans lequel on ignore la valeur $p_{j,i}$.

Ceci est répété jusqu'à ce que le client fasse affaire
ou jusqu'à ce que tous les vendeurs aient été rencontrés.


##### Exemple

On considère le client 42, avec le vecteur de pondérations 
$\overrightarrow{P_{42}} = (1\;\;3\;\;0)$.

Soit l'ensemble $E_{42} = \emptyset$.

- On a : $\sum_{i \in [0 ; \gamma]}^{i \notin E_{42}} p_{42,i} = 4$.
- On effectue un tirage aléatoire $\alpha \in ]0\;;\;4[$.  
  Supposons qu'on ait $\alpha = 1.35$.
- On cherche $v = min(k \in [0 ; \gamma]\,,\; k \notin E_{42})$ tel que
  $\left(\sum_{i \in [0 ; k]}^{i \notin E_{42}}p_{42,i} ≥ \alpha \right)$  
  (ici $v = 1$ car :
  - $1 < \alpha\;(k = 0)$ 
  - et $1+3 ≥ \alpha\;(k = 1)$ 
  - le plus petit $k$ qui convient est donc 1).
- Le client 42 rencontre donc le vendeur 1.
- Supposons que ce vendeur ne peut satisfaire son besoin. On décrémente donc
  la pondération du vendeur 1, et $\overrightarrow{P_{42}}$ prend la valeur 
  $(1\;\;2\;\;0)$
- Le client va essayer un autre vendeur.  
  On ajoute 1 à $E_{42}$ et on reprend à la première étape :
  - On a $\sum_{i \in [0 ; \gamma]}^{i \notin E_{42}} p_{42,i} = 1$
  - On prendra donc $\alpha \in ]0\;;\;1[$ ce qui nous donne $v = 0$.
  - Supposons que le vendeur 0 ne puisse lui non plus satisfaire
    le client 42.  
    On décrémente donc la pondération du vendeur 0,
    ce qui donne $\overrightarrow{P_{42}} = (0\;\;2\;\;0)$,  
    on ajoute 0 à $E_{42}$,  
    et le client va essayer un autre vendeur.
- Il ne reste que le vendeur 2, qui est sélectionné malgré sa pondération
  égale à 0.
  - Si le vendeur 2 peut satisfaire le client, alors à l'issue de ce jour
    de marché $\overrightarrow{P_{42}}$ aura 
    la valeur $(0\;\;2\;\;1)$,


##### Remarques
###### Pondérations négatives
- Si un vendeur a une pondération de 0 et ne peut faire affaire,
  alors sa pondération est décrémentée. On incrémente ensuite 
  toutes les pondérations pour faire en sorte que toutes les
  pondérations soient positives (ou nulles).  
  Par exemple, si on devait décrémenter le vendeur 2 lorsque
  $\overrightarrow{P_{42}} = (1\;\;3\;\;0)$, on obtiendrait
  le vecteur $(2\;\;4\;\;0)$.

###### Vecteurs de pondérations nulles
- Un client qui a un vecteur de pondérations égal à 
  $\overrightarrow{0}$ et qui ne fait affaire avec aucun vendeur
  aura toujours à l'issue du marché le vecteur de pondérations
  $\overrightarrow{0}$.
- En revanche, si, avec un vecteur de pondérations $\overrightarrow{0}$,
  le client fait affaire, alors, à l'issue du jour de marché :
  - les vendeurs avec lesquel il n'a pas fait affaire ont une pondération de 0,
  - les vendeurs qu'il n'a pas vus ont une pondération de 1,
  - le vendeur avec lequel il a fait affaire a une pondération de 2.
- Dans le cas particulier où le client avec un vecteur de pondérations
  nulles fait affaire avec le premier vendeur qu'il rencontre,
  celui-ci aura une pondération de 1 et les autres vendeurs une pondération
  nulle. Ce client visitera donc le même vendeur en premier lieu
  lors du prochain jour de marché. Tant qu'il fera affaire, il n'ira voir
  aucun autre vendeur.

## Simulations
### Conditions initiales

Pour une simulation, une population de clients (500) et de vendeurs (5)
est générée.

- On fixe une marge maximum $Ma_{max}$ (10฿)
- On prend les données du 
  [revenu disponible en pourcentage cumulé des ménages en france](https://www.insee.fr/fr/statistiques/5371205?sommaire=5371304),
  pour l'année 2018.  
  En voici la courbe :  
  ![Courbe des plafonds de revenus cumulés des ménages en france](img/PlafondRevenus2018.png)  
  On notera que les cinq derniers centiles ne sont pas donnés.
  On fera donc comme si c'était des quatre-vingt-quinziles.
- On calcule un facteur de renormalisation des budgets à partir de
  la dernière valeur $Rev_{max}$ du tableau des déciles :  
  on pose $coef_{budget} = \frac{Ma_{max}+c(q_{max})}{Rev_{max}}$.
  Ainsi on s'assure qu'un budget du dernier quantile pourra payer
  un truc au prix maximum, avec en plus une marge égale à $c(q_1)$.
- Pour chaque client :
  - on fixe son besoin à 1 ($n_j = 1$)
  - on tire aléatoirement un quantile. Soit $v_{quant}$ le budget
    correspondant à ce quantile. On affecte
    $b_j = c(q_1) + (v_{quant} . coef_{budget})$
  - le vecteur des pondérations est initialisé à 0.
- Partant du principe qu'un truc qui arrive sur un marché vierge
  est d'abord très cher, avant que son prix ne baisse,
  on intitialise tous les vendeurs aux qualité, marge et seuil
  maximaux.
  
Une fois tout ceci initialisé, on simule des jours de marché.

### Conditions d'arrêt
La condition d'arrêt est que tous les vendeurs soient viables.
On pourrait stopper le modèle à ce moment précis, mais on a ajouté
une valeur **nb_stop** (égale à 10) pour :

- lisser l'état des vendeurs comme sorties du modèle sur plusieurs
  itérations (sans changement de stratégie) pour estomper l'effet
  aléatoire,
- permettre au dernier vendeur ayant changé sa stratégie d'avoir
  quelques itérations dans cette stratégie (à priori ce vendeur
  n'était pas viable auparavant et n'a pas forcément
  de clientèle vraiment fidélisée)
- s'assurer que l'arrêt n'est pas dû à un tirage accidentel.

Pendant ces quelques itérations les changement de stratégie
n'ont plus lieu.

Au bout d'un certain nombre d'itérations (2000), on arrête la simulation
(après *nb_stop* itérations supplémentaires), considérant qu'on
n'atteindra pas de situation de viabilité pour tous les vendeurs.

### Réplication
Cette simulation peut être répétée plusieurs fois :

- avec la même population initiale (seul le vecteur des pondérations
  est réinitialisé). On parle alors de réplicat.
- En tirant une nouvelle population.

### Sorties du modèle
Les sorties du modèles se font dans un fichier ayant pour chaque ligne
le format suivant :

- 4 caractères : [, une lettre, ] et une espace. Ex : «[b] ».
  Ceux-ci servent identifier la nature de ce que contient la ligne.
  Actuellement, `[i] ` désigne les paramètres d'initialisation du modèle,
  `[t] ` les informations relatives à une simulation,
  `[b] ` le bilan d'une simulation.
- les données, au format csv, en utilisant un caractère de tabulation
  pour délimiter les colonnes. Une première ligne donne les en-têtes
  des colonnes.

Ainsi, on peut extraire au format csv la partie qui nous intéresse.
Par exemple :

```bash
# À l'exécution, on aime bien savoir à quelle simulation on en est,
# et comment se sont passées les simulations précédentes :
OMP_NUM_THREADS=16 ./ccsyl | tee result.txt | grep '^\[t'

# Après l'exécution, on récupère les bilans des simulations :
cat result.txt | grep '^\[b' | cut -c5- > bilans.csv
```

La richesse des résultats possibles fait que les valeurs retournées
par les simulations varient en fonction des éléments
que l'on souhaite voir à un moment donné.

Notons toutefois deux éléments que l'on retrouve
dans tous les jeux de résultats :

- IdSimu : est un identifiant unique permettant d'identifier une exécution
  d'un lot de simulations (désolé pour l'ambiguïté sud le terme "simu").
  Précisément il est le nombre de millisecondes écoulées entre le moment
  où a été lancé ce jeu de simulations et un instant précis lors du
  développement où j'ai lancé une commande pour savoir le nombre
  de millisecondes écoulées entre cet instant et l'*epoch unix*.
- NoSimu : dans un lot de simulations, chaque simulation est numérotée.

Ces deux éléments permettent de faire des jointures entre les différents
jeux de sorties (`[b]`, `[t]`, `[v]` ou `[c]` par exemple — parce qu'à un
moment on écrivait l'état de chaque vendeur et de chaque client à chaque
pas de temps en utilisant `[v]` et `[c]`).

# 8 juillet 2024

Il a été décidé de plafonner la valeur de fidélité. Avoir une fidélité à 500
n'a pas beaucoup de sens, 50 points de fidélité correspondant quasiment
à un écart irattrappable. Il a donc été implémenté un plafond au-delà
duquel une pondération cesse d'être incrémentée.

# 9 juillet 2024
On ajoute deux points d'impact marketing à chaque changement de stratégie.
Autrement dit, un vendeur qui change de stratégie marketing s'attribue un
point de pondération à deux clients tirés aléatoirement. Ces deux points
d'impact paraissent peu, mais influent beaucoup sur la convergence des
simulations.