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}