generic_rcsp/
lib.rs

1/*
2  Copyright (C) 2023 Gregor Godbersen
3  Copyright (C) 2023 Gerhard Hiermann
4  This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
5*/
6#![warn(warnings)]
7#![warn(clippy::all, clippy::pedantic)]
8#![allow(non_upper_case_globals)]
9#![allow(clippy::needless_return)]
10#![allow(clippy::items_after_statements)]
11#![allow(unused_variables, unused_imports, dead_code)]
12
13use std::cell::Cell;
14use std::cmp;
15use std::cmp::Ordering;
16use binary_heap_plus::BinaryHeap;
17use std::fmt::{Debug};
18use std::marker::PhantomData;
19use petgraph::stable_graph::EdgeIndex;
20use petgraph::stable_graph::EdgeReference;
21use petgraph::stable_graph::StableGraph;
22use petgraph::prelude::{EdgeRef, NodeIndex};
23use typed_arena::Arena;
24
25/// Shorthand for petgraph stable graph
26pub type GraphType<NodeWeight, EdgeWeight> = StableGraph::<NodeWeight, EdgeWeight, petgraph::Directed, u32>;
27
28/// Generates the initial root label
29pub trait InitialLabelGenerator<T> {
30    /// Return the LabelMeta for the initial root label
31    fn default(problem : &T) -> Self;
32}
33
34
35/// Main trait to implement that holds the user implementation of the problem.
36///
37/// Generics to implement:
38/// - LabelMeta : Data to be stored in labels
39/// - NodeWeight: Node attributes
40/// - EdgeWeight: Edge attributes
41/// - BranchFilter: Constraints made available during label extension
42///
43pub trait UserProblem<LabelMeta: Meta,  NodeWeight,EdgeWeight, BranchFilter>
44{
45    /// build your problem graph and return it
46    ///
47    /// is called once during initialization of solver and then stored
48    fn create_graph(&mut self) -> ProblemGraph<NodeWeight, EdgeWeight>;
49
50    /// Dominance function called in labeling algorithm.
51    /// return true if label_a domiates label_b
52    fn is_dominating(label_a: &Label<LabelMeta>, label_b: &Label<LabelMeta>, active_filters : &[BranchFilter]) -> bool;
53
54    /// Label extension function
55    ///
56    /// Receives previous label, edge, node weights, branching filters, and reference to the sink node
57    /// Return None if extension infeasible, otherwise return new Label.
58    fn extend_label<'arena>(
59        &self,
60        existing_label : &'arena Label<'arena, LabelMeta>,
61        edge : &EdgeReference<EdgeWeight>,
62        source : &NodeWeight,
63        target : &NodeWeight,
64        active_filters : &[BranchFilter],
65        sink : NodeIndex
66    ) -> Option<Label<'arena,LabelMeta>>;
67
68}
69
70/// Compact struct containing dag and references to its source and sink node.
71pub struct ProblemGraph<NodeWeight,EdgeWeight > {
72    pub dag : GraphType<NodeWeight,EdgeWeight>,
73    pub start : NodeIndex,
74    pub end : NodeIndex
75}
76
77/// The solver structure holding the user problem and graph
78pub struct RcspSolver<Problem,LabelMeta : Meta, NodeWeight,EdgeWeight , BranchFilter>
79    where
80        Problem : UserProblem<LabelMeta, NodeWeight, EdgeWeight,  BranchFilter>,
81
82{
83    user_problem :  Problem,
84    graph : ProblemGraph<NodeWeight,EdgeWeight>,
85    _meta : PhantomData<LabelMeta>,
86    _ew : PhantomData<EdgeWeight>,
87    _nw : PhantomData<NodeWeight>,
88    _ft : PhantomData<BranchFilter>
89}
90
91impl<Problem : UserProblem<LabelMeta,NodeWeight, EdgeWeight, BranchFilter>,LabelMeta : Meta, NodeWeight,EdgeWeight, BranchFilter>
92    RcspSolver<Problem,LabelMeta, NodeWeight, EdgeWeight, BranchFilter>
93{
94
95    pub fn new(mut problem : Problem) -> Self {
96
97        let graph = problem.create_graph();
98        Self {
99            user_problem : problem,
100            graph,
101            _meta: PhantomData,
102            _ew: PhantomData,
103            _nw: PhantomData,
104            _ft : PhantomData
105        }
106    }
107
108    /// Returns reference to stored problem graph
109    pub  fn get_graph(&self) -> &ProblemGraph<NodeWeight,EdgeWeight> {
110        &self.graph
111    }
112
113    /// Returns mutable reference to stored problem graph
114    pub  fn get_graph_mut(&mut self) -> &mut ProblemGraph<NodeWeight,EdgeWeight> {
115        &mut self.graph
116    }
117
118    /// Returns mutable reference to stored problem
119    pub  fn get_problem_mut(&mut self) -> &mut Problem {
120        &mut self.user_problem
121    }
122
123    /// Returns reference to stored problem
124    pub  fn get_problem(&self) -> &Problem {
125        &self.user_problem
126    }
127
128}
129
130#[derive(Clone, PartialEq)]
131/// Solution object describing path between source and sink node
132pub struct FeasiblePath {
133    pub path:  Vec<(NodeIndex,Option<EdgeIndex>)>
134}
135
136impl Debug for FeasiblePath{
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        f.write_str("\nFeasiblePath\n")?;
139        f.write_str("Visiting: ")?;
140        for (node,_edge) in &self.path {
141            f.write_fmt(format_args!("{}-", node.index()))?;
142        }
143        f.write_str("\n")?;
144        Ok(())
145    }
146}
147
148/// Trait to be implemented by user for LabelMeta
149pub trait Meta : Clone + Debug  {}
150
151
152
153
154impl< Problem : UserProblem<LabelMeta,  NodeWeight,EdgeWeight, BranchFilter> ,LabelMeta : Meta +  InitialLabelGenerator<Problem>,  NodeWeight,EdgeWeight, BranchFilter>
155    RcspSolver< Problem,LabelMeta,  NodeWeight,EdgeWeight, BranchFilter>
156    where NodeWeight: Debug + Clone ,
157{
158
159
160    /// Primary function of solver. Finds RCSP for network and returns non dominated results
161    ///
162    /// Return object is vector of (path cost, path) tuples
163    pub fn find_paths<const EARLY_EXIT: bool>(&mut self, filters : &[BranchFilter], path_start_cost : f64) -> Vec<(f64,FeasiblePath)> {
164
165
166        let raw_problem_graph = &self.graph;
167
168        let start_node = raw_problem_graph.start;
169        let end_node = raw_problem_graph.end;
170
171        let dag = &raw_problem_graph.dag;
172
173        // apply resource constrained shortest path algorithm using labels
174        let mut labels_at : Vec<Vec<&Label<LabelMeta>>> = vec![
175            Vec::with_capacity(128); dag.node_count()
176        ];
177
178        debug_assert_eq!(start_node.index(), 0);
179
180        let arena = Arena::new();
181
182        let initial_label = arena.alloc(Label {
183            node : start_node,
184            extended_along_edge : None,
185            parent : None,
186            meta : LabelMeta::default(&self.user_problem),
187            costs: path_start_cost,
188            was_dominated : Cell::new(false)
189        });
190
191
192        let mut unprocessed_labels: BinaryHeap<&Label<LabelMeta>, _> = BinaryHeap::with_capacity_by(4096, |a : &&Label<LabelMeta>, b : &&Label<LabelMeta>| {
193            // keep labels with small cost at front
194            b.costs.partial_cmp(&a.costs).unwrap_or(Ordering::Equal)
195        });
196
197        unprocessed_labels.push(initial_label);
198        'main: loop {
199
200            let process_opt = unprocessed_labels.pop();
201
202            // generate all direct possible child labels that are feasible
203            let potential_next_labels  = match process_opt {
204                Some(process) => {
205
206                    if process.was_dominated.get() {
207                        continue
208                    }
209
210                    dag.edges_directed(
211                        process.node, petgraph::Direction::Outgoing).into_iter().filter_map(|edge : EdgeReference<_>| {
212
213                        return self.user_problem.extend_label(process, &edge, &dag[edge.source()], &dag[edge.target()], filters, end_node)
214                    })
215
216
217
218
219                },
220                None => break,
221            };
222
223            // process every possible label
224            for new_label in potential_next_labels {
225
226                if let Some(added_label) = Self::add_with_dominance_check(&mut labels_at[new_label.node.index()], &arena,new_label, filters) {
227                    if EARLY_EXIT {
228                        if added_label.node == end_node && added_label.costs < -0.000_001 {
229                            break 'main;
230                        }
231                    }
232                    unprocessed_labels.push(added_label);
233                }
234            }
235        }
236
237
238        // is sorted by cost, so first = best
239        let labels_at_end =  &labels_at[end_node.index()];
240        let mut output : Vec<(f64,FeasiblePath)> = labels_at_end.iter().map(|label| {
241             let mut path = Vec::new();
242             Self::get_parent(&mut path, label);
243
244             path.reverse();
245
246
247             (label.costs, FeasiblePath {
248                 path: path
249             })
250         }).collect();
251
252
253        output.sort_unstable_by(|a,b| a.0.partial_cmp(&b.0).unwrap_or(cmp::Ordering::Equal) );
254
255        output
256    }
257
258
259
260
261    #[inline(always)]
262    fn get_parent(nodes : &mut Vec<(NodeIndex,Option<EdgeIndex>)>, label : &Label<LabelMeta>)   {
263        nodes.push((label.node, label.extended_along_edge));
264        if let Some(parent) = label.parent {
265            Self::get_parent(nodes, parent);
266        }
267    }
268
269
270    /// Internal function to insert new label to label storage
271    ///
272    /// Thanks to Gerhard Hiermann for the implementation
273    /// https://github.com/ghiermann
274    ///
275    /// The label storage `labels` is a list sorted by cost
276    /// and shoud remain sorted.
277    /// This function has three goals:
278    /// - Detect if new label is dominated by existing. In this case terminate
279    /// - Insert the new label at the correct position
280    /// - Prune all existing labels that the new label dominates
281    ///
282    fn add_with_dominance_check<'arena>(
283        labels: & mut Vec<&'arena Label<'arena, LabelMeta>>,
284        arena : &'arena Arena<Label<'arena, LabelMeta>>,
285        candidate: Label<'arena, LabelMeta>,
286        active_filters : &[BranchFilter]
287                                        ) -> Option<&'arena Label<'arena, LabelMeta>>
288    {
289
290
291
292        let mut i = 0;
293        // phase eins. Wir checken ob das label selbst dominiert wird!
294
295
296        while i < labels.len() {
297            if labels[i].costs > candidate.costs {
298                break;
299            } else if Problem::is_dominating(labels[i], &candidate, active_filters) {
300                return None;
301            } else if labels[i].costs == candidate.costs {
302                break;
303            } else {
304                i += 1;
305            }
306        }
307
308        // No cheaper ones found -> not dominated for now
309        // i = index where labels are first more expensive than before
310        // Now just check if I dominate one of the more expensive ones
311        let added_label = arena.alloc(candidate);
312
313        let mut candidate = &*added_label;
314        // Phase two.
315        // Am I dominating another label?
316
317        let s_idx = i; // Pointer to the first
318        let mut tmp = if i == labels.len() {
319            // End of the list - just add it and return
320            // I'm at the end, not dominating anything else, so I just have to add it
321            labels.push(candidate);
322            return Some(added_label);
323        } else if Problem::is_dominating(candidate, labels[i], active_filters) {
324            // If I dominate the first one right away, discard it
325            labels[i] = candidate; // overwrite directly
326            None // Not a candidate for exchange because it's immediately excluded
327
328        } else {
329            // If I don't dominate it: Insert label at the position
330            std::mem::swap(&mut labels[i], &mut candidate);
331            Some(candidate) // Save original s in tmp
332        };
333
334
335        let mut removed = 0;
336        i += 1; // The first one was examined specifically, now moving on to the next one
337        while i < labels.len() {
338
339
340      // if reference dominates the current one
341            if Problem::is_dominating(labels[s_idx], labels[i], active_filters) {
342
343
344             // if I have saved another one
345                if tmp.is_some() {
346                 // Replace i with the one in memory
347                    let val = tmp.take().unwrap();
348                // Save my tmp instead of the dominated one
349
350                    labels[i] = val;
351                } else {
352                    // Otherwise, it has to go
353                    // (I have nothing to swap it with)
354
355                    removed += 1;
356                }
357            } else {// If that does not dominate
358                if tmp.is_some() {
359                    // If I still have one from the tmp, exchange yours
360                    // and put it in tmp
361
362                    std::mem::swap(&mut labels[i], tmp.as_mut().unwrap());
363                } else if removed > 0 {
364                    // If I already have one as tmp to remove,
365                    // move it
366                    labels.swap(i, i - removed);
367                }
368            }
369            i += 1;
370        }
371        // If there's still one left now, then add it at the end
372        if let Some(label) = tmp {
373            labels.push(label);
374        } else if removed > 0 {
375            let kept = labels.len() - removed;
376            // Update label value
377            for i in labels.iter().skip(kept) {
378                i.was_dominated.set(true);
379            }
380
381            unsafe {
382                labels.set_len(kept);
383            }
384        }
385        
386        return  Some(added_label)
387
388    }
389
390
391}
392
393
394
395
396
397#[derive(Clone, Debug)]
398/// Internal label struct used during algorithm
399/// Holds cost used for domination and
400///     LabelMeta used for resource tracking
401pub struct Label<'a, M : Meta> {
402    pub costs: f64,
403    pub node : NodeIndex,
404    pub extended_along_edge : Option<EdgeIndex>,
405    pub parent : Option<&'a Label<'a,M>>,
406    pub meta : M,
407    was_dominated : Cell<bool>,
408}
409
410
411impl<'a,M : Meta> Label<'a, M> {
412
413    /// Helper function that creates a child label from an existing label
414    pub fn extend<T>( parent : &'a Label<'a,M>,edge : &EdgeReference<T>, delta_cost : f64, new_meta : M ) -> Self {
415        Self {
416            costs: parent.costs + delta_cost ,
417            node: edge.target(),
418            extended_along_edge: Some(edge.id()),
419            parent : Some(parent),
420            meta: new_meta,
421            was_dominated: Cell::new(false)
422        }
423    }
424
425    /// Label constructor
426    pub fn new( cost : f64, node : NodeIndex,extended_along_edge : Option<EdgeIndex>, parent : Option<&'a Label<'a,M>>, meta : M) -> Self {
427        Self{
428            costs: cost,
429            node,
430            extended_along_edge,
431            parent,
432            meta,
433            was_dominated : Cell::new(false)
434        }
435    }
436
437
438
439}
440
441
442
443
444
445