Refactor report formatting and improve clarity in observations

- Updated section headings for consistency and clarity.
- Converted tables from markdown format to a structured table format for better readability.
- Enhanced descriptions in the analysis and conclusion sections to provide clearer insights into the performance of LEACH and LEACH-C protocols.
- Corrected minor typographical errors for improved professionalism.
This commit is contained in:
paul.roost 2025-11-03 14:12:51 +01:00
parent ab902bad5f
commit fff067809f
3 changed files with 8179 additions and 6914 deletions

View File

@ -1,50 +1,32 @@
""" """
Analyseur et générateur de graphiques pour les résultats de simulation Génération de graphiques et analyses visuelles pour les résultats de simulation LEACH/LEACH-C.
Lit les métriques JSON et produit des PNG comparatifs.
""" """
import json import json
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import matplotlib import matplotlib
matplotlib.use('Agg') # Backend sans affichage matplotlib.use('Agg') # Pas d'affichage graphique (serveur)
class ResultsAnalyzer: class ResultsAnalyzer:
"""Analyse et visualise les résultats des simulations.""" """Crée les graphiques de comparaison entre les protocoles et scénarios."""
def __init__(self, results_file): def __init__(self, results_file):
""" """Charge le fichier JSON contenant les résultats de toutes les simulations."""
Charge les résultats depuis un fichier JSON.
Args:
results_file (str): Chemin vers le fichier JSON
"""
with open(results_file, 'r') as f: with open(results_file, 'r') as f:
self.results = json.load(f) self.results = json.load(f)
def generate_comparison_graphs(self, output_dir): def generate_comparison_graphs(self, output_dir):
""" """Crée tous les graphiques PNG en une seule passe."""
Génère les graphiques de comparaison LEACH vs LEACH-C.
Args:
output_dir (str): Répertoire de sortie
"""
# 1. FDN Comparison
self._plot_fdn_comparison(output_dir) self._plot_fdn_comparison(output_dir)
# 2. FMR Comparison
self._plot_fmr_comparison(output_dir) self._plot_fmr_comparison(output_dir)
# 3. DLBI Comparison
self._plot_dlbi_comparison(output_dir) self._plot_dlbi_comparison(output_dir)
# 4. RSPI Comparison
self._plot_rspi_comparison(output_dir) self._plot_rspi_comparison(output_dir)
# 5. Alive Nodes Over Rounds
self._plot_alive_nodes(output_dir) self._plot_alive_nodes(output_dir)
def _plot_fdn_comparison(self, output_dir): def _plot_fdn_comparison(self, output_dir):
"""Graphique : FDN pour tous les scénarios.""" """Graphique en barres : Premier Nœud Mort pour chaque scénario."""
scenarios = list(self.results.keys()) scenarios = list(self.results.keys())
leach_fdn = [] leach_fdn = []
leachc_fdn = [] leachc_fdn = []
@ -53,7 +35,6 @@ class ResultsAnalyzer:
leach_metrics = self.results[scenario]["LEACH"]["metrics"] leach_metrics = self.results[scenario]["LEACH"]["metrics"]
leachc_metrics = self.results[scenario]["LEACH-C"]["metrics"] leachc_metrics = self.results[scenario]["LEACH-C"]["metrics"]
# Utiliser fdn du JSON (peut être null)
leach_fdn.append(leach_metrics.get("fdn") or 0) leach_fdn.append(leach_metrics.get("fdn") or 0)
leachc_fdn.append(leachc_metrics.get("fdn") or 0) leachc_fdn.append(leachc_metrics.get("fdn") or 0)
@ -72,7 +53,7 @@ class ResultsAnalyzer:
ax.legend() ax.legend()
ax.grid(axis='y', alpha=0.3) ax.grid(axis='y', alpha=0.3)
# Ajouter note si pas de FDN (nœuds toujours vivants) # Note si tous les nœuds survivent (valeur nulle = pas de mort)
if all(f == 0 for f in leach_fdn + leachc_fdn): if all(f == 0 for f in leach_fdn + leachc_fdn):
ax.text(0.5, 0.5, 'Pas de nœuds morts\n(tous les nœuds vivants)', ax.text(0.5, 0.5, 'Pas de nœuds morts\n(tous les nœuds vivants)',
transform=ax.transAxes, ha='center', va='center', transform=ax.transAxes, ha='center', va='center',
@ -83,7 +64,7 @@ class ResultsAnalyzer:
plt.close() plt.close()
def _plot_fmr_comparison(self, output_dir): def _plot_fmr_comparison(self, output_dir):
"""Graphique : FMR pour tous les scénarios.""" """Graphique en barres : Premier Round Muet pour chaque scénario."""
scenarios = list(self.results.keys()) scenarios = list(self.results.keys())
leach_fmr = [] leach_fmr = []
leachc_fmr = [] leachc_fmr = []
@ -92,7 +73,6 @@ class ResultsAnalyzer:
leach_metrics = self.results[scenario]["LEACH"]["metrics"] leach_metrics = self.results[scenario]["LEACH"]["metrics"]
leachc_metrics = self.results[scenario]["LEACH-C"]["metrics"] leachc_metrics = self.results[scenario]["LEACH-C"]["metrics"]
# Utiliser fmr du JSON (peut être null)
leach_fmr.append(leach_metrics.get("fmr") or 0) leach_fmr.append(leach_metrics.get("fmr") or 0)
leachc_fmr.append(leachc_metrics.get("fmr") or 0) leachc_fmr.append(leachc_metrics.get("fmr") or 0)
@ -116,7 +96,7 @@ class ResultsAnalyzer:
plt.close() plt.close()
def _plot_dlbi_comparison(self, output_dir): def _plot_dlbi_comparison(self, output_dir):
"""Graphique : DLBI pour tous les scénarios.""" """Graphique en barres : Indice d'Équilibre de Charge pour chaque scénario."""
scenarios = list(self.results.keys()) scenarios = list(self.results.keys())
leach_dlbi = [] leach_dlbi = []
leachc_dlbi = [] leachc_dlbi = []
@ -149,7 +129,7 @@ class ResultsAnalyzer:
plt.close() plt.close()
def _plot_rspi_comparison(self, output_dir): def _plot_rspi_comparison(self, output_dir):
"""Graphique : RSPI pour tous les scénarios.""" """Graphique tableau : Indice de Stabilité du Réseau avec explications."""
scenarios = list(self.results.keys()) scenarios = list(self.results.keys())
leach_rspi = [] leach_rspi = []
leachc_rspi = [] leachc_rspi = []
@ -161,16 +141,14 @@ class ResultsAnalyzer:
leach_rspi.append(leach_metrics.get("rspi", 0)) leach_rspi.append(leach_metrics.get("rspi", 0))
leachc_rspi.append(leachc_metrics.get("rspi", 0)) leachc_rspi.append(leachc_metrics.get("rspi", 0))
# Graphique : Tableau de texte uniquement (plus lisible)
fig, ax = plt.subplots(figsize=(12, 8)) fig, ax = plt.subplots(figsize=(12, 8))
ax.axis('off') ax.axis('off')
# Créer un tableau affichant les valeurs # Tableau affichant RSPI pour tous les scénarios
data = [] data = []
for scenario, lrspi, crspi in zip(scenarios, leach_rspi, leachc_rspi): for scenario, lrspi, crspi in zip(scenarios, leach_rspi, leachc_rspi):
data.append([scenario, f"{lrspi:.6f}", f"{crspi:.6f}"]) data.append([scenario, f"{lrspi:.6f}", f"{crspi:.6f}"])
# Ajouter en-tête
data.insert(0, ['Scénario', 'LEACH RSPI', 'LEACH-C RSPI']) data.insert(0, ['Scénario', 'LEACH RSPI', 'LEACH-C RSPI'])
table = ax.table(cellText=data, cellLoc='center', loc='center', table = ax.table(cellText=data, cellLoc='center', loc='center',
@ -179,12 +157,12 @@ class ResultsAnalyzer:
table.set_fontsize(11) table.set_fontsize(11)
table.scale(1, 3) table.scale(1, 3)
# Colorer l'en-tête # Style en-tête
for i in range(3): for i in range(3):
table[(0, i)].set_facecolor('#4ECDC4') table[(0, i)].set_facecolor('#4ECDC4')
table[(0, i)].set_text_props(weight='bold', color='white', size=12) table[(0, i)].set_text_props(weight='bold', color='white', size=12)
# Colorer les lignes alternées et ajouter bordures # Style lignes alternées
for i in range(1, len(data)): for i in range(1, len(data)):
for j in range(3): for j in range(3):
if i % 2 == 0: if i % 2 == 0:
@ -193,11 +171,9 @@ class ResultsAnalyzer:
table[(i, j)].set_facecolor('white') table[(i, j)].set_facecolor('white')
table[(i, j)].set_text_props(size=11) table[(i, j)].set_text_props(size=11)
# Titre
fig.suptitle('Comparaison RSPI (Relative Silence Period Index)', fig.suptitle('Comparaison RSPI (Relative Silence Period Index)',
fontsize=16, fontweight='bold', y=0.98) fontsize=16, fontweight='bold', y=0.98)
# Ajouter explications
explanation = ( explanation = (
'RSPI = 0.0000 indique que le réseau reste STABLE pendant toute la simulation.\n' 'RSPI = 0.0000 indique que le réseau reste STABLE pendant toute la simulation.\n'
'Les nœuds survivent jusqu\'à la fin, ce qui est un EXCELLENT résultat.\n\n' 'Les nœuds survivent jusqu\'à la fin, ce qui est un EXCELLENT résultat.\n\n'
@ -213,16 +189,14 @@ class ResultsAnalyzer:
plt.close() plt.close()
def _plot_alive_nodes(self, output_dir): def _plot_alive_nodes(self, output_dir):
""" """Graphique récapitulatif : Tous les résultats (FDN, FMR, DLBI, RSPI) par scénario."""
Graphique : Résumé complet des résultats en format très lisible.
"""
scenarios = list(self.results.keys()) scenarios = list(self.results.keys())
fig = plt.figure(figsize=(16, 12)) fig = plt.figure(figsize=(16, 12))
ax = fig.add_subplot(111) ax = fig.add_subplot(111)
ax.axis('off') ax.axis('off')
# Préparer les données # Préparer les données : FDN, FMR, DLBI, RSPI pour chaque protocole
data = [] data = []
for scenario in scenarios: for scenario in scenarios:
scenario_short = scenario.replace('Scenario_', '').replace('_', ' ') scenario_short = scenario.replace('Scenario_', '').replace('_', ' ')
@ -243,9 +217,7 @@ class ResultsAnalyzer:
data.append(row) data.append(row)
# Ajouter en-tête data.insert(0, ["Scénario", "LEACH", "LEACH-C"])
header = ["Scénario", "LEACH", "LEACH-C"]
data.insert(0, header)
# Créer le tableau # Créer le tableau
table = ax.table(cellText=data, cellLoc='center', loc='center', table = ax.table(cellText=data, cellLoc='center', loc='center',
@ -254,14 +226,14 @@ class ResultsAnalyzer:
table.set_fontsize(11) table.set_fontsize(11)
table.scale(1, 3.5) table.scale(1, 3.5)
# Style l'en-tête # Style en-tête
for i in range(3): for i in range(3):
cell = table[(0, i)] cell = table[(0, i)]
cell.set_facecolor('#4ECDC4') cell.set_facecolor('#4ECDC4')
cell.set_text_props(weight='bold', color='white', size=13) cell.set_text_props(weight='bold', color='white', size=13)
cell.set_height(0.12) cell.set_height(0.12)
# Style les lignes alternées # Style lignes alternées et bordures
for i in range(1, len(data)): for i in range(1, len(data)):
for j in range(3): for j in range(3):
cell = table[(i, j)] cell = table[(i, j)]
@ -275,16 +247,13 @@ class ResultsAnalyzer:
cell.set_linewidth(1.5) cell.set_linewidth(1.5)
cell.set_edgecolor('#cccccc') cell.set_edgecolor('#cccccc')
# Ajouter bordures plus visibles
for key, cell in table.get_celld().items(): for key, cell in table.get_celld().items():
cell.set_linewidth(2) cell.set_linewidth(2)
cell.set_edgecolor('#333333') cell.set_edgecolor('#333333')
# Titre
fig.suptitle('Résumé Complet des Résultats - Simulation avec SimPy', fig.suptitle('Résumé Complet des Résultats - Simulation avec SimPy',
fontsize=16, fontweight='bold', y=0.98) fontsize=16, fontweight='bold', y=0.98)
# Légende
legend_text = ( legend_text = (
'FDN: First Dead Node | FMR: First Muted Round | DLBI: Load Balancing (>0.7=excellent) | RSPI: Silence Index (0=excellent)' 'FDN: First Dead Node | FMR: First Muted Round | DLBI: Load Balancing (>0.7=excellent) | RSPI: Silence Index (0=excellent)'
) )
@ -298,17 +267,10 @@ class ResultsAnalyzer:
plt.close() plt.close()
def generate_summary_table(self, output_file): def generate_summary_table(self, output_file):
""" """Exporte les résultats dans un fichier CSV pour analyse externe."""
Génère un tableau récapitulatif en CSV.
Args:
output_file (str): Chemin du fichier CSV
"""
with open(output_file, 'w') as f: with open(output_file, 'w') as f:
# En-tête
f.write("Scenario,Protocol,FDN,FMR,Alive_Nodes,DLBI,RSPI\n") f.write("Scenario,Protocol,FDN,FMR,Alive_Nodes,DLBI,RSPI\n")
# Données
for scenario_name, scenario_data in self.results.items(): for scenario_name, scenario_data in self.results.items():
for protocol in ["LEACH", "LEACH-C"]: for protocol in ["LEACH", "LEACH-C"]:
metrics = scenario_data[protocol]["metrics"] metrics = scenario_data[protocol]["metrics"]
@ -326,9 +288,8 @@ class ResultsAnalyzer:
if __name__ == "__main__": if __name__ == "__main__":
import os import os
# Déterminer les chemins dynamiquement script_dir = os.path.dirname(os.path.abspath(__file__))
script_dir = os.path.dirname(os.path.abspath(__file__)) # /home/paul/AlgoRep/code project_dir = os.path.dirname(script_dir)
project_dir = os.path.dirname(script_dir) # /home/paul/AlgoRep
results_dir = os.path.join(project_dir, "results") results_dir = os.path.join(project_dir, "results")
results_file = os.path.join(results_dir, "simulation_results.json") results_file = os.path.join(results_dir, "simulation_results.json")

File diff suppressed because it is too large Load Diff

View File

@ -310,7 +310,7 @@ $ "RSPI" = frac(2 times [(1 - "FR"_"muted"/"R"_"max") times (1 - "LR"_"dead"/"R"
== Observations Clés == Observations Clés
=== 1. Durée de Vie Réseau (FDN) === Durée de Vie Réseau (FDN)
Tous les nœuds *survivent 3000 rounds* dans tous les scénarios. Cela démontre que : Tous les nœuds *survivent 3000 rounds* dans tous les scénarios. Cela démontre que :
- L'énergie initiale (0.5J par nœud) est *suffisante* pour 3000 rounds - L'énergie initiale (0.5J par nœud) est *suffisante* pour 3000 rounds
@ -319,47 +319,56 @@ Tous les nœuds *survivent 3000 rounds* dans tous les scénarios. Cela démontre
*Implication* : FDN=null est un résultat positif le réseau maintient sa viabilité long terme. *Implication* : FDN=null est un résultat positif le réseau maintient sa viabilité long terme.
=== 2. Premières Pertes (FMR) === Premières Pertes (FMR)
| Scénario | LEACH FMR | LEACH-C FMR | Interprétation | #table(
|----------|-----------|-------------|---| columns: (1fr, auto, auto, 2fr),
| 1 (l=2000, p=0.05, n=100) | 195 | null | LEACH commence à perdre des CHs élus | align: (left, center, center, left),
| 2 (l=2000, p=0.50, n=100) | 1603 | null | LEACH perd CHs au round 1603 (53%) | [*Scénario*], [*LEACH FMR*], [*LEACH-C FMR*], [*Interprétation*],
| 3 (l=2000, p=0.95, n=100) | null | null | Aucun CH ne mute réseau plein débit | [1 (l=2000, p=0.05, n=100)], [195], [null], [LEACH commence à perdre des CHs élus],
| 4 (l=4000, p=0.05, n=100) | 99 | null | Gros paquets causent pertes précoces | [2 (l=2000, p=0.50, n=100)], [1603], [null], [LEACH perd CHs au round 1603 (53%)],
| 5 (l=4000, p=0.05, n=200) | null | null | Réseau 200 nœuds très stable | [3 (l=2000, p=0.95, n=100)], [null], [null], [Aucun CH ne mute réseau plein débit],
| 6 (l=4000, p=0.10, n=200) | null | null | Activité modérée stabilité | [4 (l=4000, p=0.05, n=100)], [99], [null], [Gros paquets causent pertes précoces],
[5 (l=4000, p=0.05, n=200)], [null], [null], [Réseau 200 nœuds très stable],
[6 (l=4000, p=0.10, n=200)], [null], [null], [Activité modérée stabilité],
)
*Analyse* : LEACH-C souffre moins de FMR (souvent null), montrant que la *sélection centralisée des CHs est plus stable* et réduit les rotations de CH inutiles. Les FMR de LEACH sont déclenchées par des élections probabilistes défaillantes. *Analyse* : LEACH-C souffre moins de FMR (souvent null), montrant que la *sélection centralisée des CHs est plus stable* et réduit les rotations de CH inutiles. Les FMR de LEACH sont déclenchées par des élections probabilistes défaillantes.
=== 3. Équilibre de Charge (DLBI) === Équilibre de Charge (DLBI)
| Scénario | LEACH DLBI | LEACH-C DLBI | Verdict | #table(
|----------|-----------|-------------|---| columns: (auto, auto, auto, 2fr),
| 1 | 0.747 | 0.070 | LEACH bien supérieur | align: (left, center, center, left),
| 2 | 0.801 | 0.035 | LEACH excellent, LEACH-C mauvais | [*Scénario*], [*LEACH DLBI*], [*LEACH-C DLBI*], [*Verdict*],
| 3 | 0.953 | 0.111 | LEACH extraordinaire, LEACH-C chaotique | [1], [0.747], [0.070], [LEACH bien supérieur],
| 4 | 0.755 | 0.128 | LEACH bien supérieur | [2], [0.801], [0.035], [LEACH excellent, LEACH-C mauvais],
| 5 | 0.681 | 0.088 | LEACH bon, LEACH-C déséquilibré | [3], [0.953], [0.111], [LEACH extraordinaire, LEACH-C chaotique],
| 6 | 0.651 | 0.102 | LEACH bon, LEACH-C mauvais | [4], [0.755], [0.128], [LEACH bien supérieur],
[5], [0.681], [0.088], [LEACH bon, LEACH-C déséquilibré],
[6], [0.651], [0.102], [LEACH bon, LEACH-C mauvais],
)
*Conclusion* : LEACH surpasse LEACH-C sur *tous* les scénarios en équilibre de charge. Moyenne LEACH = 0.775, Moyenne LEACH-C = 0.026. *Conclusion* : LEACH surpasse LEACH-C sur *tous* les scénarios en équilibre de charge. Moyenne LEACH = 0.775, Moyenne LEACH-C = 0.026.
*Explication* : LEACH distribue naturellement les responsabilités de CH à tous les nœuds (chaque nœud a 1/n chance d'être élu). LEACH-C centralise au BS qui choisit les 10% meilleurs CHs, mais cela crée une charge accumulée sur les CHs sélectionnés, d'où DLBI négatif. Le DLBI négatif indique que certains nœuds portent une charge disproportionnée. *Explication* : LEACH distribue naturellement les responsabilités de CH à tous les nœuds (chaque nœud a 1/n chance d'être élu). LEACH-C centralise au BS qui choisit les 10% meilleurs CHs, mais cela crée une charge accumulée sur les CHs sélectionnés, d'où DLBI négatif. Le DLBI négatif indique que certains nœuds portent une charge disproportionnée.
=== 4. Stabilité Réseau (RSPI) === Stabilité Réseau (RSPI)
| Tous les Scénarios | RSPI LEACH | RSPI LEACH-C | #table(
|-----------------|-----------|-------------| columns: (2fr, auto, auto),
| Moyenne | 0.0000 | 0.0000 | align: (left, center, center),
| Min/Max | 0 / 0 | 0 / 0 | [*Tous les Scénarios*], [*RSPI LEACH*], [*RSPI LEACH-C*],
[Moyenne], [0.0000], [0.0000],
[Min/Max], [0 / 0], [0 / 0],
)
*Observation remarquable* : Stabilité *parfaite* (RSPI=0) dans tous les scénarios et protocoles. Cela signifie : *Observation remarquable* : Stabilité *parfaite* (RSPI=0) dans tous les scénarios et protocoles. Cela signifie :
- Aucune oscillation d'énergie - Aucune oscillation d'énergie
- Aucune perturbation dans l'accessibilité - Aucune perturbation dans l'accessibilité
- Le réseau maintient un équilibre énergétique constant - Le réseau maintient un équilibre énergétique constant
*Implication* : La simulation SimPy avec reconnnexion dynamique crée un réseau extraordinairement stable. *Implication* : La simulation SimPy avec reconnexion dynamique crée un réseau extraordinairement stable.
== Comparaison des Protocoles == Comparaison des Protocoles