Files
ldpc/src/benchmark.rs
2026-05-11 12:08:06 +02:00

291 lines
9.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::channel::{AwgnChannel, Channel};
use crate::code::{CodeTopology, GenerationMethod, LdpcCode, LdpcParams};
use crate::decoder::{build_decoder, DecoderConfig, DecoderMethod};
use crate::encoder::{build_encoder, EncodingMethod};
use crate::Result as LdpcResult;
use rand::Rng;
use rayon::prelude::*;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::time::Instant;
pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
println!("[*] Étape 1 : Construction Mathématique et Graphe de Tanner");
println!(
" - Dimensions : n={}, k={}, m={} (Taux R={:.3})",
code.n(),
code.k(),
code.m(),
code.rate()
);
let methode_nom = match code.params.generation {
GenerationMethod::MacKayNeal { .. } => "MacKay-Neal",
GenerationMethod::Gallager => "Gallager",
};
println!(" - Topologie : Régulier via {}", methode_nom);
println!(" - Densité H : {:.2}%", code.h.density() * 100.0);
println!(" - Girth : {}", code.girth());
println!(
" - Cycles-4 : {}",
if code.graph.has_4_cycle() {
"Présents (Problématique)"
} else {
"Aucun (Optimal)"
}
);
println!("\n[*] Étape 2 : Instanciation de l'Encodeur (SIMD Bit-Packing)");
let start_enc = Instant::now();
let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?;
println!(" - Encodeur prêt en {:.2?}", start_enc.elapsed());
println!("\n[*] Étape 3 : Instanciation des Décodeurs sur Graphe");
let config = DecoderConfig {
max_iterations: 50,
early_stopping: true,
};
let dec_sp = build_decoder(&code, DecoderMethod::SumProduct, config.clone());
let dec_ms = build_decoder(
&code,
DecoderMethod::MinSum {
scaling_factor: 0.8,
},
config.clone(),
);
let dec_bf = build_decoder(&code, DecoderMethod::BitFlipping, config.clone());
println!(" - Moteurs prêts : Sum-Product, Min-Sum (α=0.8), Bit-Flipping");
let snr_range = [1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 3.0, 3.5, 4.0];
let n_trials = 1000;
println!(
"\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR, Multi-threadé)",
n_trials
);
println!("{:-<115}", "");
println!(
"{:>8} | {:>9} || {:>10} | {:>10} || {:>10} | {:>10} || {:>10} | {:>10} |",
"SNR (dB)",
"Capacité",
"FER (SP)",
"BER (SP)",
"FER (MS)",
"BER (MS)",
"FER (BF)",
"BER (BF)"
);
println!("{:-<115}", "");
for &snr in &snr_range {
let channel = AwgnChannel::new(snr, code.rate())?;
let results: Vec<_> = (0..n_trials)
.into_par_iter()
.map(|_| {
let mut rng = rand::thread_rng();
let message: Vec<u8> = (0..code.k()).map(|_| rng.gen::<u8>() & 1).collect();
let codeword = encoder.encode(&message).unwrap();
let received_llr = channel.transmit(&codeword, &mut rng);
let mut t_sp_f = 0;
let mut t_sp_b = 0;
let mut t_ms_f = 0;
let mut t_ms_b = 0;
let mut t_bf_f = 0;
let mut t_bf_b = 0;
// SP
let res_sp = dec_sp.decode(&received_llr);
if let Some(decoded) = res_sp.codeword() {
let errs = count_bit_errors(&codeword, decoded);
if errs > 0 {
t_sp_f = 1;
t_sp_b = errs;
}
} else {
t_sp_f = 1;
t_sp_b = code.n();
}
// MS
let res_ms = dec_ms.decode(&received_llr);
if let Some(decoded) = res_ms.codeword() {
let errs = count_bit_errors(&codeword, decoded);
if errs > 0 {
t_ms_f = 1;
t_ms_b = errs;
}
} else {
t_ms_f = 1;
t_ms_b = code.n();
}
// BF
let res_bf = dec_bf.decode(&received_llr);
if let Some(decoded) = res_bf.codeword() {
let errs = count_bit_errors(&codeword, decoded);
if errs > 0 {
t_bf_f = 1;
t_bf_b = errs;
}
} else {
t_bf_f = 1;
t_bf_b = code.n();
}
(t_sp_f, t_sp_b, t_ms_f, t_ms_b, t_bf_f, t_bf_b)
})
.collect();
// Agrégation
let mut err_sp_frames = 0;
let mut err_sp_bits = 0;
let mut err_ms_frames = 0;
let mut err_ms_bits = 0;
let mut err_bf_frames = 0;
let mut err_bf_bits = 0;
for res in results {
err_sp_frames += res.0;
err_sp_bits += res.1;
err_ms_frames += res.2;
err_ms_bits += res.3;
err_bf_frames += res.4;
err_bf_bits += res.5;
}
let total_bits = (n_trials * code.n()) as f64;
let total_frames = n_trials as f64;
println!(
"{:>8.2} | {:>9.4} || {:>9.2}% | {:>9.2}% || {:>9.2}% | {:>9.2}% || {:>9.2}% | {:>9.2}% |",
snr, channel.capacity(),
(err_sp_frames as f64 / total_frames) * 100.0, (err_sp_bits as f64 / total_bits) * 100.0,
(err_ms_frames as f64 / total_frames) * 100.0, (err_ms_bits as f64 / total_bits) * 100.0,
(err_bf_frames as f64 / total_frames) * 100.0, (err_bf_bits as f64 / total_bits) * 100.0
);
}
println!("{:-<115}", "");
Ok(())
}
#[inline]
fn count_bit_errors(transmitted: &[u8], decoded: &[u8]) -> usize {
transmitted
.iter()
.zip(decoded.iter())
.filter(|(a, b)| a != b)
.count()
}
// pub fn generate_valid_code(
// n: usize,
// k: usize,
// wc: usize,
// wr: usize,
// generation_method: GenerationMethod,
// ) -> LdpcResult<LdpcCode> {
// let mut attempt = 0;
// let start_gen = Instant::now();
// loop {
// attempt += 1;
// let params = LdpcParams {
// n,
// k,
// topology: CodeTopology::Regular { wc, wr },
// generation: generation_method.clone(),
// seed: Some(rand::random()),
// };
//
// if let Ok(mut code) = LdpcCode::new(params) {
// if code.compute_systematic_form().is_ok() {
// if attempt > 1 {
// println!(
// " -> Matrice inversible obtenue après {} tentatives.",
// attempt
// );
// }
// println!(" - Génération : Terminée en {:.2?}", start_gen.elapsed());
// return Ok(code);
// }
// }
// }
// }
pub fn get_or_generate_cached_code(
n: usize,
k: usize,
wc: usize,
wr: usize,
generation_method: GenerationMethod,
) -> LdpcResult<LdpcCode> {
let method_str = match generation_method {
GenerationMethod::MacKayNeal { .. } => "MN",
GenerationMethod::Gallager => "GAL",
};
let cache_filename = format!("cache_ldpc_{}_n{}_k{}.bin", method_str, n, k);
let path = Path::new(&cache_filename);
// Chargement
if path.exists() {
let start_load = Instant::now();
let mut file = File::open(&path).expect("Impossible d'ouvrir le fichier de cache");
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let mut code: LdpcCode = bincode::deserialize(&buffer).expect(
"Erreur de désérialisation du cache LDPC. Supprimez le fichier .bin et réessayez.",
);
// Reconstruction du Graphe de Tanner
code.graph = crate::graph::TannerGraph::from_matrix(&code.h);
println!(
" -> Matrice chargée depuis le cache en {:.2?}",
start_load.elapsed()
);
return Ok(code);
}
// Génération
println!(" -> Aucun cache trouvé. Génération en cours ...");
let mut attempt = 0;
let start_gen = Instant::now();
loop {
attempt += 1;
let params = LdpcParams {
n,
k,
topology: CodeTopology::Regular { wc, wr },
generation: generation_method.clone(),
seed: Some(rand::random()),
};
if let Ok(mut code) = LdpcCode::new(params) {
if code.compute_systematic_form().is_ok() {
println!(
" - Génération et Pivot de Gauss terminés en {:.2?}",
start_gen.elapsed()
);
// sauvegarde cache
let encoded = bincode::serialize(&code).expect("Échec de la sérialisation");
let mut file =
File::create(&path).expect("Impossible de créer le fichier de cache");
file.write_all(&encoded)
.expect("Impossible d'écrire sur le disque");
println!(" -> Matrice sauvegardée ({})", cache_filename);
return Ok(code);
}
}
}
}