init
This commit is contained in:
268
src/benchmark.rs
Normal file
268
src/benchmark.rs
Normal file
@ -0,0 +1,268 @@
|
||||
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, SeedableRng};
|
||||
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 : Extraction de G^T et Instanciation de l'Encodeur");
|
||||
let start_enc = Instant::now();
|
||||
let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?;
|
||||
println!(
|
||||
" - Forme systématique calculée 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.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0];
|
||||
let n_trials = 100;
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(42);
|
||||
|
||||
println!(
|
||||
"\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR)",
|
||||
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 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 _ in 0..n_trials {
|
||||
let message: Vec<u8> = (0..code.k()).map(|_| rng.gen::<u8>() & 1).collect();
|
||||
let codeword = encoder.encode(&message)?;
|
||||
let received_llr = channel.transmit(&codeword, &mut rng);
|
||||
|
||||
// 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 {
|
||||
err_sp_frames += 1;
|
||||
err_sp_bits += errs;
|
||||
}
|
||||
} else {
|
||||
err_sp_frames += 1;
|
||||
err_sp_bits += 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 {
|
||||
err_ms_frames += 1;
|
||||
err_ms_bits += errs;
|
||||
}
|
||||
} else {
|
||||
err_ms_frames += 1;
|
||||
err_ms_bits += 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 {
|
||||
err_bf_frames += 1;
|
||||
err_bf_bits += errs;
|
||||
}
|
||||
} else {
|
||||
err_bf_frames += 1;
|
||||
err_bf_bits += code.n();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user