Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.vscode
Cargo.lock
# Generated by Cargo
# will have compiled files and executables
debug
Expand Down
1 change: 1 addition & 0 deletions pathrex-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ fn regenerate_bindings(graphblas_install: &Path) {
.allowlist_item("GrB_Info")
.allowlist_function("GrB_Matrix_new")
.allowlist_function("GrB_Matrix_nvals")
.allowlist_function("GrB_Matrix_nrows")
.allowlist_function("GrB_Matrix_dup")
.allowlist_function("GrB_Matrix_free")
.allowlist_function("GrB_Matrix_extractElement_BOOL")
Expand Down
3 changes: 3 additions & 0 deletions pathrex-sys/src/lagraph_sys_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ unsafe extern "C" {
unsafe extern "C" {
pub fn GrB_Matrix_dup(C: *mut GrB_Matrix, A: GrB_Matrix) -> GrB_Info;
}
unsafe extern "C" {
pub fn GrB_Matrix_nrows(nrows: *mut GrB_Index, A: GrB_Matrix) -> GrB_Info;
}
unsafe extern "C" {
pub fn GrB_Matrix_nvals(nvals: *mut GrB_Index, A: GrB_Matrix) -> GrB_Info;
}
Expand Down
2 changes: 2 additions & 0 deletions pathrex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ serde_json = { version = "1", optional = true }
chrono = { version = "0.4", features = ["serde"], optional = true }
criterion = { version = "0.5", optional = true }
tempfile = { version = "3", optional = true }
rand = "0.10.2"

[features]
default = []
bench = ["clap", "serde", "serde_json", "chrono", "criterion", "tempfile"]

[dev-dependencies]
tempfile = "3"
expect-test = "1.5.1"

[[bin]]
name = "pathrex"
Expand Down
44 changes: 41 additions & 3 deletions pathrex/src/bin/pathrex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
//! cargo run --release --bin pathrex --features bench -- bench \
//! --graph tests/testdata/mm_graph \
//! --queries tests/testdata/cases/any-any/queries.txt \
//! --algo nfa rpqmatrix \
//! --algo nfarpq rpqmatrix \
//! --rpqmatrix-optimizer cardinality \
//! --output results.json
//! ```

Expand All @@ -27,7 +28,9 @@ use chrono::Utc;
use clap::Parser;
use thiserror::Error;

use pathrex::cli::args::{BenchArgs, Cli, Commands, QueryArgs};
use pathrex::cli::args::{
Algo, BenchArgs, Cli, Commands, CommonArgs, GraphFormat, QueryArgs, RpqMatrixOptimizer,
};
use pathrex::cli::bench::BenchError;
use pathrex::cli::checkpoint::{Checkpoint, CheckpointError, Checkpointer};
use pathrex::cli::dispatch::{dispatch_bench, dispatch_query};
Expand Down Expand Up @@ -55,6 +58,8 @@ enum MainError {
#[source]
source: std::io::Error,
},
#[error("invalid arguments: {0}")]
InvalidArgs(String),
}

fn main() {
Expand All @@ -78,6 +83,26 @@ fn run() -> Result<(), MainError> {
}
}

fn validate_common_args(common: &CommonArgs) -> Result<(), MainError> {
if common.rpqmatrix_optimizer == RpqMatrixOptimizer::None {
return Ok(());
}

if !common.algo.contains(&Algo::Rpqmatrix) {
return Err(MainError::InvalidArgs(
"--rpqmatrix-optimizer can only be used when --algo includes rpqmatrix".to_string(),
));
}

if common.format != GraphFormat::Mm {
return Err(MainError::InvalidArgs(
"--rpqmatrix-optimizer can only be used with --format mm".to_string(),
));
}

Ok(())
}

fn load_query_file(path: &str, base_iri: Option<&str>) -> Result<Vec<LoadedQuery>, MainError> {
load_queries(Path::new(path), base_iri).map_err(|e| MainError::Queries {
path: path.to_string(),
Expand All @@ -87,12 +112,14 @@ fn load_query_file(path: &str, base_iri: Option<&str>) -> Result<Vec<LoadedQuery

fn run_query_cmd(args: QueryArgs) -> Result<(), MainError> {
let common = &args.common;
validate_common_args(common)?;

eprintln!("=== pathrex query ===");
eprintln!("Graph: {}", common.graph);
eprintln!("Format: {}", common.format);
eprintln!("Queries: {}", common.queries);
eprintln!("Algos: {:?}", common.algo);
eprintln!("RPQMatrix optimizer: {}", common.rpqmatrix_optimizer);
eprintln!();

eprintln!("[1/2] Loading graph...");
Expand Down Expand Up @@ -127,6 +154,7 @@ fn run_query_cmd(args: QueryArgs) -> Result<(), MainError> {
graph_path: common.graph.clone(),
graph_format: common.format.to_string(),
queries_file: common.queries.clone(),
rpqmatrix_optimizer: Some(common.rpqmatrix_optimizer.to_string()),
base_iri: common.base_iri.clone(),
num_nodes: graph.num_nodes(),
num_labels: graph.num_labels(),
Expand All @@ -152,7 +180,12 @@ fn build_checkpointer(args: &BenchArgs, queries_len: usize) -> Result<Checkpoint
if args.resume {
match Checkpoint::load(&path)? {
Some(cp) => {
cp.validate(&common.graph, &common.queries, &common.algo)?;
cp.validate(
&common.graph,
&common.queries,
&common.algo,
common.rpqmatrix_optimizer,
)?;
let cper = Checkpointer::with_inner(cp, path);
eprintln!(
" resuming: {}/{} queries fully done",
Expand All @@ -167,6 +200,7 @@ fn build_checkpointer(args: &BenchArgs, queries_len: usize) -> Result<Checkpoint
&common.graph,
&common.queries,
&common.algo,
common.rpqmatrix_optimizer,
path,
))
}
Expand All @@ -176,19 +210,22 @@ fn build_checkpointer(args: &BenchArgs, queries_len: usize) -> Result<Checkpoint
&common.graph,
&common.queries,
&common.algo,
common.rpqmatrix_optimizer,
path,
))
}
}

fn run_bench_cmd(args: BenchArgs) -> Result<(), MainError> {
let common = &args.common;
validate_common_args(common)?;

eprintln!("=== pathrex bench ===");
eprintln!("Graph: {}", common.graph);
eprintln!("Format: {}", common.format);
eprintln!("Queries: {}", common.queries);
eprintln!("Algos: {:?}", common.algo);
eprintln!("RPQMatrix optimizer: {}", common.rpqmatrix_optimizer);
eprintln!("Output: {}", args.output);
eprintln!();

Expand Down Expand Up @@ -223,6 +260,7 @@ fn run_bench_cmd(args: BenchArgs) -> Result<(), MainError> {
graph_format: common.format.to_string(),
queries_file: common.queries.clone(),
base_iri: common.base_iri.clone(),
rpqmatrix_optimizer: Some(common.rpqmatrix_optimizer.to_string()),
num_nodes: graph.num_nodes(),
num_labels: graph.num_labels(),
sample_size: args.sample_size,
Expand Down
47 changes: 47 additions & 0 deletions pathrex/src/cli/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

use clap::{Args, Parser, Subcommand, ValueEnum};

use crate::rpq::rpqmatrix::OptimizationStrategy;

/// Top-level CLI for pathrex.
#[derive(Parser, Debug)]
#[command(
Expand Down Expand Up @@ -62,6 +64,15 @@ pub struct CommonArgs {
/// Algorithms to use.
#[arg(short = 'a', long, value_enum, num_args = 1.., required = true)]
pub algo: Vec<Algo>,

/// Optimizer type (only for the RPQMatrix algorithm and MatrixMarket source graph).
#[arg(
short = 'p',
long = "rpqmatrix-optimizer",
value_enum,
default_value_t = RpqMatrixOptimizer::None
)]
pub rpqmatrix_optimizer: RpqMatrixOptimizer,
}

/// Arguments for the `query` subcommand.
Expand Down Expand Up @@ -156,6 +167,42 @@ impl std::fmt::Display for GraphFormat {
}
}

/// Optimizer types.
/// Only for RPQMatrix algorithm and MatrixMarket source graph.
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, serde::Serialize, serde::Deserialize)]
#[value(rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum RpqMatrixOptimizer {
/// Optimizer based on cardinality of source matrices.
Cardinality,
/// Without any optimizations.
None,
}

