Skip to content
Merged
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
61 changes: 61 additions & 0 deletions src/lax/hypergraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,67 @@ impl<O, A> Hypergraph<O, A> {
self.with_edges(|edges| edges.into_iter().map(f).collect())
.unwrap()
}

/// Delete the specified nodes, remapping remaining node indices in adjacency and quotient.
///
/// Out-of-bounds node ids are ignored.
pub fn delete_nodes(&mut self, node_ids: &[NodeId]) {
Comment thread
mstn marked this conversation as resolved.
if node_ids.is_empty() {
return;
}

let node_count = self.nodes.len();
let mut remove = vec![false; node_count];
let mut any_removed = false;
let mut remove_count = 0usize;
for node_id in node_ids {
if node_id.0 < node_count {
if !remove[node_id.0] {
remove[node_id.0] = true;
any_removed = true;
remove_count += 1;
}
}
}

if !any_removed {
return;
}

let mut new_index = vec![None; node_count];
let mut nodes = Vec::with_capacity(node_count - remove_count);
for (i, node) in self.nodes.drain(..).enumerate() {
if !remove[i] {
let next = nodes.len();
new_index[i] = Some(next);
nodes.push(node);
}
}
self.nodes = nodes;

for edge in &mut self.adjacency {
edge.sources = edge
.sources
.iter()
.filter_map(|node| new_index[node.0].map(NodeId))
.collect();
edge.targets = edge
.targets
.iter()
.filter_map(|node| new_index[node.0].map(NodeId))
.collect();
}

let mut quotient_left = Vec::with_capacity(self.quotient.0.len());
let mut quotient_right = Vec::with_capacity(self.quotient.1.len());
for (v, w) in self.quotient.0.iter().zip(self.quotient.1.iter()) {
if let (Some(v_new), Some(w_new)) = (new_index[v.0], new_index[w.0]) {
quotient_left.push(NodeId(v_new));
quotient_right.push(NodeId(w_new));
}
}
self.quotient = (quotient_left, quotient_right);
}
}

impl<O: Clone + PartialEq, A: Clone> Hypergraph<O, A> {
Expand Down
84 changes: 84 additions & 0 deletions tests/lax/hypergraph.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use open_hypergraphs::lax::{Hyperedge, Hypergraph, NodeId};

#[test]
fn test_delete_nodes_remap_and_quotient() {
let mut h = Hypergraph::empty();
h.nodes = vec![10, 20, 30, 40];
h.edges = vec![0];
h.adjacency = vec![Hyperedge {
sources: vec![NodeId(0), NodeId(1), NodeId(2), NodeId(3)],
targets: vec![NodeId(3), NodeId(1)],
}];
h.quotient = (vec![NodeId(0), NodeId(1)], vec![NodeId(2), NodeId(3)]);

h.delete_nodes(&[NodeId(1), NodeId(3)]);

assert_eq!(h.nodes, vec![10, 30]);
assert_eq!(h.adjacency.len(), 1);
assert_eq!(h.adjacency[0].sources, vec![NodeId(0), NodeId(1)]);
assert!(h.adjacency[0].targets.is_empty());
assert_eq!(h.quotient.0, vec![NodeId(0)]);
assert_eq!(h.quotient.1, vec![NodeId(1)]);
}

#[test]
fn test_delete_nodes_empty_input_no_change() {
let mut h = Hypergraph::empty();
h.nodes = vec![1, 2];
h.edges = vec![7];
h.adjacency = vec![Hyperedge {
sources: vec![NodeId(0)],
targets: vec![NodeId(1)],
}];
h.quotient = (vec![NodeId(0)], vec![NodeId(1)]);

h.delete_nodes(&[]);

assert_eq!(h.nodes, vec![1, 2]);
assert_eq!(h.edges, vec![7]);
assert_eq!(h.adjacency[0].sources, vec![NodeId(0)]);
assert_eq!(h.adjacency[0].targets, vec![NodeId(1)]);
assert_eq!(h.quotient.0, vec![NodeId(0)]);
assert_eq!(h.quotient.1, vec![NodeId(1)]);
}

#[test]
fn test_delete_nodes_all_nodes_removed() {
let mut h = Hypergraph::empty();
h.nodes = vec![1, 2, 3];
h.edges = vec![0];
h.adjacency = vec![Hyperedge {
sources: vec![NodeId(0), NodeId(2)],
targets: vec![NodeId(1)],
}];
h.quotient = (vec![NodeId(0), NodeId(1)], vec![NodeId(2), NodeId(0)]);

h.delete_nodes(&[NodeId(0), NodeId(1), NodeId(2)]);

assert!(h.nodes.is_empty());
assert_eq!(h.edges, vec![0]);
assert!(h.adjacency[0].sources.is_empty());
assert!(h.adjacency[0].targets.is_empty());
assert!(h.quotient.0.is_empty());
assert!(h.quotient.1.is_empty());
}

#[test]
fn test_delete_nodes_ignores_out_of_range() {
let mut h = Hypergraph::empty();
h.nodes = vec![5, 6];
h.edges = vec![1];
h.adjacency = vec![Hyperedge {
sources: vec![NodeId(0)],
targets: vec![NodeId(1)],
}];
h.quotient = (vec![NodeId(0)], vec![NodeId(1)]);

h.delete_nodes(&[NodeId(99)]);

assert_eq!(h.nodes, vec![5, 6]);
assert_eq!(h.adjacency[0].sources, vec![NodeId(0)]);
assert_eq!(h.adjacency[0].targets, vec![NodeId(1)]);
assert_eq!(h.quotient.0, vec![NodeId(0)]);
assert_eq!(h.quotient.1, vec![NodeId(1)]);
}
1 change: 1 addition & 0 deletions tests/lax/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod eval;
pub mod hypergraph;