dag: implement DAG algorithms on NameDag

Summary: Those just delegate to IdDag for the actual calculation.

Reviewed By: sfilipco

Differential Revision: D20020522

fbshipit-source-id: 272828c520097c993ab50dac6ecc94dc370c8e8b
This commit is contained in:
Jun Wu 2020-02-28 16:30:31 -08:00 committed by Facebook Github Bot
parent b88da34fb0
commit bc9f72ccf3
2 changed files with 183 additions and 0 deletions

View File

@ -16,6 +16,9 @@ use crate::iddag::SyncableIdDag;
use crate::idmap::IdMap;
use crate::idmap::IdMapLike;
use crate::idmap::SyncableIdMap;
use crate::nameset::dag::DagSet;
use crate::nameset::NameSet;
use crate::spanset::SpanSet;
use anyhow::{anyhow, bail, ensure, Result};
use indexedlog::multi;
use std::collections::{HashMap, HashSet};
@ -258,6 +261,137 @@ impl NameDag {
// Before those APIs, LowLevelAccess might have to be used by callsites.
}
// Dag operations. Those are just simple wrappers around [`IdDag`].
// See [`IdDag`] for the actual implementations of these algorithms.
impl NameDag {
/// Returns a [`SpanSet`] that covers all vertexes tracked by this DAG.
pub fn all(&self) -> Result<NameSet> {
let spans = self.dag.all()?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates all ancestors reachable from any name from the given set.
pub fn ancestors(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.ancestors(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates parents of the given set.
///
/// Note: Parent order is not preserved. Use [`NameDag::parent_names`]
/// to preserve order.
pub fn parents(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.parents(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates the n-th first ancestor.
pub fn first_ancestor_nth(&self, name: VertexName, n: u64) -> Result<VertexName> {
let id = self.map.vertex_id(name)?;
let id = self.dag.first_ancestor_nth(id, n)?;
self.map.vertex_name(id)
}
/// Calculates heads of the given set.
pub fn heads(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.heads(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates children of the given set.
pub fn children(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.children(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates roots of the given set.
pub fn roots(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.roots(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates one "greatest common ancestor" of the given set.
///
/// If there are no common ancestors, return None.
/// If there are multiple greatest common ancestors, pick one arbitrarily.
/// Use `gca_all` to get all of them.
pub fn gca_one(&self, set: NameSet) -> Result<Option<VertexName>> {
match self.dag.gca_one(self.to_span_set(set)?)? {
None => Ok(None),
Some(id) => Ok(Some(self.map.vertex_name(id)?)),
}
}
/// Calculates all "greatest common ancestor"s of the given set.
/// `gca_one` is faster if an arbitrary answer is ok.
pub fn gca_all(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.gca_all(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates all common ancestors of the given set.
pub fn common_ancestors(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.common_ancestors(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Tests if `ancestor` is an ancestor of `descendant`.
pub fn is_ancestor(&self, ancestor: VertexName, descendant: VertexName) -> Result<bool> {
let ancestor_id = self.map.vertex_id(ancestor)?;
let descendant_id = self.map.vertex_id(descendant)?;
self.dag.is_ancestor(ancestor_id, descendant_id)
}
/// Calculates "heads" of the ancestors of the given set. That is,
/// Find Y, which is the smallest subset of set X, where `ancestors(Y)` is
/// `ancestors(X)`.
///
/// This is faster than calculating `heads(ancestors(set))`.
///
/// This is different from `heads`. In case set contains X and Y, and Y is
/// an ancestor of X, but not the immediate ancestor, `heads` will include
/// Y while this function won't.
pub fn heads_ancestors(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.heads_ancestors(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates the "dag range" - vertexes reachable from both sides.
pub fn range(&self, roots: NameSet, heads: NameSet) -> Result<NameSet> {
let roots = self.to_span_set(roots)?;
let heads = self.to_span_set(heads)?;
let spans = self.dag.range(roots, heads)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Calculates the descendants of the given set.
pub fn descendants(&self, set: NameSet) -> Result<NameSet> {
let spans = self.dag.descendants(self.to_span_set(set)?)?;
Ok(NameSet::from_spans_idmap(spans, self.arc_map.clone()))
}
/// Converts [`NameSet`] to [`SpanSet`].
fn to_span_set(&self, set: NameSet) -> Result<SpanSet> {
// Fast path: extract SpanSet directly.
if let Some(set) = set.as_any().downcast_ref::<DagSet>() {
if Arc::ptr_eq(&set.map, &self.arc_map) {
return Ok(set.spans.clone());
}
}
// Slow path: iterate through the set and convert it to a non-lazy
// SpanSet.
let mut spans = SpanSet::empty();
for name in set.iter()? {
let name = name?;
let id = self.map.vertex_id(name)?;
spans.push(id);
}
Ok(spans)
}
}
/// Export non-master DAG as parent_names_func on HashMap.
///
/// This can be expensive. It is expected to be either called infrequently,

View File

@ -9,6 +9,7 @@ use crate::id::{Group, Id, VertexName};
use crate::iddag::FirstAncestorConstraint;
use crate::iddag::IdDag;
use crate::idmap::IdMap;
use crate::nameset::NameSet;
use crate::protocol::{Process, RequestLocationToName, RequestNameToLocation};
use crate::spanset::SpanSet;
use crate::NameDag;
@ -47,6 +48,54 @@ static ASCII_DAG5: &str = r#"
\ \ \
A---C---E---G"#;
#[test]
fn test_namedag() -> Result<()> {
let ascii = r#"
J K
/|\|\
G H I H
|/|/
E F
/|/|\
A B C D"#;
let result = build_segments(ascii, "J K", 2);
let dag = &result.name_dag;
fn nameset(names: &str) -> NameSet {
let names: Vec<VertexName> = names
.split_whitespace()
.map(|n| VertexName::copy_from(n.as_bytes()))
.collect();
NameSet::from_static_names(names)
}
fn expand(set: NameSet) -> String {
set.iter()
.unwrap()
.map(|n| String::from_utf8_lossy(n.unwrap().as_ref()).to_string())
.collect::<Vec<String>>()
.join(" ")
}
assert_eq!(expand(dag.all()?), "K J I H F D C G E B A");
assert_eq!(expand(dag.ancestors(nameset("H I"))?), "I H F D C E B A");
assert_eq!(expand(dag.parents(nameset("H I E"))?), "F E B A");
assert_eq!(dag.first_ancestor_nth("H".into(), 2)?, "A".into());
assert_eq!(expand(dag.heads(nameset("E H F K I D"))?), "K");
assert_eq!(expand(dag.children(nameset("E F I"))?), "K J I H G");
assert_eq!(expand(dag.roots(nameset("E G H J I K D"))?), "I D E");
assert_eq!(dag.gca_one(nameset("J K"))?, Some("I".into()));
assert_eq!(expand(dag.gca_all(nameset("J K"))?), "I H");
assert_eq!(expand(dag.common_ancestors(nameset("G H"))?), "E B A");
assert!(dag.is_ancestor("B".into(), "K".into())?);
assert!(!dag.is_ancestor("K".into(), "B".into())?);
assert_eq!(expand(dag.heads_ancestors(nameset("A E F D G"))?), "F G");
assert_eq!(expand(dag.range(nameset("A"), nameset("K"))?), "K H E A");
assert_eq!(expand(dag.descendants(nameset("F E"))?), "K J I H F G E");
Ok(())
}
#[test]
fn test_protocols() {
let mut built = build_segments(ASCII_DAG1, "A C E L", 3);