dijkstra's algorithm

main
Ziyang Hu 2 years ago
parent 0e3c9507e4
commit 582de562e9

@ -5,11 +5,11 @@
* [ ] graph algorithms * [ ] graph algorithms
* [x] bfs * [x] bfs
* [x] dfs * [x] dfs
* [ ] shortest path, SSSP * [x] shortest path
* [ ] A* * [ ] A*
* [ ] Yen's k-shortest * [ ] Yen's k-shortest
* [ ] all-pairs shortest path * [ ] all-pairs shortest path
* [ ] single-source shortest path * [x] single-source shortest path
* [ ] minimum spanning tree * [ ] minimum spanning tree
* [ ] random walking * [ ] random walking
* [x] degree centrality * [x] degree centrality

@ -83,17 +83,19 @@ impl MagicAlgoRuleArg {
pub(crate) fn convert_edge_to_weighted_graph( pub(crate) fn convert_edge_to_weighted_graph(
&self, &self,
undirected: bool, undirected: bool,
allow_negative_edge: bool, allow_negative_edges: bool,
tx: &SessionTx, tx: &SessionTx,
stores: &BTreeMap<MagicSymbol, DerivedRelStore>, stores: &BTreeMap<MagicSymbol, DerivedRelStore>,
) -> Result<( ) -> Result<(
Vec<Vec<(usize, f64)>>, Vec<Vec<(usize, f64)>>,
Vec<DataValue>, Vec<DataValue>,
BTreeMap<DataValue, usize>, BTreeMap<DataValue, usize>,
bool
)> { )> {
let mut graph: Vec<Vec<(usize, f64)>> = vec![]; let mut graph: Vec<Vec<(usize, f64)>> = vec![];
let mut indices: Vec<DataValue> = vec![]; let mut indices: Vec<DataValue> = vec![];
let mut inv_indices: BTreeMap<DataValue, usize> = Default::default(); let mut inv_indices: BTreeMap<DataValue, usize> = Default::default();
let mut has_neg_edge = false;
for tuple in self.iter(tx, stores)? { for tuple in self.iter(tx, stores)? {
let mut tuple = tuple?.0.into_iter(); let mut tuple = tuple?.0.into_iter();
@ -108,8 +110,11 @@ impl MagicAlgoRuleArg {
Some(d) => match d.get_float() { Some(d) => match d.get_float() {
Some(f) => { Some(f) => {
ensure!(f.is_finite(), "edge weight must be finite, got {}", f); ensure!(f.is_finite(), "edge weight must be finite, got {}", f);
if !allow_negative_edge { if f < 0. {
ensure!(f >= 0., "edge weight must be non-negative, got {}", f); if !allow_negative_edges {
bail!("edge weight must be non-negative, got {}", f);
}
has_neg_edge = true;
} }
f f
} }
@ -139,7 +144,7 @@ impl MagicAlgoRuleArg {
to_target.push((from_idx, weight)); to_target.push((from_idx, weight));
} }
} }
Ok((graph, indices, inv_indices)) Ok((graph, indices, inv_indices, has_neg_edge))
} }
pub(crate) fn convert_edge_to_graph( pub(crate) fn convert_edge_to_graph(
&self, &self,

@ -32,11 +32,6 @@ impl AlgoImpl for ShortestPathDijkstra {
anyhow!("'shortest_path_dijkstra' requires starting relation as second argument") anyhow!("'shortest_path_dijkstra' requires starting relation as second argument")
})?; })?;
let termination = rels.get(2); let termination = rels.get(2);
let allow_negative_edges = match opts.get(&Symbol::from("allow_negative_edges")) {
None => false,
Some(Expr::Const(DataValue::Bool(b))) => *b,
Some(v) => bail!("option 'allow_negative_edges' for 'shortest_path_dijkstra' requires a boolean, got {:?}", v)
};
let undirected = match opts.get(&Symbol::from("undirected")) { let undirected = match opts.get(&Symbol::from("undirected")) {
None => false, None => false,
Some(Expr::Const(DataValue::Bool(b))) => *b, Some(Expr::Const(DataValue::Bool(b))) => *b,
@ -46,8 +41,8 @@ impl AlgoImpl for ShortestPathDijkstra {
), ),
}; };
let (graph, indices, inv_indices) = let (graph, indices, inv_indices, _) =
edges.convert_edge_to_weighted_graph(undirected, allow_negative_edges, tx, stores)?; edges.convert_edge_to_weighted_graph(undirected, false, tx, stores)?;
let mut starting_nodes = BTreeSet::new(); let mut starting_nodes = BTreeSet::new();
for tuple in starting.iter(tx, stores)? { for tuple in starting.iter(tx, stores)? {
@ -79,7 +74,7 @@ impl AlgoImpl for ShortestPathDijkstra {
}; };
for start in starting_nodes { for start in starting_nodes {
let res = dijkstra(&graph, start, &termination_nodes); let res = dijkstra(&graph, start, &termination_nodes, ());
for (target, cost, path) in res { for (target, cost, path) in res {
let t = vec![ let t = vec![
indices[start].clone(), indices[start].clone(),
@ -118,10 +113,27 @@ impl Ord for HeapState {
impl Eq for HeapState {} impl Eq for HeapState {}
fn dijkstra( trait ForbiddenEdge {
fn is_forbidden(&self, src: usize, dst: usize) -> bool;
}
impl ForbiddenEdge for () {
fn is_forbidden(&self, _src: usize, _dst: usize) -> bool {
false
}
}
impl ForbiddenEdge for BTreeSet<(usize, usize)> {
fn is_forbidden(&self, src: usize, dst: usize) -> bool {
self.contains(&(src, dst))
}
}
fn dijkstra<T: ForbiddenEdge>(
edges: &[Vec<(usize, f64)>], edges: &[Vec<(usize, f64)>],
start: usize, start: usize,
maybe_goals: &Option<BTreeSet<usize>>, maybe_goals: &Option<BTreeSet<usize>>,
forbidden: T,
) -> Vec<(usize, f64, Vec<usize>)> { ) -> Vec<(usize, f64, Vec<usize>)> {
let mut distance = vec![f64::INFINITY; edges.len()]; let mut distance = vec![f64::INFINITY; edges.len()];
let mut heap = BinaryHeap::new(); let mut heap = BinaryHeap::new();
@ -139,6 +151,9 @@ fn dijkstra(
} }
for (nxt_node, path_weight) in &edges[state.node] { for (nxt_node, path_weight) in &edges[state.node] {
if forbidden.is_forbidden(state.node, *nxt_node) {
continue;
}
let nxt_cost = state.cost + *path_weight; let nxt_cost = state.cost + *path_weight;
if nxt_cost < distance[*nxt_node] { if nxt_cost < distance[*nxt_node] {
heap.push(HeapState { heap.push(HeapState {

@ -43,9 +43,6 @@ impl Symbol {
pub(crate) fn is_query_var(&self) -> bool { pub(crate) fn is_query_var(&self) -> bool {
self.0.starts_with('?') && self.0.len() > 1 self.0.starts_with('?') && self.0.len() > 1
} }
pub(crate) fn is_ignored_var(&self) -> bool {
self.0 == "?_"
}
pub(crate) fn validate_not_reserved(&self) -> Result<()> { pub(crate) fn validate_not_reserved(&self) -> Result<()> {
ensure!( ensure!(
!self.is_reserved(), !self.is_reserved(),

@ -4,26 +4,16 @@ use std::mem;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use crate::data::program::{NormalFormAtom, NormalFormRule}; use crate::data::program::{NormalFormAtom, NormalFormRule};
use crate::data::symb::Symbol;
impl NormalFormRule { impl NormalFormRule {
pub(crate) fn convert_to_well_ordered_rule(self) -> Result<Self> { pub(crate) fn convert_to_well_ordered_rule(self) -> Result<Self> {
let mut seen_variables = BTreeSet::default(); let mut seen_variables = BTreeSet::default();
let mut round_1_collected = vec![]; let mut round_1_collected = vec![];
let mut pending = vec![]; let mut pending = vec![];
let mut symb_count = 0;
let mut process_ignored_symbol = |symb: &mut Symbol| {
if symb.is_ignored_var() {
symb_count += 1;
let mut new_symb = Symbol::from(&format!("_{}", symb_count) as &str);
mem::swap(&mut new_symb, symb);
}
};
for atom in self.body { for atom in self.body {
match atom { match atom {
NormalFormAtom::Unification(mut u) => { NormalFormAtom::Unification(u) => {
process_ignored_symbol(&mut u.binding);
if u.is_const() { if u.is_const() {
seen_variables.insert(u.binding.clone()); seen_variables.insert(u.binding.clone());
round_1_collected.push(NormalFormAtom::Unification(u)); round_1_collected.push(NormalFormAtom::Unification(u));
@ -37,42 +27,30 @@ impl NormalFormRule {
} }
} }
} }
NormalFormAtom::AttrTriple(mut t) => { NormalFormAtom::AttrTriple(t) => {
process_ignored_symbol(&mut t.value);
process_ignored_symbol(&mut t.entity);
seen_variables.insert(t.value.clone()); seen_variables.insert(t.value.clone());
seen_variables.insert(t.entity.clone()); seen_variables.insert(t.entity.clone());
round_1_collected.push(NormalFormAtom::AttrTriple(t)); round_1_collected.push(NormalFormAtom::AttrTriple(t));
} }
NormalFormAtom::Rule(mut r) => { NormalFormAtom::Rule(mut r) => {
for arg in &mut r.args { for arg in &mut r.args {
process_ignored_symbol(arg);
seen_variables.insert(arg.clone()); seen_variables.insert(arg.clone());
} }
round_1_collected.push(NormalFormAtom::Rule(r)) round_1_collected.push(NormalFormAtom::Rule(r))
} }
NormalFormAtom::View(mut v) => { NormalFormAtom::View(mut v) => {
for arg in &mut v.args { for arg in &mut v.args {
process_ignored_symbol(arg);
seen_variables.insert(arg.clone()); seen_variables.insert(arg.clone());
} }
round_1_collected.push(NormalFormAtom::View(v)) round_1_collected.push(NormalFormAtom::View(v))
} }
NormalFormAtom::NegatedAttrTriple(mut t) => { NormalFormAtom::NegatedAttrTriple(t) => {
process_ignored_symbol(&mut t.value);
process_ignored_symbol(&mut t.entity);
pending.push(NormalFormAtom::NegatedAttrTriple(t)) pending.push(NormalFormAtom::NegatedAttrTriple(t))
} }
NormalFormAtom::NegatedRule(mut r) => { NormalFormAtom::NegatedRule(r) => {
for arg in &mut r.args {
process_ignored_symbol(arg);
}
pending.push(NormalFormAtom::NegatedRule(r)) pending.push(NormalFormAtom::NegatedRule(r))
} }
NormalFormAtom::NegatedView(mut v) => { NormalFormAtom::NegatedView(v) => {
for arg in &mut v.args {
process_ignored_symbol(arg);
}
pending.push(NormalFormAtom::NegatedView(v)) pending.push(NormalFormAtom::NegatedView(v))
} }
NormalFormAtom::Predicate(p) => { NormalFormAtom::Predicate(p) => {

@ -173,6 +173,19 @@ fn air_routes() -> Result<()> {
)? )?
); );
let dijkstra_time = Instant::now();
let res = db.run_script(
r#"
starting <- [['JFK']];
ending <- [['KUL']];
res <- shortest_path_dijkstra!(:flies_to_code[], starting[], ending[]);
?[?path] := res[?src, ?dst, ?cost, ?path];
"#,
)?;
dbg!(dijkstra_time.elapsed());
assert_eq!(*res.get("rows").unwrap(), json!([[["JFK", "CTU", "KUL"]]]));
let starts_with_time = Instant::now(); let starts_with_time = Instant::now();
let res = db.run_script( let res = db.run_script(
r#" r#"

Loading…
Cancel
Save