Skip to content

Commit

Permalink
feat!:ConvexChecker is a trait
Browse files Browse the repository at this point in the history
  • Loading branch information
lmondada committed Dec 13, 2023
1 parent a577492 commit 47459e2
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 41 deletions.
8 changes: 4 additions & 4 deletions benches/benchmarks/convex.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use criterion::{black_box, criterion_group, AxisScale, BenchmarkId, Criterion, PlotConfiguration};
use itertools::Itertools;
use portgraph::{algorithms::ConvexChecker, PortView};
use portgraph::{algorithms::TopoConvexChecker, PortView};

use super::generators::make_two_track_dag;

Expand All @@ -14,7 +14,7 @@ fn bench_convex_construction(c: &mut Criterion) {
&size,
|b, size| {
let graph = make_two_track_dag(*size);
b.iter(|| black_box(ConvexChecker::new(&graph)))
b.iter(|| black_box(TopoConvexChecker::new(&graph)))
},
);
}
Expand All @@ -29,7 +29,7 @@ fn bench_convex_full(c: &mut Criterion) {

for size in [100, 1_000, 10_000] {
let graph = make_two_track_dag(size);
let checker = ConvexChecker::new(&graph);
let checker = TopoConvexChecker::new(&graph);
g.bench_with_input(
BenchmarkId::new("check_convexity_full", size),
&size,
Expand All @@ -47,7 +47,7 @@ fn bench_convex_sparse(c: &mut Criterion) {
for size in [100usize, 1_000, 5_000] {
let graph_size = size.pow(2);
let graph = make_two_track_dag(graph_size);
let checker = ConvexChecker::new(&graph);
let checker = TopoConvexChecker::new(&graph);
let nodes = graph.nodes_iter().step_by(graph_size / size).collect_vec();
g.bench_with_input(
BenchmarkId::new("check_convexity_sparse", size),
Expand Down
2 changes: 1 addition & 1 deletion src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mod dominators;
mod post_order;
mod toposort;

pub use convex::ConvexChecker;
pub use convex::{TopoConvexChecker, ConvexChecker};
pub use dominators::{dominators, dominators_filtered, DominatorTree};
pub use post_order::{postorder, postorder_filtered, PostOrder};
pub use toposort::{toposort, toposort_filtered, TopoSort};
81 changes: 48 additions & 33 deletions src/algorithms/convex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,49 @@ use crate::{Direction, LinkView, NodeIndex, PortIndex, SecondaryMap, UnmanagedDe

use super::TopoSort;

/// A pre-computed data structure for fast convexity checking.
pub struct ConvexChecker<G> {
/// Pre-computed data for fast convexity checking.
pub trait ConvexChecker {
/// Whether a subgraph is convex.
///
/// A subgraph is convex if there is no path between two nodes of the
/// subgraph that has an edge outside of the subgraph.
///
/// Equivalently, we check the following two conditions:
/// - There is no node that is both in the past and in the future of
/// another node of the subgraph (convexity on induced subgraph),
/// - There is no edge from an output port to an input port.
///
/// This function requires mutable access to `self` because it uses a
/// temporary data structure within the object.
///
/// ## Arguments
///
/// - `nodes`: The nodes of the subgraph of `self.graph`,
/// - `inputs`: The input ports of the subgraph of `self.graph`. These must
/// be [`Direction::Incoming`] ports of a node in `nodes`,
/// - `outputs`: The output ports of the subgraph of `self.graph`. These
/// must be [`Direction::Outgoing`] ports of a node in `nodes`.
///
/// Any edge between two nodes of the subgraph that does not have an explicit
/// input or output port is considered within the subgraph.
fn is_convex(
&self,
nodes: impl IntoIterator<Item = NodeIndex>,
inputs: impl IntoIterator<Item = PortIndex>,
outputs: impl IntoIterator<Item = PortIndex>,
) -> bool;
}

/// Convexity checking using a pre-computed topological node order.
pub struct TopoConvexChecker<G> {
graph: G,
// The nodes in topological order
topsort_nodes: Vec<NodeIndex>,
// The index of a node in the topological order (the inverse of topsort_nodes)
topsort_ind: UnmanagedDenseMap<NodeIndex, usize>,
}

impl<G> ConvexChecker<G>
impl<G> TopoConvexChecker<G>
where
G: LinkView + Clone,
{
Expand Down Expand Up @@ -126,31 +159,13 @@ where
}
true
}
}

/// Whether a subgraph is convex.
///
/// A subgraph is convex if there is no path between two nodes of the
/// subgraph that has an edge outside of the subgraph.
///
/// Equivalently, we check the following two conditions:
/// - There is no node that is both in the past and in the future of
/// another node of the subgraph (convexity on induced subgraph),
/// - There is no edge from an output port to an input port.
///
/// This function requires mutable access to `self` because it uses a
/// temporary data structure within the object.
///
/// ## Arguments
///
/// - `nodes`: The nodes of the subgraph of `self.graph`,
/// - `inputs`: The input ports of the subgraph of `self.graph`. These must
/// be [`Direction::Incoming`] ports of a node in `nodes`,
/// - `outputs`: The output ports of the subgraph of `self.graph`. These
/// must be [`Direction::Outgoing`] ports of a node in `nodes`.
///
/// Any edge between two nodes of the subgraph that does not have an explicit
/// input or output port is considered within the subgraph.
pub fn is_convex(
impl<G> ConvexChecker for TopoConvexChecker<G>
where
G: LinkView + Clone,
{
fn is_convex(
&self,
nodes: impl IntoIterator<Item = NodeIndex>,
inputs: impl IntoIterator<Item = PortIndex>,
Expand All @@ -169,9 +184,9 @@ where

#[cfg(test)]
mod tests {
use crate::{LinkMut, NodeIndex, PortGraph, PortMut, PortView};
use crate::{LinkMut, NodeIndex, PortGraph, PortMut, PortView, algorithms::convex::ConvexChecker};

use super::ConvexChecker;
use super::TopoConvexChecker;

fn graph() -> (PortGraph, [NodeIndex; 7]) {
let mut g = PortGraph::new();
Expand Down Expand Up @@ -201,7 +216,7 @@ mod tests {
#[test]
fn induced_convexity_test() {
let (g, [i1, i2, i3, n1, n2, o1, o2]) = graph();
let checker = ConvexChecker::new(&g);
let checker = TopoConvexChecker::new(&g);

assert!(checker.is_node_convex([i1, i2, i3]));
assert!(checker.is_node_convex([i1, n2]));
Expand All @@ -216,7 +231,7 @@ mod tests {
#[test]
fn edge_convexity_test() {
let (g, [i1, i2, _, n1, n2, _, o2]) = graph();
let checker = ConvexChecker::new(&g);
let checker = TopoConvexChecker::new(&g);

assert!(checker.is_convex(
[i1, n2],
Expand Down Expand Up @@ -249,7 +264,7 @@ mod tests {
fn dangling_input() {
let mut g = PortGraph::new();
let n = g.add_node(1, 1);
let checker = ConvexChecker::new(&g);
let checker = TopoConvexChecker::new(&g);
assert!(checker.is_node_convex([n]));
}

Expand All @@ -258,7 +273,7 @@ mod tests {
let mut g = PortGraph::new();
let n = g.add_node(1, 1);
g.add_node(1, 1);
let checker = ConvexChecker::new(&g);
let checker = TopoConvexChecker::new(&g);
assert!(checker.is_node_convex([n]));
}
}
9 changes: 6 additions & 3 deletions src/view/subgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
use std::collections::BTreeSet;

use crate::{algorithms::ConvexChecker, Direction, LinkView, NodeIndex, PortIndex, PortView};
use crate::{
algorithms::{ConvexChecker, TopoConvexChecker},
Direction, LinkView, NodeIndex, PortIndex, PortView,
};

use super::filter::FilteredGraph;

Expand Down Expand Up @@ -96,12 +99,12 @@ where

/// Whether the subgraph is convex.
pub fn is_convex(&self) -> bool {
let checker = ConvexChecker::new(self.graph());
let checker = TopoConvexChecker::new(self.graph());
self.is_convex_with_checker(&checker)
}

/// Whether the subgraph is convex, using a pre-existing checker.
pub fn is_convex_with_checker(&self, checker: &ConvexChecker<G>) -> bool {
pub fn is_convex_with_checker(&self, checker: &impl ConvexChecker) -> bool {
checker.is_convex(
self.nodes_iter(),
self.context().inputs.iter().copied(),
Expand Down

0 comments on commit 47459e2

Please sign in to comment.