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::{ensure, Result};
9use clap::{Parser, ValueEnum};
10use dsi_bitstream::prelude::*;
11use dsi_progress_logger::{concurrent_progress_logger, ProgressLog};
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!(args.symmetric || args.transposed.is_some(), "You have to either pass --transposed with with the basename of the transposed graph or --symm if the graph is symmetric.");
61    ensure!(
62        !(args.symmetric && args.transposed.is_some()),
63        "--transposed is needed only if the graph is not symmetric."
64    );
65    ensure!(args.forward.is_none() || matches!(args.level, LevelArg::All | LevelArg::AllForward), "You cannot only pass --forward with --level=all or --level=all-forward as the forward eccentricities won't be computed otherwise.");
66    ensure!(!(args.forward.is_none() && matches!(args.level, LevelArg::All | LevelArg::AllForward)), "If --level=all or --level=all-forward, you should pass --forward to store the computed eccentricities.");
67    ensure!(!(args.backward.is_some() && args.level != LevelArg::All), "You cannot only pass --backward with --level=all as the backward eccentricities won't be computed otherwise.");
68    ensure!(!(args.level == LevelArg::All && args.symmetric && args.backward.is_some()), "You cannot pass --backward with --symm and --level=all as the eccentricities of a symmetric graph are the same in both directions.");
69
70    match get_endianness(&args.basename)?.as_str() {
71        #[cfg(feature = "be_bins")]
72        BE::NAME => exact_sum_sweep::<BE>(global_args, args),
73        #[cfg(feature = "le_bins")]
74        LE::NAME => exact_sum_sweep::<LE>(global_args, args),
75        e => panic!("Unknown endianness: {}", e),
76    }
77}
78
79pub fn exact_sum_sweep<E: Endianness>(global_args: GlobalArgs, args: CliArgs) -> Result<()> {
80    match args.level {
81        LevelArg::Radius => {
82            exact_sum_sweep_level::<E, Radius>(global_args, args)?;
83        }
84        LevelArg::Diameter => {
85            exact_sum_sweep_level::<E, Diameter>(global_args, args)?;
86        }
87        LevelArg::RadiusDiameter => {
88            exact_sum_sweep_level::<E, RadiusDiameter>(global_args, args)?;
89        }
90        LevelArg::AllForward => {
91            exact_sum_sweep_level::<E, AllForward>(global_args, args)?;
92        }
93        LevelArg::All => {
94            exact_sum_sweep_level::<E, All>(global_args, args)?;
95        }
96    }
97    Ok(())
98}
99
100pub fn exact_sum_sweep_level<E: Endianness, L: Level>(
101    global_args: GlobalArgs,
102    args: CliArgs,
103) -> Result<()> {
104    let graph = BvGraph::with_basename(&args.basename).load()?;
105
106    let thread_pool = crate::get_thread_pool(args.num_threads.num_threads);
107    let mut pl = concurrent_progress_logger![];
108    if let Some(log_interval) = global_args.log_interval {
109        pl.log_interval(log_interval);
110    }
111
112    if args.symmetric {
113        let _out = L::run_symm(graph, &thread_pool, &mut pl);
114    } else {
115        let transpose_path = args
116            .transposed
117            .as_ref()
118            .expect("You have to pass the transposed graph if the graph is not symmetric.");
119        let transpose = BvGraph::with_basename(transpose_path).load()?;
120        let _out = L::run(graph, transpose, None, &thread_pool, &mut pl);
121    }
122
123    todo!("print out and serialize the eccentricities if present");
124}