ldpc_toolbox/cli/mackay_neal.rs
1//! MacKay-Neal CLI subcommand
2//!
3//! This subcommand uses the MacKay-Neal pseudorandom construction to build
4//! an LDPC parity check matrix. It runs the MacKay-Neal algorithm and,
5//! if the construction is successful, prints to `stdout` the alist of the
6//! parity check matrix. For more details about this construction, see
7//! [`crate::mackay_neal`].
8//!
9//! # Examples
10//! An r=1/2, n=16800 regular code with column weight 3 can be generated
11//! with
12//! ```shell
13//! $ ldpc-toolbox mackay-neal 8100 16200 6 3 0 --uniform
14//! ```
15//! The `--uniform` parameter is useful when constructing regular codes
16//! to prevent the construction from failing (see [`FillPolicy`]).
17//!
18//! A minimum graph girth can be enforced using the `--min_girth` and
19//! `--girth_trials` parameters. For instance, a code of girth at least
20//! 8 can be construced like so:
21//! ```shell
22//! $ ldpc-toolbox mackay-neal 8100 16200 6 3 0 --uniform \
23//! --min-girth 8 --girth-trials 1000
24//! ```
25//! This uses backtracking to try to find a construction that satisfies
26//! the girth requirement.
27//!
28//! For high rate codes, the construction is less likely to suceed even if
29//! backtracking is used. The `--search` parameter is useful to try several
30//! seeds in parallel. It will print to `stderr` the seed that gave a successful
31//! construction. For instance, an r=8/9 code with no 4-cycles can be
32//! constructed with
33//! ```shell
34//! $ ldpc-toolbox mackay-neal 1800 16200 27 3 0 --uniform \
35//! --min-girth 6 --girth-trials 1000 --search
36//! ```
37
38use crate::cli::*;
39use crate::mackay_neal::{Config, FillPolicy};
40use clap::Parser;
41use std::error::Error;
42
43/// MacKay-Neal CLI arguments.
44#[derive(Debug, Parser)]
45#[command(about = "Generates LDPC codes using the MacKay-Neal algorithm")]
46pub struct Args {
47 /// Number of rows
48 pub num_rows: usize,
49 /// Number of columns
50 pub num_columns: usize,
51 /// Maximum row weight
52 pub wr: usize,
53 /// Column weight
54 pub wc: usize,
55 /// Seed
56 pub seed: u64,
57 /// Columns to backtrack
58 #[arg(long, default_value_t = 0)]
59 pub backtrack_cols: usize,
60 /// Backtrack attemps
61 #[arg(long, default_value_t = 0)]
62 pub backtrack_trials: usize,
63 /// Minimum girth
64 #[arg(long)]
65 pub min_girth: Option<usize>,
66 /// Girth trials
67 #[arg(long, default_value_t = 0)]
68 pub girth_trials: usize,
69 /// Use uniform fill policy
70 #[arg(long)]
71 pub uniform: bool,
72 /// Maximum seed trials
73 #[arg(long, default_value_t = 1000)]
74 pub seed_trials: u64,
75 /// Try several seeds in parallel
76 #[arg(long)]
77 pub search: bool,
78}
79
80impl Args {
81 fn config(&self) -> Config {
82 Config {
83 nrows: self.num_rows,
84 ncols: self.num_columns,
85 wr: self.wr,
86 wc: self.wc,
87 backtrack_cols: self.backtrack_cols,
88 backtrack_trials: self.backtrack_trials,
89 min_girth: self.min_girth,
90 girth_trials: self.girth_trials,
91 fill_policy: match self.uniform {
92 true => FillPolicy::Uniform,
93 false => FillPolicy::Random,
94 },
95 }
96 }
97}
98
99impl Run for Args {
100 fn run(&self) -> Result<(), Box<dyn Error>> {
101 let conf = self.config();
102 let h = if self.search {
103 let (seed, hh) = conf
104 .search(self.seed, self.seed_trials)
105 .ok_or("no solution found")?;
106 eprintln!("seed = {}", seed);
107 hh
108 } else {
109 conf.run(self.seed)?
110 };
111 println!("{}", h.alist());
112 Ok(())
113 }
114}