From 8521f933eb6bcfa7a33861ea3ed7d417fe358358 Mon Sep 17 00:00:00 2001 From: Alessandrini Antoine Date: Sun, 19 Apr 2026 11:11:28 +0200 Subject: [PATCH 1/4] feat(example): minimal loop benchmark with simulink compare --- .../bench_01_minimal_loop/compare.py | 84 +- .../matlab/bench_01_simulink.mat | Bin 0 -> 1600480 bytes .../matlab/simulink_case.mlx | Bin 0 -> 3573 bytes .../matlab/simulink_case.slx | Bin 0 -> 69846 bytes .../matlab/simulink_results.csv | 100002 +++++++++++++++ .../bench_01_minimal_loop/noise_seq.npy | Bin 944 -> 800144 bytes .../bench_01_minimal_loop/params.py | 2 +- .../bench_01_minimal_loop/pysimblocks_case.py | 1 + .../reference_results.npz | Bin 0 -> 4802062 bytes 9 files changed, 100064 insertions(+), 25 deletions(-) create mode 100644 examples/benchmarks/bench_01_minimal_loop/matlab/bench_01_simulink.mat create mode 100644 examples/benchmarks/bench_01_minimal_loop/matlab/simulink_case.mlx create mode 100644 examples/benchmarks/bench_01_minimal_loop/matlab/simulink_case.slx create mode 100644 examples/benchmarks/bench_01_minimal_loop/matlab/simulink_results.csv create mode 100644 examples/benchmarks/bench_01_minimal_loop/reference_results.npz diff --git a/examples/benchmarks/bench_01_minimal_loop/compare.py b/examples/benchmarks/bench_01_minimal_loop/compare.py index d79045a..a199180 100644 --- a/examples/benchmarks/bench_01_minimal_loop/compare.py +++ b/examples/benchmarks/bench_01_minimal_loop/compare.py @@ -1,3 +1,13 @@ +""" +Comparaison des résultats de simulation entre différentes librairies (bdsim, PathSim, pySimBlocks, Simulink). +- Charge les résultats de chaque librairie (temps, signal de bruit, signal de sortie). +- Calcule les erreurs numériques entre les librairies (norme de la différence). +- Affiche les résultats dans la console (temps, erreurs). +- Trace les signaux de bruit et de sortie pour chaque librairie sur un même graphique. +- Affiche un histogramme comparant les temps de simulation des différentes librairies. + +Rq: (Bdsim est commenté car trop lent) +""" import logging import numpy as np @@ -13,62 +23,88 @@ if __name__ == "__main__": - N_RUNS = 1 + N_RUNS = 100 logger.info(f"Running benchmark ({N_RUNS} runs each)...") results_bd, results_ps, results_pb = [], [], [] for i in range(N_RUNS): print(f"Run {i+1}/{N_RUNS}...", end="\r") - results_bd.append(test_bdsim_case()) + # results_bd.append(test_bdsim_case()) results_ps.append(test_pathsim_case()) results_pb.append(test_pysimblocks_case()) - times_bd = np.array([r[3] for r in results_bd]) + # times_bd = np.array([r[3] for r in results_bd]) times_ps = np.array([r[3] for r in results_ps]) times_pb = np.array([r[3] for r in results_pb]) - t_bd, noise_bd, output_bd, _ = results_bd[-1] + sl = np.loadtxt("matlab/simulink_results.csv", delimiter=",", skiprows=1) + t_sl = sl[:, 0] + noise_sl = sl[:, 1] + output_sl = sl[:, 2] + t_mean_sl = 143.3 # ms + t_std_sl = 6.7 # ms + + # t_bd, noise_bd, output_bd, _ = results_bd[-1] t_ps, noise_ps, output_ps, _ = results_ps[-1] t_pb, noise_pb, output_pb, _ = results_pb[-1] - n = min(len(t_ps), len(t_pb), len(t_bd)) - t_bd, noise_bd, output_bd = t_bd[:n], noise_bd[:n], output_bd[:n] + n = min(len(t_ps), len(t_pb), len(t_sl)) + # t_bd, noise_bd, output_bd = t_bd[:n], noise_bd[:n], output_bd[:n] t_ps, noise_ps, output_ps = t_ps[:n], noise_ps[:n], output_ps[:n] t_pb, noise_pb, output_pb = t_pb[:n], noise_pb[:n], output_pb[:n] + t_sl, noise_sl, output_sl = t_sl[:n], noise_sl[:n], output_sl[:n] - t_error_bd_ps = np.linalg.norm(t_bd - t_ps) - noise_error_bd_ps = np.linalg.norm(noise_bd - noise_ps) - output_error_bd_ps = np.linalg.norm(output_bd - output_ps) - t_error_bd_pb = np.linalg.norm(t_bd - t_pb) - noise_error_bd_pb = np.linalg.norm(noise_bd - noise_pb) - output_error_bd_pb = np.linalg.norm(output_bd - output_pb) + # t_error_bd_pb = np.linalg.norm(t_bd - t_pb) + # noise_error_bd_pb = np.linalg.norm(noise_bd - noise_pb) + # output_error_bd_pb = np.linalg.norm(output_bd - output_pb) t_error_ps_pb = np.linalg.norm(t_ps - t_pb) noise_error_ps_pb = np.linalg.norm(noise_ps - noise_pb) output_error_ps_pb = np.linalg.norm(output_ps - output_pb) + t_error_sl_pb = np.linalg.norm(t_sl - t_pb) + noise_error_sl_pb = np.linalg.norm(noise_sl - noise_pb) + output_error_sl_pb = np.linalg.norm(output_sl - output_pb) + + # --- Sauvegarder les résultats de référence --- + np.savez("reference_results.npz", + t = t_pb, + noise = noise_pb, + output_psb = output_pb, + output_ps = output_ps, + # output_bd = output_bd, + output_sl = output_sl, + # times_bd_ms = times_bd * 1e3, + times_ps_ms = times_ps * 1e3, + times_pb_ms = times_pb * 1e3, + t_sl = t_sl, + ) logger.info("") logger.info("=== Timing ===") - logger.info(f" bdsim: median={np.median(times_bd)*1e3:.1f} ms, std={np.std(times_bd)*1e3:.1f} ms") + # logger.info(f" bdsim: median={np.median(times_bd)*1e3:.1f} ms, std={np.std(times_bd)*1e3:.1f} ms") logger.info(f" PathSim: median={np.median(times_ps)*1e3:.1f} ms, std={np.std(times_ps)*1e3:.1f} ms") logger.info(f" pySimBlocks: median={np.median(times_pb)*1e3:.1f} ms, std={np.std(times_pb)*1e3:.1f} ms") + logger.info(f" Simulink: median={t_mean_sl:.1f} ms, std={t_std_sl:.1f} ms") logger.info(f" Speedup pySimBlocks vs PathSim: {np.median(times_ps) / np.median(times_pb):.2f}x") - logger.info(f" Speedup pySimBlocks vs bdsim: {np.median(times_bd) / np.median(times_pb):.2f}x") + # logger.info(f" Speedup pySimBlocks vs bdsim: {np.median(times_bd) / np.median(times_pb):.2f}x") + logger.info(f" Speedup pySimBlocks vs Simulink: {t_mean_sl*1e-3 / np.median(times_pb):.2f}x") logger.info("") logger.info("=== Numerical errors (last run) ===") logger.info("Librairies | Time error (a/b%) | Noise error (a/b%) | Output error (a/b%)") - logger.info(f"bdsim vs PathSim | {t_error_bd_ps:.2e} ({t_error_bd_ps / np.linalg.norm(t_ps) * 100:.2f}%) | {noise_error_bd_ps:.2e} ({noise_error_bd_ps / np.linalg.norm(noise_ps) * 100:.2f}%) | {output_error_bd_ps:.2e} ({output_error_bd_ps / np.linalg.norm(output_ps) * 100:.2f}%)") - logger.info(f"bdsim vs pySimBlocks | {t_error_bd_pb:.2e} ({t_error_bd_pb / np.linalg.norm(t_pb) * 100:.2f}%) | {noise_error_bd_pb:.2e} ({noise_error_bd_pb / np.linalg.norm(noise_pb) * 100:.2f}%) | {output_error_bd_pb:.2e} ({output_error_bd_pb / np.linalg.norm(output_pb) * 100:.2f}%)") + # logger.info(f"bdsim vs pySimBlocks | {t_error_bd_pb:.2e} ({t_error_bd_pb / np.linalg.norm(t_pb) * 100:.2f}%) | {noise_error_bd_pb:.2e} ({noise_error_bd_pb / np.linalg.norm(noise_pb) * 100:.2f}%) | {output_error_bd_pb:.2e} ({output_error_bd_pb / np.linalg.norm(output_pb) * 100:.2f}%)") logger.info(f"PathSim vs pySimBlocks | {t_error_ps_pb:.2e} ({t_error_ps_pb / np.linalg.norm(t_pb) * 100:.2f}%) | {noise_error_ps_pb:.2e} ({noise_error_ps_pb / np.linalg.norm(noise_pb) * 100:.2f}%) | {output_error_ps_pb:.2e} ({output_error_ps_pb / np.linalg.norm(output_pb) * 100:.2f}%)") + logger.info(f"Simulink vs pySimBlocks | {t_error_sl_pb:.2e} ({t_error_sl_pb / np.linalg.norm(t_pb) * 100:.2f}%) | {noise_error_sl_pb:.2e} ({noise_error_sl_pb / np.linalg.norm(noise_pb) * 100:.2f}%) | {output_error_sl_pb:.2e} ({output_error_sl_pb / np.linalg.norm(output_pb) * 100:.2f}%)") plt.figure(figsize=(10, 6)) - plt.plot(t_bd, noise_bd, "-.r", label="Noise (bdsim)", alpha=0.7) - plt.plot(t_bd, output_bd, "-.b", label="Output (bdsim)", alpha=0.7) + # plt.plot(t_bd, noise_bd, "-.r", label="Noise (bdsim)", alpha=0.7) + # plt.plot(t_bd, output_bd, "-.b", label="Output (bdsim)", alpha=0.7) plt.plot(t_ps, noise_ps, "--r", label="Noise (PathSim)", alpha=0.7) plt.plot(t_ps, output_ps, "--b", label="Output (PathSim)", alpha=0.7) plt.plot(t_pb, noise_pb, ":r", label="Noise (pySimBlocks)", alpha=0.7) plt.plot(t_pb, output_pb, ":b", label="Output (pySimBlocks)", alpha=0.7) + plt.plot(t_sl, noise_sl, "-.m", label="Noise (Simulink)", alpha=0.7) + plt.plot(t_sl, output_sl, "-.c", label="Output (Simulink)", alpha=0.7) plt.title("Simulation Results Comparison") plt.xlabel("Time (s)") plt.ylabel("Amplitude") @@ -79,25 +115,25 @@ # --- Histogramme des temps --- benchmarks = { 'bench_01': { - 'bdsim': np.median(times_bd) * 1e3, + # 'bdsim': np.median(times_bd) * 1e3, 'PathSim': np.median(times_ps) * 1e3, 'pySimBlocks': np.median(times_pb) * 1e3, + 'Simulink': t_mean_sl, }, - # 'bench_02': { 'bdsim': ..., 'PathSim': ..., 'pySimBlocks': ... }, - # 'bench_03': { 'bdsim': ..., 'PathSim': ..., 'pySimBlocks': ... }, } - libs = ['bdsim', 'PathSim', 'pySimBlocks'] - colors = {'bdsim': '#7F77DD', 'PathSim': '#1D9E75', 'pySimBlocks': '#D85A30'} + libs = ['PathSim', 'pySimBlocks', 'Simulink'] + colors = {'PathSim': '#1D9E75', 'pySimBlocks': '#D85A30', 'Simulink': '#E1B000'} n_bench = len(benchmarks) - width = 0.25 + width = 0.2 group_gap = 1.0 # espace entre groupes fig, ax = plt.subplots(figsize=(4 + 2 * n_bench, 5)) for g, (bench_name, values) in enumerate(benchmarks.items()): group_center = g * group_gap + # offsets = [-1.5*width, -0.5*width, 0.5*width, 1.5*width] offsets = [-width, 0, width] for lib, offset in zip(libs, offsets): val = values[lib] diff --git a/examples/benchmarks/bench_01_minimal_loop/matlab/bench_01_simulink.mat b/examples/benchmarks/bench_01_minimal_loop/matlab/bench_01_simulink.mat new file mode 100644 index 0000000000000000000000000000000000000000..8acd3f0787ea0804f51399f8f2034d0ddbfc5675 GIT binary patch literal 1600480 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