dijkstra's algorithm

main
Ziyang Hu 2 years ago
parent 00fff0ce5a
commit 0e3c9507e4

@ -36,8 +36,8 @@ cozorocks = { path = "cozorocks" }
#[target.'cfg(not(target_env = "msvc"))'.dependencies]
#tikv-jemallocator = { version = "0.5", features = ["disable_initial_exec_tls"] }
[profile.release]
lto = true
#[profile.release]
#lto = true
#[profile.release]
#debug = true

@ -18,7 +18,4 @@ serde_json = "1.0.81"
cozo = { path = ".." }
[build-dependencies]
static-files = "0.2.3"
[profile.release]
lto = true
static-files = "0.2.3"

@ -6,6 +6,7 @@ use itertools::Itertools;
use crate::algo::bfs::Bfs;
use crate::algo::degree_centrality::DegreeCentrality;
use crate::algo::dfs::Dfs;
use crate::algo::shortest_path_dijkstra::ShortestPathDijkstra;
use crate::algo::strongly_connected_components::StronglyConnectedComponent;
use crate::algo::top_sort::TopSort;
use crate::data::expr::Expr;
@ -21,6 +22,7 @@ pub(crate) mod bfs;
pub(crate) mod degree_centrality;
pub(crate) mod dfs;
pub(crate) mod page_rank;
pub(crate) mod shortest_path_dijkstra;
pub(crate) mod strongly_connected_components;
pub(crate) mod top_sort;
@ -51,6 +53,7 @@ impl AlgoHandle {
"degree_centrality" => 4,
"depth_first_search" | "dfs" => 1,
"breadth_first_search" | "bfs" => 1,
"shortest_path_dijkstra" => 4,
"top_sort" => 2,
"connected_components" => 2,
"strongly_connected_components" | "scc" => 2,
@ -64,6 +67,7 @@ impl AlgoHandle {
"degree_centrality" => Box::new(DegreeCentrality),
"depth_first_search" | "dfs" => Box::new(Dfs),
"breadth_first_search" | "bfs" => Box::new(Bfs),
"shortest_path_dijkstra" => Box::new(ShortestPathDijkstra),
"top_sort" => Box::new(TopSort),
"connected_components" => Box::new(StronglyConnectedComponent::new(false)),
"strongly_connected_components" | "scc" => {
@ -76,9 +80,70 @@ impl AlgoHandle {
}
impl MagicAlgoRuleArg {
pub(crate) fn convert_edge_to_weighted_graph(
&self,
undirected: bool,
allow_negative_edge: bool,
tx: &SessionTx,
stores: &BTreeMap<MagicSymbol, DerivedRelStore>,
) -> Result<(
Vec<Vec<(usize, f64)>>,
Vec<DataValue>,
BTreeMap<DataValue, usize>,
)> {
let mut graph: Vec<Vec<(usize, f64)>> = vec![];
let mut indices: Vec<DataValue> = vec![];
let mut inv_indices: BTreeMap<DataValue, usize> = Default::default();
for tuple in self.iter(tx, stores)? {
let mut tuple = tuple?.0.into_iter();
let from = tuple
.next()
.ok_or_else(|| anyhow!("edges relation too short"))?;
let to = tuple
.next()
.ok_or_else(|| anyhow!("edges relation too short"))?;
let weight = match tuple.next() {
None => 1.0,
Some(d) => match d.get_float() {
Some(f) => {
ensure!(f.is_finite(), "edge weight must be finite, got {}", f);
if !allow_negative_edge {
ensure!(f >= 0., "edge weight must be non-negative, got {}", f);
}
f
}
None => bail!("edge weight must be a number, got {:?}", d),
},
};
let from_idx = if let Some(idx) = inv_indices.get(&from) {
*idx
} else {
inv_indices.insert(from.clone(), graph.len());
indices.push(from.clone());
graph.push(vec![]);
graph.len() - 1
};
let to_idx = if let Some(idx) = inv_indices.get(&to) {
*idx
} else {
inv_indices.insert(to.clone(), graph.len());
indices.push(to.clone());
graph.push(vec![]);
graph.len() - 1
};
let from_target = graph.get_mut(from_idx).unwrap();
from_target.push((to_idx, weight));
if undirected {
let to_target = graph.get_mut(to_idx).unwrap();
to_target.push((from_idx, weight));
}
}
Ok((graph, indices, inv_indices))
}
pub(crate) fn convert_edge_to_graph(
&self,
bidir: bool,
undirected: bool,
tx: &SessionTx,
stores: &BTreeMap<MagicSymbol, DerivedRelStore>,
) -> Result<(Vec<Vec<usize>>, Vec<DataValue>, BTreeMap<DataValue, usize>)> {
@ -88,12 +153,12 @@ impl MagicAlgoRuleArg {
for tuple in self.iter(tx, stores)? {
let mut tuple = tuple?.0.into_iter();
let from = tuple.next().ok_or_else(|| {
anyhow!("edges relation too short")
})?;
let to = tuple.next().ok_or_else(|| {
anyhow!("edges relation too short")
})?;
let from = tuple
.next()
.ok_or_else(|| anyhow!("edges relation too short"))?;
let to = tuple
.next()
.ok_or_else(|| anyhow!("edges relation too short"))?;
let from_idx = if let Some(idx) = inv_indices.get(&from) {
*idx
} else {
@ -112,7 +177,7 @@ impl MagicAlgoRuleArg {
};
let from_target = graph.get_mut(from_idx).unwrap();
from_target.push(to_idx);
if bidir {
if undirected {
let to_target = graph.get_mut(to_idx).unwrap();
to_target.push(from_idx);
}

@ -0,0 +1,187 @@
use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet, BinaryHeap};
use anyhow::{anyhow, bail, Result};
use either::{Left, Right};
use itertools::Itertools;
use crate::algo::AlgoImpl;
use crate::data::expr::Expr;
use crate::data::program::{MagicAlgoRuleArg, MagicSymbol};
use crate::data::symb::Symbol;
use crate::data::tuple::Tuple;
use crate::data::value::DataValue;
use crate::runtime::derived::DerivedRelStore;
use crate::runtime::transact::SessionTx;
pub(crate) struct ShortestPathDijkstra;
impl AlgoImpl for ShortestPathDijkstra {
fn run(
&mut self,
tx: &mut SessionTx,
rels: &[MagicAlgoRuleArg],
opts: &BTreeMap<Symbol, Expr>,
stores: &BTreeMap<MagicSymbol, DerivedRelStore>,
out: &DerivedRelStore,
) -> Result<()> {
let edges = rels
.get(0)
.ok_or_else(|| anyhow!("'shortest_path_dijkstra' requires edges relation"))?;
let starting = rels.get(1).ok_or_else(|| {
anyhow!("'shortest_path_dijkstra' requires starting relation as second argument")
})?;
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")) {
None => false,
Some(Expr::Const(DataValue::Bool(b))) => *b,
Some(v) => bail!(
"option 'undirected' for 'shortest_path_dijkstra' requires a boolean, got {:?}",
v
),
};
let (graph, indices, inv_indices) =
edges.convert_edge_to_weighted_graph(undirected, allow_negative_edges, tx, stores)?;
let mut starting_nodes = BTreeSet::new();
for tuple in starting.iter(tx, stores)? {
let tuple = tuple?;
let node = tuple
.0
.get(0)
.ok_or_else(|| anyhow!("node relation too short"))?;
if let Some(idx) = inv_indices.get(node) {
starting_nodes.insert(*idx);
}
}
let termination_nodes = match termination {
None => None,
Some(t) => {
let mut tn = BTreeSet::new();
for tuple in t.iter(tx, stores)? {
let tuple = tuple?;
let node = tuple
.0
.get(0)
.ok_or_else(|| anyhow!("node relation too short"))?;
if let Some(idx) = inv_indices.get(node) {
tn.insert(*idx);
}
}
Some(tn)
}
};
for start in starting_nodes {
let res = dijkstra(&graph, start, &termination_nodes);
for (target, cost, path) in res {
let t = vec![
indices[start].clone(),
indices[target].clone(),
DataValue::from(cost),
DataValue::List(path.into_iter().map(|u| indices[u].clone()).collect_vec()),
];
out.put(Tuple(t), 0)
}
}
Ok(())
}
}
#[derive(PartialEq)]
struct HeapState {
cost: f64,
node: usize,
}
impl PartialOrd for HeapState {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for HeapState {
fn cmp(&self, other: &Self) -> Ordering {
self.cost
.total_cmp(&other.cost)
.reverse()
.then_with(|| self.node.cmp(&other.node))
}
}
impl Eq for HeapState {}
fn dijkstra(
edges: &[Vec<(usize, f64)>],
start: usize,
maybe_goals: &Option<BTreeSet<usize>>,
) -> Vec<(usize, f64, Vec<usize>)> {
let mut distance = vec![f64::INFINITY; edges.len()];
let mut heap = BinaryHeap::new();
let mut back_pointers = vec![usize::MAX; edges.len()];
distance[start] = 0.;
heap.push(HeapState {
cost: 0.,
node: start,
});
let mut goals_remaining = maybe_goals.clone();
while let Some(state) = heap.pop() {
if state.cost > distance[state.node] {
continue;
}
for (nxt_node, path_weight) in &edges[state.node] {
let nxt_cost = state.cost + *path_weight;
if nxt_cost < distance[*nxt_node] {
heap.push(HeapState {
cost: nxt_cost,
node: *nxt_node,
});
distance[*nxt_node] = nxt_cost;
back_pointers[*nxt_node] = state.node;
}
}
if let Some(goals) = &mut goals_remaining {
if goals.remove(&state.node) {
if goals.is_empty() {
break;
}
}
}
}
let targets = if let Some(goals) = maybe_goals {
Left(goals.iter().cloned())
} else {
Right(0..edges.len())
};
let ret = targets
.map(|target| {
let cost = distance[target];
if !cost.is_finite() {
(target, cost, vec![])
} else {
let mut path = vec![];
let mut current = target;
while current != start {
path.push(current);
current = back_pointers[current];
}
path.push(start);
path.reverse();
(target, cost, path)
}
})
.collect_vec();
ret
}
Loading…
Cancel
Save