#![warn(warnings)]
#![warn(clippy::all, clippy::pedantic)]
#![allow(non_upper_case_globals)]
#![allow(clippy::needless_return)]
#![allow(clippy::items_after_statements)]
#![allow(unused_variables, unused_imports, dead_code)]
use std::cell::Cell;
use std::cmp;
use std::cmp::Ordering;
use binary_heap_plus::BinaryHeap;
use std::fmt::{Debug};
use std::marker::PhantomData;
use petgraph::stable_graph::EdgeIndex;
use petgraph::stable_graph::EdgeReference;
use petgraph::stable_graph::StableGraph;
use petgraph::prelude::{EdgeRef, NodeIndex};
use typed_arena::Arena;
pub type GraphType<NodeWeight, EdgeWeight> = StableGraph::<NodeWeight, EdgeWeight, petgraph::Directed, u32>;
pub trait InitialLabelGenerator<T> {
fn default(problem : &T) -> Self;
}
pub trait UserProblem<LabelMeta: Meta, NodeWeight,EdgeWeight, BranchFilter>
{
fn create_graph(&mut self) -> ProblemGraph<NodeWeight, EdgeWeight>;
fn is_dominating(label_a: &Label<LabelMeta>, label_b: &Label<LabelMeta>, active_filters : &[BranchFilter]) -> bool;
fn extend_label<'arena>(
&self,
existing_label : &'arena Label<'arena, LabelMeta>,
edge : &EdgeReference<EdgeWeight>,
source : &NodeWeight,
target : &NodeWeight,
active_filters : &[BranchFilter],
sink : NodeIndex
) -> Option<Label<'arena,LabelMeta>>;
}
pub struct ProblemGraph<NodeWeight,EdgeWeight > {
pub dag : GraphType<NodeWeight,EdgeWeight>,
pub start : NodeIndex,
pub end : NodeIndex
}
pub struct RcspSolver<Problem,LabelMeta : Meta, NodeWeight,EdgeWeight , BranchFilter>
where
Problem : UserProblem<LabelMeta, NodeWeight, EdgeWeight, BranchFilter>,
{
user_problem : Problem,
graph : ProblemGraph<NodeWeight,EdgeWeight>,
_meta : PhantomData<LabelMeta>,
_ew : PhantomData<EdgeWeight>,
_nw : PhantomData<NodeWeight>,
_ft : PhantomData<BranchFilter>
}
impl<Problem : UserProblem<LabelMeta,NodeWeight, EdgeWeight, BranchFilter>,LabelMeta : Meta, NodeWeight,EdgeWeight, BranchFilter>
RcspSolver<Problem,LabelMeta, NodeWeight, EdgeWeight, BranchFilter>
{
pub fn new(mut problem : Problem) -> Self {
let graph = problem.create_graph();
Self {
user_problem : problem,
graph,
_meta: PhantomData,
_ew: PhantomData,
_nw: PhantomData,
_ft : PhantomData
}
}
pub fn get_graph(&self) -> &ProblemGraph<NodeWeight,EdgeWeight> {
&self.graph
}
pub fn get_graph_mut(&mut self) -> &mut ProblemGraph<NodeWeight,EdgeWeight> {
&mut self.graph
}
pub fn get_problem_mut(&mut self) -> &mut Problem {
&mut self.user_problem
}
pub fn get_problem(&self) -> &Problem {
&self.user_problem
}
}
#[derive(Clone, PartialEq)]
pub struct FeasiblePath {
pub path: Vec<(NodeIndex,Option<EdgeIndex>)>
}
impl Debug for FeasiblePath{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("\nFeasiblePath\n")?;
f.write_str("Visiting: ")?;
for (node,_edge) in &self.path {
f.write_fmt(format_args!("{}-", node.index()))?;
}
f.write_str("\n")?;
Ok(())
}
}
pub trait Meta : Clone + Debug {}
impl< Problem : UserProblem<LabelMeta, NodeWeight,EdgeWeight, BranchFilter> ,LabelMeta : Meta + InitialLabelGenerator<Problem>, NodeWeight,EdgeWeight, BranchFilter>
RcspSolver< Problem,LabelMeta, NodeWeight,EdgeWeight, BranchFilter>
where NodeWeight: Debug + Clone ,
{
pub fn find_paths<const EARLY_EXIT: bool>(&mut self, filters : &[BranchFilter], path_start_cost : f64) -> Vec<(f64,FeasiblePath)> {
let raw_problem_graph = &self.graph;
let start_node = raw_problem_graph.start;
let end_node = raw_problem_graph.end;
let dag = &raw_problem_graph.dag;
let mut labels_at : Vec<Vec<&Label<LabelMeta>>> = vec![
Vec::with_capacity(128); dag.node_count()
];
debug_assert_eq!(start_node.index(), 0);
let arena = Arena::new();
let initial_label = arena.alloc(Label {
node : start_node,
extended_along_edge : None,
parent : None,
meta : LabelMeta::default(&self.user_problem),
costs: path_start_cost,
was_dominated : Cell::new(false)
});
let mut unprocessed_labels: BinaryHeap<&Label<LabelMeta>, _> = BinaryHeap::with_capacity_by(4096, |a : &&Label<LabelMeta>, b : &&Label<LabelMeta>| {
b.costs.partial_cmp(&a.costs).unwrap_or(Ordering::Equal)
});
unprocessed_labels.push(initial_label);
'main: loop {
let process_opt = unprocessed_labels.pop();
let potential_next_labels = match process_opt {
Some(process) => {
if process.was_dominated.get() {
continue
}
dag.edges_directed(
process.node, petgraph::Direction::Outgoing).into_iter().filter_map(|edge : EdgeReference<_>| {
return self.user_problem.extend_label(process, &edge, &dag[edge.source()], &dag[edge.target()], filters, end_node)
})
},
None => break,
};
for new_label in potential_next_labels {
if let Some(added_label) = Self::add_with_dominance_check(&mut labels_at[new_label.node.index()], &arena,new_label, filters) {
if EARLY_EXIT {
if added_label.node == end_node && added_label.costs < -0.000_001 {
break 'main;
}
}
unprocessed_labels.push(added_label);
}
}
}
let labels_at_end = &labels_at[end_node.index()];
let mut output : Vec<(f64,FeasiblePath)> = labels_at_end.iter().map(|label| {
let mut path = Vec::new();
Self::get_parent(&mut path, label);
path.reverse();
(label.costs, FeasiblePath {
path: path
})
}).collect();
output.sort_unstable_by(|a,b| a.0.partial_cmp(&b.0).unwrap_or(cmp::Ordering::Equal) );
output
}
#[inline(always)]
fn get_parent(nodes : &mut Vec<(NodeIndex,Option<EdgeIndex>)>, label : &Label<LabelMeta>) {
nodes.push((label.node, label.extended_along_edge));
if let Some(parent) = label.parent {
Self::get_parent(nodes, parent);
}
}
fn add_with_dominance_check<'arena>(
labels: & mut Vec<&'arena Label<'arena, LabelMeta>>,
arena : &'arena Arena<Label<'arena, LabelMeta>>,
candidate: Label<'arena, LabelMeta>,
active_filters : &[BranchFilter]
) -> Option<&'arena Label<'arena, LabelMeta>>
{
let mut i = 0;
while i < labels.len() {
if labels[i].costs > candidate.costs {
break;
} else if Problem::is_dominating(labels[i], &candidate, active_filters) {
return None;
} else if labels[i].costs == candidate.costs {
break;
} else {
i += 1;
}
}
let added_label = arena.alloc(candidate);
let mut candidate = &*added_label;
let s_idx = i; let mut tmp = if i == labels.len() {
labels.push(candidate);
return Some(added_label);
} else if Problem::is_dominating(candidate, labels[i], active_filters) {
labels[i] = candidate; None
} else {
std::mem::swap(&mut labels[i], &mut candidate);
Some(candidate) };
let mut removed = 0;
i += 1; while i < labels.len() {
if Problem::is_dominating(labels[s_idx], labels[i], active_filters) {
if tmp.is_some() {
let val = tmp.take().unwrap();
labels[i] = val;
} else {
removed += 1;
}
} else { if tmp.is_some() {
std::mem::swap(&mut labels[i], tmp.as_mut().unwrap());
} else if removed > 0 {
labels.swap(i, i - removed);
}
}
i += 1;
}
if let Some(label) = tmp {
labels.push(label);
} else if removed > 0 {
let kept = labels.len() - removed;
for i in labels.iter().skip(kept) {
i.was_dominated.set(true);
}
unsafe {
labels.set_len(kept);
}
}
return Some(added_label)
}
}
#[derive(Clone, Debug)]
pub struct Label<'a, M : Meta> {
pub costs: f64,
pub node : NodeIndex,
pub extended_along_edge : Option<EdgeIndex>,
pub parent : Option<&'a Label<'a,M>>,
pub meta : M,
was_dominated : Cell<bool>,
}
impl<'a,M : Meta> Label<'a, M> {
pub fn extend<T>( parent : &'a Label<'a,M>,edge : &EdgeReference<T>, delta_cost : f64, new_meta : M ) -> Self {
Self {
costs: parent.costs + delta_cost ,
node: edge.target(),
extended_along_edge: Some(edge.id()),
parent : Some(parent),
meta: new_meta,
was_dominated: Cell::new(false)
}
}
pub fn new( cost : f64, node : NodeIndex,extended_along_edge : Option<EdgeIndex>, parent : Option<&'a Label<'a,M>>, meta : M) -> Self {
Self{
costs: cost,
node,
extended_along_edge,
parent,
meta,
was_dominated : Cell::new(false)
}
}
}