diff --git a/Cargo.lock b/Cargo.lock index afd0b22..aac622f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,56 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "approx" version = "0.5.1" @@ -44,6 +94,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + [[package]] name = "bytemuck" version = "1.25.0" @@ -62,12 +118,70 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "unicode-width", + "windows-sys", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -114,6 +228,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "exr" version = "1.74.0" @@ -148,6 +268,30 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "getrandom" version = "0.2.17" @@ -180,6 +324,18 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "image" version = "0.24.9" @@ -198,6 +354,25 @@ dependencies = [ "tiff", ] +[[package]] +name = "indicatif" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" +dependencies = [ + "console", + "portable-atomic", + "unicode-width", + "unit-prefix", + "web-time", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "jpeg-decoder" version = "0.3.2" @@ -207,15 +382,31 @@ dependencies = [ "rayon", ] +[[package]] +name = "js-sys" +version = "0.3.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + [[package]] name = "ldpc" version = "0.1.0" dependencies = [ "approx", "bincode", + "clap", "image", + "indicatif", + "num_cpus", "rand", "rand_distr", + "rayon", "serde", "thiserror", ] @@ -258,6 +449,34 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "png" version = "0.17.16" @@ -271,6 +490,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -367,6 +592,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "serde" version = "1.0.228" @@ -403,12 +634,24 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.117" @@ -457,18 +700,106 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasm-bindgen" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "zerocopy" version = "0.8.48" diff --git a/Cargo.toml b/Cargo.toml index 924849d..43611d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,10 @@ rand_distr = "0.4.3" thiserror = "1.0" serde = { version = "1.0", features = ["derive"] } bincode = "1.3" +rayon = "1.12.0" +indicatif = "0.18.4" +clap = {version = "4.6.1", features = ["derive"]} +num_cpus = "1.17.0" [dev-dependencies] approx = "0.5" diff --git a/cache_ldpc_GAL_n1200_k900.bin b/cache_ldpc_GAL_n1200_k900.bin new file mode 100644 index 0000000..d50bddb Binary files /dev/null and b/cache_ldpc_GAL_n1200_k900.bin differ diff --git a/cache_ldpc_GAL_n1296_k648.bin b/cache_ldpc_GAL_n1296_k648.bin new file mode 100644 index 0000000..9359cb7 Binary files /dev/null and b/cache_ldpc_GAL_n1296_k648.bin differ diff --git a/cache_ldpc_GAL_n1296_k864.bin b/cache_ldpc_GAL_n1296_k864.bin new file mode 100644 index 0000000..45d85f3 Binary files /dev/null and b/cache_ldpc_GAL_n1296_k864.bin differ diff --git a/cache_ldpc_GAL_n1944_k972.bin b/cache_ldpc_GAL_n1944_k972.bin new file mode 100644 index 0000000..b20e7aa Binary files /dev/null and b/cache_ldpc_GAL_n1944_k972.bin differ diff --git a/cache_ldpc_GAL_n24_k12.bin b/cache_ldpc_GAL_n24_k12.bin new file mode 100644 index 0000000..e4b6021 Binary files /dev/null and b/cache_ldpc_GAL_n24_k12.bin differ diff --git a/cache_ldpc_GAL_n408_k204.bin b/cache_ldpc_GAL_n408_k204.bin new file mode 100644 index 0000000..5e45d28 Binary files /dev/null and b/cache_ldpc_GAL_n408_k204.bin differ diff --git a/cache_ldpc_MACKAY_n1005_k804.bin b/cache_ldpc_MACKAY_n1005_k804.bin new file mode 100644 index 0000000..b1a1c39 Binary files /dev/null and b/cache_ldpc_MACKAY_n1005_k804.bin differ diff --git a/cache_ldpc_MACKAY_n1296_k648.bin b/cache_ldpc_MACKAY_n1296_k648.bin new file mode 100644 index 0000000..4d81c48 Binary files /dev/null and b/cache_ldpc_MACKAY_n1296_k648.bin differ diff --git a/cache_ldpc_MN_n1296_k864.bin b/cache_ldpc_MN_n1296_k864.bin new file mode 100644 index 0000000..0672c91 Binary files /dev/null and b/cache_ldpc_MN_n1296_k864.bin differ diff --git a/cache_ldpc_MN_n408_k204.bin b/cache_ldpc_MN_n408_k204.bin new file mode 100644 index 0000000..f7f6998 Binary files /dev/null and b/cache_ldpc_MN_n408_k204.bin differ diff --git a/decoded_out.png b/decoded_out.png index ff81d07..5f082d2 100644 Binary files a/decoded_out.png and b/decoded_out.png differ diff --git a/noisy_out.png b/noisy_out.png index e29ddb6..ef64c13 100644 Binary files a/noisy_out.png and b/noisy_out.png differ diff --git a/src/benchmark.rs b/src/benchmark.rs index d800e56..59e232d 100644 --- a/src/benchmark.rs +++ b/src/benchmark.rs @@ -3,7 +3,8 @@ 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 rand::Rng; +use rayon::prelude::*; use std::fs::File; use std::io::{Read, Write}; use std::path::Path; @@ -37,13 +38,10 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> { } ); - println!("\n[*] Étape 2 : Extraction de G^T et Instanciation de l'Encodeur"); + 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!( - " - Forme systématique calculée en {:.2?}", - start_enc.elapsed() - ); + println!(" - Encodeur prêt en {:.2?}", start_enc.elapsed()); println!("\n[*] Étape 3 : Instanciation des Décodeurs sur Graphe"); let config = DecoderConfig { @@ -62,12 +60,11 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> { 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); + 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)", + "\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR, Multi-threadé)", n_trials ); println!("{:-<115}", ""); @@ -86,6 +83,67 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> { 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 = (0..code.k()).map(|_| rng.gen::() & 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; @@ -93,49 +151,13 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> { let mut err_bf_frames = 0; let mut err_bf_bits = 0; - for _ in 0..n_trials { - let message: Vec = (0..code.k()).map(|_| rng.gen::() & 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(); - } + 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; @@ -162,39 +184,39 @@ fn count_bit_errors(transmitted: &[u8], decoded: &[u8]) -> usize { .count() } -pub fn generate_valid_code( - n: usize, - k: usize, - wc: usize, - wr: usize, - generation_method: GenerationMethod, -) -> LdpcResult { - 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 generate_valid_code( +// n: usize, +// k: usize, +// wc: usize, +// wr: usize, +// generation_method: GenerationMethod, +// ) -> LdpcResult { +// 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, diff --git a/src/benchmark2.rs b/src/benchmark2.rs new file mode 100644 index 0000000..433f51e --- /dev/null +++ b/src/benchmark2.rs @@ -0,0 +1,228 @@ +use crate::benchmark::get_or_generate_cached_code; +use crate::channel::{AwgnChannel, Channel}; +use crate::code::GenerationMethod; +use crate::decoder::{build_decoder, DecoderConfig, DecoderMethod}; +use crate::encoder::{build_encoder, EncodingMethod}; +use crate::Result as LdpcResult; + +use indicatif::{ProgressBar, ProgressStyle}; +use rand::Rng; +use rayon::prelude::*; +use std::fs::OpenOptions; +use std::io::Write; +use std::time::Instant; + +#[derive(Clone)] +pub struct CodeScenario { + pub n: usize, + pub k: usize, + pub wc: usize, + pub wr: usize, + pub method: GenerationMethod, + pub name: String, +} + +pub struct CampaignConfig { + pub scenarios: Vec, + pub snr_range: Vec, + pub n_trials: usize, + pub max_iterations: usize, + pub output_csv: String, + pub export_graph: bool, +} + +pub fn run_massive_campaign(config: CampaignConfig) -> LdpcResult<()> { + println!("TEST)"); + println!("Fichier de sortie : {}", config.output_csv); + println!("Scenarios a tester: {}", config.scenarios.len()); + println!("SNRs par scenario : {}", config.snr_range.len()); + println!("Trames par point : {}\n", config.n_trials); + + let file_exists = std::path::Path::new(&config.output_csv).exists(); + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(&config.output_csv) + .expect("Impossible d'ouvrir le fichier CSV"); + + if !file_exists { + writeln!( + file, + "Scenario,N,K,Rate,Wc,Wr,Method,Girth,Density_pct,SNR_dB,Capacity,Decoder,FER_pct,BER_pct,Frames,Time_ms" + ).unwrap(); + } + + let total_steps = (config.scenarios.len() * config.snr_range.len()) as u64; + let pb = ProgressBar::new(total_steps); + pb.set_style( + ProgressStyle::default_bar() + .template("{spinner:.cyan} [{elapsed_precise}] [{bar:40.green/blue}] {pos}/{len} ({eta}) - {msg}") + .unwrap() + .progress_chars("=>-"), + ); + + for scenario in config.scenarios { + pb.set_message(format!("Generation Matrice: {}", scenario.name)); + + let mut code = get_or_generate_cached_code( + scenario.n, + scenario.k, + scenario.wc, + scenario.wr, + scenario.method.clone(), + )?; + + if config.export_graph { + let dot_filename = format!("{}_tanner.dot", scenario.name); + let mut dot_file = std::fs::File::create(&dot_filename).expect("Erreur creation .dot"); + + writeln!(dot_file, "graph TannerGraph {{").unwrap(); + writeln!(dot_file, " rankdir=TB;").unwrap(); + writeln!(dot_file, " nodesep=0.5;").unwrap(); + writeln!(dot_file, " ranksep=2.0;").unwrap(); + + writeln!(dot_file, " node [style=filled, fontname=\"Arial\"];").unwrap(); + + writeln!(dot_file, " {{ rank=same;").unwrap(); + for v in 0..code.n() { + writeln!( + dot_file, + " v{} [shape=circle, fillcolor=lightblue, label=\"v{}\"];", + v, v + ) + .unwrap(); + } + writeln!(dot_file, " }}").unwrap(); + + writeln!(dot_file, " {{ rank=same;").unwrap(); + for c in 0..code.m() { + writeln!( + dot_file, + " c{} [shape=square, fillcolor=lightcoral, label=\"c{}\"];", + c, c + ) + .unwrap(); + } + writeln!(dot_file, " }}").unwrap(); + + for c in 0..code.m() { + for &v in code.graph.chk_neighbors(c) { + writeln!(dot_file, " v{} -- c{};", v, c).unwrap(); + } + } + writeln!(dot_file, "}}").unwrap(); + } + + let rate = code.rate(); + let girth = code.girth(); + let density = code.h.density() * 100.0; + let method_str = match scenario.method { + GenerationMethod::Gallager => "Gallager", + GenerationMethod::MacKayNeal { .. } => "MacKayNeal", + }; + + pb.set_message(format!("Initialisation DSP: {}", scenario.name)); + let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?; + + let dec_config = DecoderConfig { + max_iterations: config.max_iterations, + early_stopping: true, + }; + let dec_sp = build_decoder(&code, DecoderMethod::SumProduct, dec_config.clone()); + let dec_ms = build_decoder( + &code, + DecoderMethod::MinSum { + scaling_factor: 0.8, + }, + dec_config.clone(), + ); + let dec_bf = build_decoder(&code, DecoderMethod::BitFlipping, dec_config); + + for &snr in &config.snr_range { + pb.set_message(format!("Test: {} | SNR: {:.2} dB", scenario.name, snr)); + let channel = AwgnChannel::new(snr, rate)?; + let cap = channel.capacity(); + + let start_snr_time = Instant::now(); + + let results: Vec<_> = (0..config.n_trials) + .into_par_iter() + .map(|_| { + let mut rng = rand::thread_rng(); + let message: Vec = (0..scenario.k).map(|_| rng.gen::() & 1).collect(); + let codeword = encoder.encode(&message).unwrap(); + let rx_llr = channel.transmit(&codeword, &mut rng); + + let eval_decoder = |decoder: &dyn crate::decoder::Decoder| -> (usize, usize) { + if let Some(decoded) = decoder.decode(&rx_llr).codeword() { + let errs = codeword + .iter() + .zip(decoded.iter()) + .filter(|(a, b)| a != b) + .count(); + (if errs > 0 { 1 } else { 0 }, errs) + } else { + (1, scenario.n) + } + }; + + let (sp_f, sp_b) = eval_decoder(&*dec_sp); + let (ms_f, ms_b) = eval_decoder(&*dec_ms); + let (bf_f, bf_b) = eval_decoder(&*dec_bf); + + (sp_f, sp_b, ms_f, ms_b, bf_f, bf_b) + }) + .collect(); + + let mut sp_err = (0, 0); + let mut ms_err = (0, 0); + let mut bf_err = (0, 0); + for r in results { + sp_err.0 += r.0; + sp_err.1 += r.1; + ms_err.0 += r.2; + ms_err.1 += r.3; + bf_err.0 += r.4; + bf_err.1 += r.5; + } + + let elapsed_ms = start_snr_time.elapsed().as_millis(); + let total_f = config.n_trials as f64; + let total_b = (config.n_trials * scenario.n) as f64; + + let mut write_csv_line = |dec_name: &str, f_err: usize, b_err: usize| { + writeln!( + file, + "{},{},{},{:.3},{},{},{},{},{:.4},{:.2},{:.4},{},{:.4},{:.4e},{},{}", + scenario.name, + scenario.n, + scenario.k, + rate, + scenario.wc, + scenario.wr, + method_str, + girth, + density, + snr, + cap, + dec_name, + (f_err as f64 / total_f) * 100.0, + (b_err as f64 / total_b) * 100.0, + config.n_trials, + elapsed_ms + ) + .unwrap(); + }; + + write_csv_line("SumProduct", sp_err.0, sp_err.1); + write_csv_line("MinSum_0.8", ms_err.0, ms_err.1); + write_csv_line("BitFlipping", bf_err.0, bf_err.1); + + file.flush().unwrap(); + pb.inc(1); + } + } + + pb.finish_with_message("Campagne terminee avec succes !"); + Ok(()) +} diff --git a/src/bin/generator.rs b/src/bin/generator.rs new file mode 100644 index 0000000..75efca4 --- /dev/null +++ b/src/bin/generator.rs @@ -0,0 +1,118 @@ +use clap::Parser; +use indicatif::{ProgressBar, ProgressStyle}; +use ldpc::code::{CodeTopology, GenerationMethod, LdpcCode, LdpcParams}; +use rayon::prelude::*; +use std::fs::File; +use std::io::Write; +use std::sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, +}; +use std::time::{Duration, Instant}; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Generateur LDPC Haute Performance")] +struct Args { + #[arg(short, long)] + n: usize, + #[arg(short, long)] + k: usize, + #[arg(long, default_value_t = 3)] + wc: usize, + #[arg(long, default_value_t = 6)] + wr: usize, + #[arg(short, long, default_value = "mackay")] + method: String, +} + +fn main() -> ldpc::Result<()> { + let args = Args::parse(); + let m = args.n - args.k; + + if (args.n * args.wc) % m != 0 || (args.n * args.wc) / m != args.wr { + println!("Erreur : Parametres impossibles (n*wc != m*wr)"); + std::process::exit(1); + } + + let found = Arc::new(AtomicBool::new(false)); + let attempts = Arc::new(AtomicU64::new(0)); + let start_global = Instant::now(); + + let pb = ProgressBar::new_spinner(); + pb.enable_steady_tick(Duration::from_millis(80)); + pb.set_style( + ProgressStyle::default_spinner() + .template( + "{spinner:.cyan} [{elapsed_precise}] {msg} | Tentatives: {pos} | {per_sec} mats/s", + ) + .unwrap(), + ); + pb.set_message("Recherche d'une matrice valide..."); + + let method = if args.method == "mackay" { + GenerationMethod::MacKayNeal { max_attempts: 1000 } + } else { + GenerationMethod::Gallager + }; + + let pb_clone = pb.clone(); + let found_clone = Arc::clone(&found); + let attempts_clone = Arc::clone(&attempts); + + let result = (0..num_cpus::get()) + .into_par_iter() + .map(|_| { + while !found_clone.load(Ordering::Relaxed) { + let current_attempt = attempts_clone.fetch_add(1, Ordering::SeqCst); + pb_clone.set_position(current_attempt); + + let params = LdpcParams { + n: args.n, + k: args.k, + topology: CodeTopology::Regular { + wc: args.wc, + wr: args.wr, + }, + generation: method.clone(), + seed: Some(rand::random()), + }; + + if let Ok(mut code) = LdpcCode::new(params) { + // Cette etape est la plus longue (Gauss-Jordan) + if code.compute_systematic_form().is_ok() { + if !found_clone.swap(true, Ordering::SeqCst) { + return Some(code); + } + } + } + } + None + }) + .find_any(|res| res.is_some()) + .flatten(); + + if let Some(code) = result { + pb.finish_with_message(format!("Succes en {:.2?}", start_global.elapsed())); + + let filename = format!( + "cache_ldpc_{}_n{}_k{}.bin", + args.method.to_uppercase(), + args.n, + args.k + ); + let encoded = bincode::serialize(&code).expect("Erreur serialisation"); + let mut file = File::create(&filename).expect("Erreur creation"); + file.write_all(&encoded).expect("Erreur ecriture"); + + println!("\nFichier genere : {}", filename); + println!( + "Girth : {} | Densite : {:.2}%", + code.girth(), + code.h.density() * 100.0 + ); + } else { + pb.abandon_with_message("Recherche arretee."); + } + + Ok(()) +} diff --git a/src/encoder.rs b/src/encoder.rs index 6193801..93b0019 100644 --- a/src/encoder.rs +++ b/src/encoder.rs @@ -1,5 +1,4 @@ use crate::code::LdpcCode; -use crate::matrix::DenseMatrixGF2; use crate::{BitVec, Gf2, LdpcError, Result}; pub trait Encoder: Send + Sync { @@ -27,7 +26,8 @@ pub enum EncodingMethod { pub struct SystematicEncoder { k: usize, n: usize, - g_t: DenseMatrixGF2, + // g_t: DenseMatrixGF2, + packed_g_cols: Vec>, perm_inv: Vec, col_perm: Vec, } @@ -36,10 +36,30 @@ impl SystematicEncoder { pub fn new(code: &mut LdpcCode) -> Result { code.compute_systematic_form()?; let sf = code.systematic_form.as_ref().unwrap(); + + let k = code.k(); + let n = code.n(); + let g_t = &sf.g; + + let num_blocks = (n + 63) / 64; + + // Bitpacking + let mut packed_g_cols = vec![vec![0u64; num_blocks]; k]; + + for j in 0..k { + for i in 0..n { + if g_t.get(i, j) == 1 { + let block_idx = i / 64; + let bit_idx = i % 64; + packed_g_cols[j][block_idx] |= 1 << bit_idx; + } + } + } + Ok(Self { - k: code.k(), - n: code.n(), - g_t: sf.g.clone(), + k, + n, + packed_g_cols, perm_inv: sf.col_perm_inv.clone(), col_perm: sf.col_perm.clone(), }) @@ -47,19 +67,44 @@ impl SystematicEncoder { } impl Encoder for SystematicEncoder { + // fn encode(&self, message: &[Gf2]) -> Result { + // self.check_input(message)?; + // + // let c_perm = self.g_t.multiply_vec(message); + // + // // Retablir l'ordre initial des bits selon la permutation de H + // let mut c = vec![0u8; self.n]; + // // for (i, &ci) in c_perm.iter().enumerate() { + // // c[self.perm_inv[i]] = ci; + // // } + // + // for i in 0..self.n { + // c[i] = c_perm[self.perm_inv[i]]; + // } + // + // Ok(c) + // } fn encode(&self, message: &[Gf2]) -> Result { self.check_input(message)?; - let c_perm = self.g_t.multiply_vec(message); + let num_blocks = (self.n + 63) / 64; + let mut accum = vec![0u64; num_blocks]; + + for (j, &bit) in message.iter().enumerate() { + if bit == 1 { + for b in 0..num_blocks { + accum[b] ^= self.packed_g_cols[j][b]; + } + } + } - // Retablir l'ordre initial des bits selon la permutation de H let mut c = vec![0u8; self.n]; - // for (i, &ci) in c_perm.iter().enumerate() { - // c[self.perm_inv[i]] = ci; - // } - for i in 0..self.n { - c[i] = c_perm[self.perm_inv[i]]; + let block_idx = i / 64; + let bit_idx = i % 64; + let val = ((accum[block_idx] >> bit_idx) & 1) as u8; + + c[self.col_perm[i]] = val; } Ok(c) diff --git a/src/image_sim.rs b/src/image_sim.rs index 0deb354..1e08bca 100644 --- a/src/image_sim.rs +++ b/src/image_sim.rs @@ -5,7 +5,9 @@ use crate::{ Gf2, Result, }; use image::{ImageBuffer, Rgb}; -use rand::SeedableRng; +use indicatif::{ProgressBar, ProgressStyle}; +use rayon::prelude::*; +use std::time::Instant; // Convertit un tableau d'octets en un flux de bits pub fn bytes_to_bits(bytes: &[u8]) -> Vec { @@ -32,6 +34,103 @@ pub fn bits_to_bytes(bits: &[Gf2]) -> Vec { } // Transmet une image à travers le canal avec codage LDPC +// pub fn transmit_image( +// input_path: &str, +// noisy_out_path: &str, +// decoded_out_path: &str, +// encoder: &dyn Encoder, +// decoder: &dyn Decoder, +// channel: &AwgnChannel, +// ) -> Result<()> { +// println!("[*] Chargement de l'image : {}", input_path); +// let img = image::open(input_path) +// .expect("Erreur de chargement de l'image") +// .to_rgb8(); +// let (width, height) = img.dimensions(); +// let raw_bytes = img.into_raw(); +// +// let mut bits = bytes_to_bits(&raw_bytes); +// let original_bit_len = bits.len(); +// +// // Padding +// let k = encoder.message_len(); +// let remainder = bits.len() % k; +// if remainder != 0 { +// bits.resize(bits.len() + (k - remainder), 0); +// } +// +// let num_blocks = bits.len() / k; +// println!(" - Taille: {}x{} pixels", width, height); +// println!(" - Blocs à transmettre (k={}): {}", k, num_blocks); +// +// let mut rng = rand::rngs::StdRng::seed_from_u64(42); +// +// let mut noisy_bits = Vec::with_capacity(num_blocks * k); +// let mut decoded_bits = Vec::with_capacity(num_blocks * k); +// +// let mut frame_errors = 0; +// +// println!("[*] Transmission et Décodage en cours..."); +// +// for (i, block) in bits.chunks(k).enumerate() { +// if i % 100 == 0 && i > 0 { +// println!(" - Progession: {} / {} blocs...", i, num_blocks); +// } +// +// let codeword = encoder.encode(block)?; +// +// let rx_llr = channel.transmit(&codeword, &mut rng); +// +// // Sans correction LDPC +// let hard_codeword: Vec = rx_llr +// .iter() +// .map(|&l| if l < 0.0 { 1 } else { 0 }) +// .collect(); +// let noisy_block = encoder.extract_message(&hard_codeword); +// noisy_bits.extend_from_slice(&noisy_block); +// +// // Décodage LDPC +// let res = decoder.decode(&rx_llr); +// if let Some(decoded_codeword) = res.codeword() { +// let decoded_msg = encoder.extract_message(decoded_codeword); +// decoded_bits.extend_from_slice(&decoded_msg); +// +// if decoded_msg != block { +// frame_errors += 1; +// } +// } else { +// decoded_bits.extend_from_slice(&noisy_block); +// frame_errors += 1; +// } +// } +// +// println!( +// "[*] Transmission terminée. FER : {:.2}%", +// (frame_errors as f64 / num_blocks as f64) * 100.0 +// ); +// +// // Suppression du padding +// noisy_bits.truncate(original_bit_len); +// decoded_bits.truncate(original_bit_len); +// +// // Reconstitution des images +// let noisy_bytes = bits_to_bytes(&noisy_bits); +// let decoded_bytes = bits_to_bytes(&decoded_bits); +// +// let noisy_img = ImageBuffer::, _>::from_raw(width, height, noisy_bytes) +// .expect("Erreur de reconstruction de l'image bruitée"); +// noisy_img.save(noisy_out_path).unwrap(); +// +// let decoded_img = ImageBuffer::, _>::from_raw(width, height, decoded_bytes) +// .expect("Erreur de reconstruction de l'image décodée"); +// decoded_img.save(decoded_out_path).unwrap(); +// +// println!( +// "[*] Images sauvegardées : {} et {}", +// noisy_out_path, decoded_out_path +// ); +// Ok(()) +// } pub fn transmit_image( input_path: &str, noisy_out_path: &str, @@ -40,9 +139,9 @@ pub fn transmit_image( decoder: &dyn Decoder, channel: &AwgnChannel, ) -> Result<()> { - println!("[*] Chargement de l'image : {}", input_path); + println!("\n[*] Chargement de l'image : {}", input_path); let img = image::open(input_path) - .expect("Erreur de chargement de l'image") + .expect("Erreur : Impossible de trouver ou lire l'image") .to_rgb8(); let (width, height) = img.dimensions(); let raw_bytes = img.into_raw(); @@ -50,7 +149,6 @@ pub fn transmit_image( let mut bits = bytes_to_bits(&raw_bytes); let original_bit_len = bits.len(); - // Padding let k = encoder.message_len(); let remainder = bits.len() % k; if remainder != 0 { @@ -58,60 +156,68 @@ pub fn transmit_image( } let num_blocks = bits.len() / k; - println!(" - Taille: {}x{} pixels", width, height); - println!(" - Blocs à transmettre (k={}): {}", k, num_blocks); + println!(" - Résolution : {}x{} pixels", width, height); + println!( + " - Poids total : {:.2} Mo", + raw_bytes.len() as f64 / 1_048_576.0 + ); + println!(" - Découpage en : {} blocs de {} bits\n", num_blocks, k); - let mut rng = rand::rngs::StdRng::seed_from_u64(42); + let start_time = Instant::now(); + + let pb = ProgressBar::new(num_blocks as u64); + pb.set_style( + ProgressStyle::default_bar() + .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} blocs ({eta})") + .unwrap() + .progress_chars("=>-") + ); + + let results: Vec<_> = bits + .par_chunks(k) + .map(|block| { + let mut rng = rand::thread_rng(); + let codeword = encoder.encode(block).unwrap(); + let rx_llr = channel.transmit(&codeword, &mut rng); + let hard_codeword: Vec = rx_llr + .iter() + .map(|&l| if l < 0.0 { 1 } else { 0 }) + .collect(); + let noisy_block = encoder.extract_message(&hard_codeword); + let res = decoder.decode(&rx_llr); + + let (decoded_block, has_error) = if let Some(decoded_codeword) = res.codeword() { + let decoded_msg = encoder.extract_message(decoded_codeword); + let err = if decoded_msg != block { 1 } else { 0 }; + (decoded_msg, err) + } else { + (noisy_block.clone(), 1) + }; + pb.inc(1); + (noisy_block, decoded_block, has_error) + }) + .collect(); + + pb.finish_with_message("Décodage terminé !"); let mut noisy_bits = Vec::with_capacity(num_blocks * k); let mut decoded_bits = Vec::with_capacity(num_blocks * k); - let mut frame_errors = 0; - println!("[*] Transmission et Décodage en cours..."); - - for (i, block) in bits.chunks(k).enumerate() { - if i % 100 == 0 && i > 0 { - println!(" - Progession: {} / {} blocs...", i, num_blocks); - } - - let codeword = encoder.encode(block)?; - - let rx_llr = channel.transmit(&codeword, &mut rng); - - // Sans correction LDPC - let hard_codeword: Vec = rx_llr - .iter() - .map(|&l| if l < 0.0 { 1 } else { 0 }) - .collect(); - let noisy_block = encoder.extract_message(&hard_codeword); - noisy_bits.extend_from_slice(&noisy_block); - - // Décodage LDPC - let res = decoder.decode(&rx_llr); - if let Some(decoded_codeword) = res.codeword() { - let decoded_msg = encoder.extract_message(decoded_codeword); - decoded_bits.extend_from_slice(&decoded_msg); - - if decoded_msg != block { - frame_errors += 1; - } - } else { - decoded_bits.extend_from_slice(&noisy_block); - frame_errors += 1; - } + for (n_block, d_block, err) in results { + noisy_bits.extend_from_slice(&n_block); + decoded_bits.extend_from_slice(&d_block); + frame_errors += err; } - println!( - "[*] Transmission terminée. FER : {:.2}%", - (frame_errors as f64 / num_blocks as f64) * 100.0 - ); + let duration = start_time.elapsed(); + let fer = (frame_errors as f64 / num_blocks as f64) * 100.0; + println!("[*] Transmission terminée en {:.2?}", duration); + println!(" - Taux d'Erreur Trame (FER) : {:.2}%", fer); - // Suppression du padding noisy_bits.truncate(original_bit_len); decoded_bits.truncate(original_bit_len); - // Reconstitution des images let noisy_bytes = bits_to_bytes(&noisy_bits); let decoded_bytes = bits_to_bytes(&decoded_bits); @@ -124,7 +230,7 @@ pub fn transmit_image( decoded_img.save(decoded_out_path).unwrap(); println!( - "[*] Images sauvegardées : {} et {}", + "[*] Succès ! Images sauvegardées : {} et {}", noisy_out_path, decoded_out_path ); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 0968aab..2b033dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod benchmark; +pub mod benchmark2; pub mod channel; pub mod code; pub mod decoder; diff --git a/src/main.rs b/src/main.rs index 81697df..f875865 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,49 +5,116 @@ use ldpc::decoder::{build_decoder, DecoderConfig, DecoderMethod}; use ldpc::encoder::{build_encoder, EncodingMethod}; use ldpc::image_sim::transmit_image; +// fn main() -> ldpc::Result<()> { +// let n = 1944; +// let k = 972; +// let wc = 3; +// let wr = 6; +// +// println!("Transmission d'image via code LDPC"); +// +// let code_mn = get_or_generate_cached_code( +// n, +// k, +// wc, +// wr, +// GenerationMethod::MacKayNeal { max_attempts: 5000 }, +// )?; +// +// let mut code = code_mn; +// let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?; +// +// let config = DecoderConfig { +// max_iterations: 50, +// early_stopping: true, +// }; +// +// // Sum-Product +// let decoder = build_decoder(&code, DecoderMethod::SumProduct, config); +// +// let channel = AwgnChannel::new(2.0, code.rate())?; +// +// transmit_image( +// "test.png", +// "noisy_out.png", +// "decoded_out.png", +// &*encoder, +// &*decoder, +// &channel, +// )?; +// +// Ok(()) +// } + +use clap::Parser; +use ldpc::benchmark2::{run_massive_campaign, CampaignConfig, CodeScenario}; + +#[derive(Parser, Debug)] +#[command(author, version, about = "Laboratoire de test LDPC - TIPE")] +struct Args { + #[arg(short, long, default_value_t = 1000)] + trials: usize, + + /// Générer les fichiers .dot pour visualiser les graphes de Tanner + #[arg(long, default_value_t = false)] + export_graph: bool, +} + fn main() -> ldpc::Result<()> { - let n = 1944; - let k = 972; - let wc = 3; - let wr = 6; + let args = Args::parse(); + let snrs = vec![1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.5, 4.0]; + let mut scenarios = Vec::new(); - println!("Transmission d'image via code LDPC"); + // ------------------------------------------------------------------------- + // SCÉNARIO "TOY MODEL" : Spécial pour ton document Typst + // Un code minuscule R=1/2 avec n=24, k=12, m=12. + // Lisible sur une feuille A4. + // ------------------------------------------------------------------------- + if args.export_graph { + scenarios.push(CodeScenario { + name: "ToyModel_Typst_N24".into(), + n: 24, + k: 12, + wc: 3, + wr: 6, + method: GenerationMethod::Gallager, + }); + } - let code_mn = get_or_generate_cached_code( - n, - k, - wc, - wr, - GenerationMethod::MacKayNeal { max_attempts: 5000 }, - )?; + // Les vrais tests pour tes courbes de performances + scenarios.push(CodeScenario { + name: "Gallager_N1296_R05".into(), + n: 1296, + k: 648, + wc: 3, + wr: 6, + method: GenerationMethod::Gallager, + }); - let mut code = code_mn; - let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?; + scenarios.push(CodeScenario { + name: "MacKay_N1296_R05".into(), + n: 1296, + k: 648, + wc: 3, + wr: 6, + method: GenerationMethod::MacKayNeal { max_attempts: 1000 }, + }); - let config = DecoderConfig { + let config = CampaignConfig { + scenarios, + snr_range: snrs, + n_trials: args.trials, max_iterations: 50, - early_stopping: true, + output_csv: "tipe_results.csv".into(), + export_graph: args.export_graph, }; - // Sum-Product - let decoder = build_decoder(&code, DecoderMethod::SumProduct, config); - - let channel = AwgnChannel::new(2.0, code.rate())?; - - transmit_image( - "test.png", - "noisy_out.png", - "decoded_out.png", - &*encoder, - &*decoder, - &channel, - )?; + run_massive_campaign(config)?; Ok(()) } -// use ldpc::benchmark::{generate_valid_code, run_simulation}; -// use ldpc::code::GenerationMethod; +// use ldpc::benchmark::run_simulation; // // fn main() -> ldpc::Result<()> { // let n = 1944; @@ -59,17 +126,18 @@ fn main() -> ldpc::Result<()> { // println!(); // // println!("Test 1: Génération MacKayNeal\n"); -// let code_mn = generate_valid_code( +// let code_mn = get_or_generate_cached_code( // n, // k, // wc, // wr, -// GenerationMethod::MacKayNeal { max_attempts: 1000 }, +// GenerationMethod::MacKayNeal { max_attempts: 5000 }, // )?; +// // run_simulation(code_mn)?; // // println!("\nTest 2 : Génération Gallager\n"); -// let code_gal = generate_valid_code(n, k, wc, wr, GenerationMethod::Gallager)?; +// let code_gal = get_or_generate_cached_code(n, k, wc, wr, GenerationMethod::Gallager)?; // run_simulation(code_gal)?; // // Ok(()) diff --git a/test.png b/test.png index a9e8789..1a6759c 100644 Binary files a/test.png and b/test.png differ diff --git a/test2.png b/test2.png new file mode 100644 index 0000000..a9e8789 Binary files /dev/null and b/test2.png differ