impl Default for RpqMatrixOptimizer {
fn default() -> Self {
Self::None
}
}

impl std::fmt::Display for RpqMatrixOptimizer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RpqMatrixOptimizer::Cardinality => write!(f, "cardinality"),
RpqMatrixOptimizer::None => write!(f, "none"),
}
}
}

impl From<RpqMatrixOptimizer> for OptimizationStrategy {
fn from(value: RpqMatrixOptimizer) -> Self {
match value {
RpqMatrixOptimizer::None => OptimizationStrategy::NoOpt,
RpqMatrixOptimizer::Cardinality => OptimizationStrategy::Cardinality,
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
29 changes: 25 additions & 4 deletions pathrex/src/cli/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use thiserror::Error;

use super::args::Algo;
use super::args::{Algo, RpqMatrixOptimizer};

/// Persistent checkpoint state written to disk as JSON.
#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -25,6 +25,8 @@ pub struct Checkpoint {
pub graph_path: String,
pub queries_file: String,
pub algorithms: Vec<Algo>,
#[serde(default)]
pub rpqmatrix_optimizer: RpqMatrixOptimizer,
pub completed: Vec<QueryCompletion>,
}

Expand All @@ -37,12 +39,18 @@ pub struct QueryCompletion {

impl Checkpoint {
/// Create a fresh checkpoint for a new benchmark run.
pub fn new(graph_path: &str, queries_file: &str, algorithms: &[Algo]) -> Self {
pub fn new(
graph_path: &str,
queries_file: &str,
algorithms: &[Algo],
rpqmatrix_optimizer: RpqMatrixOptimizer,
) -> Self {
Self {
version: 1,
graph_path: graph_path.to_string(),
queries_file: queries_file.to_string(),
algorithms: algorithms.to_vec(),
rpqmatrix_optimizer,
completed: Vec::new(),
}
}
Expand All @@ -65,6 +73,7 @@ impl Checkpoint {
graph_path: &str,
queries_file: &str,
algorithms: &[Algo],
rpqmatrix_optimizer: RpqMatrixOptimizer,
) -> Result<(), CheckpointError> {
if self.graph_path != graph_path {
return Err(CheckpointError::Mismatch(format!(
Expand All @@ -86,6 +95,12 @@ impl Checkpoint {
self.algorithms, algorithms
)));
}
if self.rpqmatrix_optimizer != rpqmatrix_optimizer {
return Err(CheckpointError::Mismatch(format!(
"rpqmatrix_optimizer: checkpoint has '{}', current is '{}'",
self.rpqmatrix_optimizer, rpqmatrix_optimizer
)));
}
Ok(())
}

Expand Down Expand Up @@ -145,9 +160,15 @@ pub struct Checkpointer {

impl Checkpointer {
/// Create a new checkpointer with no completions.
pub fn fresh(graph_path: &str, queries_file: &str, algorithms: &[Algo], path: PathBuf) -> Self {
pub fn fresh(
graph_path: &str,
queries_file: &str,
algorithms: &[Algo],
rpqmatrix_optimizer: RpqMatrixOptimizer,
path: PathBuf,
) -> Self {
Self {
inner: Checkpoint::new(graph_path, queries_file, algorithms),
inner: Checkpoint::new(graph_path, queries_file, algorithms, rpqmatrix_optimizer),
path,
}
}
Expand Down
12 changes: 8 additions & 4 deletions pathrex/src/cli/dispatch.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Typed dispatch from CLI algorithm choices to concrete evaluators.

use crate::cli::args::{Algo, BenchArgs, QueryArgs};
use crate::cli::bench::error::BenchError;
use crate::cli::bench::runner::run_bench_for_evaluator;
Expand All @@ -8,6 +7,7 @@ use crate::cli::loader::LoadedQuery;
use crate::cli::output::QueryResult;
use crate::cli::query::run_query_for_evaluator;
use crate::graph::InMemoryGraph;

use crate::rpq::nfarpq::NfaRpqEvaluator;
use crate::rpq::rpqmatrix::RpqMatrixEvaluator;

Expand All @@ -30,12 +30,16 @@ pub fn dispatch_query(
queries: &[LoadedQuery],
) -> Vec<QueryResult> {
let mut all = Vec::new();

for algo in &args.common.algo {
let name = algo.to_string();
let per_algo = match algo {
Algo::NfaRpq => run_query_for_evaluator(&name, NfaRpqEvaluator, graph, queries),
Algo::Rpqmatrix => run_query_for_evaluator(&name, RpqMatrixEvaluator, graph, queries),
Algo::Rpqmatrix => run_query_for_evaluator(
&name,
RpqMatrixEvaluator::optimized(args.common.rpqmatrix_optimizer.into()),
graph,
queries,
),
};
merge_results(&mut all, per_algo);
}
Expand Down Expand Up @@ -67,7 +71,7 @@ pub fn dispatch_bench(
args,
algo,
&name,
RpqMatrixEvaluator,
RpqMatrixEvaluator::optimized(args.common.rpqmatrix_optimizer.into()),
graph,
queries,
checkpointer,
Expand Down
2 changes: 1 addition & 1 deletion pathrex/src/cli/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ pub fn load_graph(
})
}
GraphFormat::Rdf => {
let rdf = Rdf::from_path(graph_path).unwrap();
let rdf = Rdf::from_path(graph_path).unwrap(); // TODO: handle panic
Graph::<InMemory>::try_from(rdf).map_err(|e| GraphLoadError::Build {
path: graph_path.to_string(),
source: e,
Expand Down
2 changes: 2 additions & 0 deletions pathrex/src/cli/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub struct QueryMetadata {
pub graph_path: String,
pub graph_format: String,
pub queries_file: String,
pub rpqmatrix_optimizer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_iri: Option<String>,
pub num_nodes: usize,
Expand All @@ -116,6 +117,7 @@ pub struct BenchMetadata {
pub graph_path: String,
pub graph_format: String,
pub queries_file: String,
pub rpqmatrix_optimizer: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base_iri: Option<String>,
pub num_nodes: usize,
Expand Down
Loading
Loading