use std::collections::HashMap;
use dsi_progress_logger::{ProgressLog, progress_logger};
use rapidhash::RapidHashMap;
use swh_graph::graph::*;
use swh_graph::graph::{NodeId, SwhForwardGraph};
pub trait MapReducer {
type Label: Clone;
type Error;
fn map(&mut self, node: NodeId) -> Result<Option<Self::Label>, Self::Error>;
fn reduce<'a, I: Iterator<Item = &'a Self::Label>>(
&mut self,
first_label: Self::Label,
other_labels: I,
) -> Result<Option<Self::Label>, Self::Error>
where
Self::Label: 'a;
fn map_reduce(
&mut self,
node: NodeId,
successors_label: Self::Label,
) -> Result<Option<Self::Label>, Self::Error> {
match self.map(node)? {
Some(own_label) => self.reduce(own_label, [&successors_label].into_iter()),
None => Ok(Some(successors_label)),
}
}
fn on_node_traversed(
&mut self,
_node: NodeId,
_label: Option<&Self::Label>,
) -> Result<(), Self::Error> {
Ok(())
}
}
pub struct MapReduce<G: SwhForwardGraph + SwhBackwardGraph, MR: MapReducer> {
graph: G,
num_nodes: usize,
pub map_reducer: MR,
}
impl<G: SwhForwardGraph + SwhBackwardGraph, MR: MapReducer> MapReduce<G, MR> {
pub fn new(graph: G, map_reducer: MR) -> Self {
MapReduce {
num_nodes: graph.actual_num_nodes().unwrap_or(graph.num_nodes()),
graph,
map_reducer,
}
}
pub fn with_num_nodes(mut self, num_nodes: usize) -> Self {
self.num_nodes = num_nodes;
self
}
pub fn run_in_topological_order(
&mut self,
nodes: impl Iterator<Item = NodeId>,
) -> Result<(), MR::Error> {
let mut labels: RapidHashMap<NodeId, MR::Label> = RapidHashMap::default();
let mut pending_dependents = HashMap::<NodeId, usize>::new();
let mut pl = progress_logger!(
display_memory = true,
item_name = "node",
local_speed = true,
expected_updates = Some(self.num_nodes),
);
pl.start("Traversing graph");
for node in nodes {
pl.light_update();
let num_dependents = self.graph.indegree(node);
if num_dependents > 0 {
pending_dependents.insert(node, num_dependents);
}
let mut dependencies = self.graph.successors(node).into_iter();
let mut merged_label: Option<MR::Label> = None;
while let Some(first_dependency) = dependencies.next() {
let first_dependency_label =
if pending_dependents.get(&first_dependency) == Some(&1) {
pending_dependents.remove(&first_dependency);
labels.remove(&first_dependency)
} else {
*pending_dependents.get_mut(&first_dependency).unwrap() -= 1;
labels.get(&first_dependency).cloned()
};
if let Some(first_dependency_label) = first_dependency_label {
merged_label = self.map_reducer.reduce(
first_dependency_label,
dependencies.flat_map(|dep|
labels.get(&dep)),
)?;
break;
}
}
let label = match merged_label {
Some(merged_label) => self.map_reducer.map_reduce(node, merged_label)?,
None => self.map_reducer.map(node)?,
};
self.map_reducer.on_node_traversed(node, label.as_ref())?;
if num_dependents > 0 {
if let Some(label) = label {
let previous_label = labels.insert(node, label);
assert!(previous_label.is_none(), "{node} was labeled twice");
}
} else {
assert!(!labels.contains_key(&node), "{node} was already labeled");
}
}
pl.done();
Ok(())
}
}