webgraph_cli/dist/ess/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2025 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6 */
7use crate::{GlobalArgs, NumThreadsArg};
8use anyhow::{Result, ensure};
9use clap::{Parser, ValueEnum};
10use dsi_bitstream::prelude::*;
11use dsi_progress_logger::{ProgressLog, concurrent_progress_logger};
12use std::path::PathBuf;
13use webgraph::{graphs::bvgraph::get_endianness, prelude::BvGraph};
14use webgraph_algo::distances::exact_sum_sweep::{
15    All, AllForward, Diameter, Level, Radius, RadiusDiameter,
16};
17
18#[derive(Parser, Debug)]
19#[command(name = "exactsumsweep", about = "Compute radius, diameter, and possibly eccentricities using the ExactSumSweep algorithm. (WORK IN PROGRESS)", long_about = None)]
20pub struct CliArgs {
21    /// The basename of the graph.
22    pub basename: PathBuf,
23
24    /// The transposed graph of `basename`.
25    pub transposed: Option<PathBuf>,
26
27    #[arg(short, long = "symm")]
28    /// If passed, we assume that the graph is symmetric.
29    pub symmetric: bool,
30
31    /// The path where to store the forward eccentricities.
32    #[arg(short, long)]
33    pub forward: Option<PathBuf>,
34
35    /// The path where to store the backward eccentricities.
36    #[arg(short, long)]
37    pub backward: Option<PathBuf>,
38
39    #[arg(long, value_enum)]
40    pub level: LevelArg,
41
42    #[clap(flatten)]
43    pub num_threads: NumThreadsArg,
44}
45
46#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
47/// Enum for the level of exact sum sweep to compute.
48pub enum LevelArg {
49    Radius,
50    Diameter,
51    #[clap(name = "radius-diameter")]
52    RadiusDiameter,
53    #[clap(name = "all-forward")]
54    AllForward,
55    All,
56}
57
58pub fn main(global_args: GlobalArgs, args: CliArgs) -> Result<()> {
59    println!("{:#4?}", args);
60    ensure!(
61        args.symmetric || args.transposed.is_some(),
62        "You have to either pass --transposed with with the basename of the transposed graph or --symm if the graph is symmetric."
63    );
64    ensure!(
65        !(args.symmetric && args.transposed.is_some()),
66        "--transposed is needed only if the graph is not symmetric."
67    );
68    ensure!(
69        args.forward.is_none() || matches!(args.level, LevelArg::All | LevelArg::AllForward),
70        "You cannot only pass --forward with --level=all or --level=all-forward as the forward eccentricities won't be computed otherwise."
71    );
72    ensure!(
73        !(args.forward.is_none() && matches!(args.level, LevelArg::All | LevelArg::AllForward)),
74        "If --level=all or --level=all-forward, you should pass --forward to store the computed eccentricities."
75    );
76    ensure!(
77        !(args.backward.is_some() && args.level != LevelArg::All),
78        "You cannot only pass --backward with --level=all as the backward eccentricities won't be computed otherwise."
79    );
80    ensure!(
81        !(args.level == LevelArg::All && args.symmetric && args.backward.is_some()),
82        "You cannot pass --backward with --symm and --level=all as the eccentricities of a symmetric graph are the same in both directions."
83    );
84
85    match get_endianness(&args.basename)?.as_str() {
86        #[cfg(feature = "be_bins")]
87        BE::NAME => exact_sum_sweep::<BE>(global_args, args),
88        #[cfg(feature = "le_bins")]
89        LE::NAME => exact_sum_sweep::<LE>(global_args, args),
90        e => panic!("Unknown endianness: {}", e),
91    }
92}
93
94pub fn exact_sum_sweep<E: Endianness>(global_args: GlobalArgs, args: CliArgs) -> Result<()> {
95    match args.level {
96        LevelArg::Radius => {
97            exact_sum_sweep_level::<E, Radius>(global_args, args)?;
98        }
99        LevelArg::Diameter => {
100            exact_sum_sweep_level::<E, Diameter>(global_args, args)?;
101        }
102        LevelArg::RadiusDiameter => {
103            exact_sum_sweep_level::<E, RadiusDiameter>(global_args, args)?;
104        }
105        LevelArg::AllForward => {
106            exact_sum_sweep_level::<E, AllForward>(global_args, args)?;
107        }
108        LevelArg::All => {
109            exact_sum_sweep_level::<E, All>(global_args, args)?;
110        }
111    }
112    Ok(())
113}
114
115pub fn exact_sum_sweep_level<E: Endianness, L: Level>(
116    global_args: GlobalArgs,
117    args: CliArgs,
118) -> Result<()> {
119    let graph = BvGraph::with_basename(&args.basename).load()?;
120
121    let thread_pool = crate::get_thread_pool(args.num_threads.num_threads);
122    let mut pl = concurrent_progress_logger![];
123    if let Some(log_interval) = global_args.log_interval {
124        pl.log_interval(log_interval);
125    }
126
127    if args.symmetric {
128        let _out = thread_pool.install(|| L::run_symm(graph, &mut pl));
129    } else {
130        let transpose_path = args
131            .transposed
132            .as_ref()
133            .expect("You have to pass the transposed graph if the graph is not symmetric.");
134        let transpose = BvGraph::with_basename(transpose_path).load()?;
135        let _out = thread_pool.install(|| L::run(graph, transpose, None, &mut pl));
136    }
137
138    todo!("print out and serialize the eccentricities if present");
139}