use crate::{
algorithm::{
dijkstra::{
forward::{DijkstraSearchError, Memory},
Dijkstra,
},
Algorithm, Coherent, Path, SearchStatus, Solvable,
},
domain::{
Activity, ArrivalKeyring, Backtrack, Closable, ClosedStatusForKey, Configurable,
Connectable, Domain, Initializable, Keyed, Keyring, Reversible, Weighted,
},
error::{Anyhow, ThisError},
};
use std::ops::Add;
pub struct BackwardDijkstra<D: Reversible>
where
D: Domain + Keyed + Activity<D::State> + Weighted<D::State, D::Action> + Closable<D::State>,
{
backward: Dijkstra<D>,
}
impl<D: Reversible> BackwardDijkstra<D>
where
D: Domain + Keyed + Activity<D::State> + Weighted<D::State, D::Action> + Closable<D::State>,
{
pub fn new(domain: &D) -> Result<Self, D::ReversalError> {
Ok(Self {
backward: Dijkstra::new(domain.reversed()?),
})
}
pub fn backward(&self) -> &Dijkstra<D> {
&self.backward
}
}
impl<D: Reversible> Algorithm for BackwardDijkstra<D>
where
D: Domain + Keyed + Activity<D::State> + Weighted<D::State, D::Action> + Closable<D::State>,
{
type Memory = BackwardMemory<D>;
}
pub struct BackwardMemory<D: Reversible>
where
D: Domain + Keyed + Activity<D::State> + Weighted<D::State, D::Action> + Closable<D::State>,
{
backward: Memory<D>,
}
impl<D: Reversible, Start, Goal> Coherent<Start, Goal> for BackwardDijkstra<D>
where
D: Domain
+ Keyring<D::State>
+ Initializable<Goal, Start, D::State>
+ Activity<D::State>
+ Weighted<D::State, D::Action>
+ Closable<D::State>
+ Connectable<D::State, D::Action, D::Key>
+ ArrivalKeyring<D::Key, Goal, Start>,
D::ClosedSet<usize>: ClosedStatusForKey<D::Key, usize>,
D::Action: Clone,
D::InitialError: Into<D::Error>,
D::ArrivalKeyError: Into<D::Error>,
D::WeightedError: Into<D::Error>,
D::ConnectionError: Into<D::Error>,
D::State: Clone,
D::Cost: Clone + Ord + Add<D::Cost, Output = D::Cost>,
D::Key: Clone,
Start: Clone,
Goal: Clone,
{
type InitError = DijkstraSearchError<D::Error>;
fn initialize(&self, start: Start, goal: &Goal) -> Result<Self::Memory, Self::InitError> {
let memory = self.backward.initialize(goal.clone(), &start)?;
Ok(BackwardMemory { backward: memory })
}
}
impl<D, Goal> Solvable<Goal> for BackwardDijkstra<D>
where
D: Domain
+ Reversible
+ Activity<D::State>
+ Weighted<D::State, D::Action>
+ Keyring<D::State>
+ Closable<D::State>
+ Connectable<D::State, D::Action, D::Key>
+ Backtrack<D::State, D::Action>,
D::State: Clone,
D::Action: Clone,
D::Cost: Clone + Ord + Add<D::Cost, Output = D::Cost>,
D::ClosedSet<usize>: ClosedStatusForKey<D::Key, usize>,
D::ActivityError: Into<D::Error>,
D::WeightedError: Into<D::Error>,
D::ConnectionError: Into<D::Error>,
D::BacktrackError: Into<D::Error>,
{
type Solution = Path<D::State, D::Action, D::Cost>;
type StepError = DijkstraSearchError<D::Error>;
fn step(
&self,
memory: &mut Self::Memory,
_: &Goal,
) -> Result<SearchStatus<Self::Solution>, Self::StepError> {
self.backward.step(&mut memory.backward, &()).and_then(|r|
r.and_then(|path| path.backtrack(self.backward.domain()))
.map_err(DijkstraSearchError::Domain))
}
}
impl<D: Configurable> Configurable for BackwardDijkstra<D>
where
D: Domain
+ Reversible
+ Keyed
+ Activity<D::State>
+ Weighted<D::State, D::Action>
+ Closable<D::State>,
D::ReversalError: Into<Anyhow>,
{
type Configuration = D::Configuration;
fn configure<F>(self, f: F) -> Result<Self, Anyhow>
where
F: FnOnce(Self::Configuration) -> Result<Self::Configuration, Anyhow>,
{
let configured = self
.backward
.domain()
.reversed()
.map_err(Into::into)?
.configure(f)?;
Self::new(&configured).map_err(Into::into)
}
}
#[derive(Debug, ThisError)]
pub enum BackwardConfigurationError<R, C> {
#[error("An error occurred while reversing the domain:\n{0:?}")]
Reverse(R),
#[error("An error occured while configuring the forward domain:\n{0:?}")]
Configure(C),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
domain::KeyedCloser,
graph::{SharedGraph, SimpleGraph},
motion::{se2::*, TravelTimeCost},
templates::{GraphMotion, LazyGraphMotion, UninformedSearch},
Planner,
};
use std::sync::Arc;
#[test]
fn test_dijkstra_same_start_and_goal_se2() {
let graph = SharedGraph::new(SimpleGraph::from_iters(
[
Point::new(0.0, 0.0), Point::new(1.0, 0.0), Point::new(2.0, 0.0), Point::new(3.0, 0.0), Point::new(1.0, -1.0), Point::new(2.0, -1.0), Point::new(3.0, -1.0), Point::new(2.0, -2.0), Point::new(3.0, -2.0), ],
[
(0, 1, ()),
(1, 0, ()),
(1, 2, ()),
(2, 1, ()),
(2, 3, ()),
(3, 2, ()),
(2, 4, ()),
(4, 2, ()),
(3, 6, ()),
(6, 3, ()),
(4, 5, ()),
(5, 4, ()),
(5, 7, ()),
(7, 5, ()),
(7, 8, ()),
(8, 7, ()),
],
));
let extrapolator = DifferentialDriveLineFollow::new(1.0, 1.0).unwrap();
let weight = TravelTimeCost(1.0);
let motion = GraphMotion {
space: DiscreteSpaceTimeSE2::<usize, 100>::new(),
graph: graph.clone(),
extrapolator,
};
let planner = Planner::new(Arc::new(
BackwardDijkstra::new(
&UninformedSearch::new_uninformed(
motion.clone(),
weight,
KeyedCloser(DiscreteSpaceTimeSE2::new()),
)
.with_initializer(PreferentialStarburstSE2::for_start(graph.clone()))
.with_satisfier(PreferentialStarburstSE2::for_goal(graph).unwrap())
.with_connector(LazyGraphMotion {
motion,
keyring: (),
chain: MergeIntoGoal(extrapolator),
}),
)
.unwrap(),
));
for i in 0..=8usize {
for angle in [0.0, 90.0, 180.0, 30.0, -140.0_f64] {
let start_key = KeySE2::new(i, angle.to_radians());
let result = planner.plan(start_key, i).unwrap().solve().unwrap();
let solution = result.solution().unwrap();
assert_eq!(solution.total_cost.0, 0.0);
}
}
}
}