Skip to content

Commit

Permalink
refactor: collect search
Browse files Browse the repository at this point in the history
  • Loading branch information
jmdha committed May 24, 2024
1 parent 9282205 commit 61136e2
Show file tree
Hide file tree
Showing 8 changed files with 1,955 additions and 105 deletions.
1,845 changes: 1,845 additions & 0 deletions out

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
max_width = 120
6 changes: 0 additions & 6 deletions src/heuristic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub enum HeuristicKind {

pub struct Heuristic {
kind: HeuristicKind,
best_estimate: usize,
estimates: usize,
time: Duration,
}
Expand All @@ -23,7 +22,6 @@ impl Heuristic {
pub fn new(kind: HeuristicKind) -> Self {
Self {
kind,
best_estimate: usize::MAX,
estimates: 0,
time: Duration::default(),
}
Expand All @@ -36,10 +34,6 @@ impl Heuristic {
HeuristicKind::GoalCount => goal_count::estimate(task, state),
};
self.time += t.elapsed();
if estimate < self.best_estimate {
println!("New best heuristic estimate: {}", estimate);
self.best_estimate = estimate;
}
self.estimates += 1;
estimate
}
Expand Down
35 changes: 35 additions & 0 deletions src/search/bfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use super::SearchAlgorithm;
use crate::{search::Error, trace, FxIndexMap};
use indexmap::map::Entry::Vacant;
use pddllib::{state::State, successor_generation::successors};

pub struct BFS {
index: usize,
parents: FxIndexMap<State, usize>,
}

impl BFS {
pub fn new(initial_state: &State) -> Self {
let mut parents = FxIndexMap::default();
parents.insert(initial_state.clone(), 0);
Self { index: 0, parents }
}
}

impl<'a> SearchAlgorithm<'a> for BFS {
fn step(&mut self, task: &'a pddllib::task::Task) -> super::Result<'a> {
let (node, _) = self.parents.get_index(self.index).ok_or(Error::Unsolvable)?;
for successor in successors(task, node) {
if successor.covers(task, &task.goal) {
let mut path = trace(&self.parents, self.index);
path.push(successor);
return Ok(path);
}
if let Vacant(e) = self.parents.entry(successor) {
e.insert(self.index);
}
}
self.index += 1;
Err(Error::Unfinished)
}
}
22 changes: 22 additions & 0 deletions src/search/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use std::fmt::Display;

#[derive(Debug)]
pub enum Error {
Unfinished,
Unsolvable,
OutOfTime,
OutOfMemory,
}

impl std::error::Error for Error {}

impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Unfinished => write!(f, "Unfinished"),
Error::Unsolvable => write!(f, "Unsolvable"),
Error::OutOfTime => write!(f, "Out of time"),
Error::OutOfMemory => write!(f, "Out of memory"),
}
}
}
49 changes: 18 additions & 31 deletions src/search/gbfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,34 @@ impl GBFS {
let mut parents = FxIndexMap::default();
parents.insert(initial_state.clone(), 0);
Self {
queue: BinaryHeap::from(vec![Element {
index: 0,
estimate: 0,
}]),
queue: BinaryHeap::from(vec![Element { index: 0, estimate: 0 }]),
parents,
heuristic,
}
}
}

impl<'a> SearchAlgorithm<'a> for GBFS {
fn step(&mut self, task: &'a Task) -> Option<Result<'a>> {
let Element { index, estimate: _ } = match self.queue.pop() {
Some(e) => e,
None => return Some(Err(Error::Unsolvable)),
};

let successors = {
let (node, _) = self.parents.get_index(index).unwrap();
if node.covers(&task, &task.goal) {
return Some(Ok(trace(&self.parents, index)));
fn step(&mut self, task: &'a pddllib::task::Task) -> super::Result<'a> {
let Element { index, estimate: _ } = self.queue.pop().ok_or(Error::Unsolvable)?;
let (node, _) = self.parents.get_index(index).unwrap();
if node.covers(task, &task.goal) {
return Ok(trace(&self.parents, index));
}
for successor in successors(task, node) {
let s_index;
if let Vacant(e) = self.parents.entry(successor) {
s_index = e.index();
e.insert(index);
} else {
continue;
}
successors(task, node)
};

for successor in successors.into_iter() {
let estimate = self.heuristic.estimate(task, &successor);
let successor_index = match self.parents.entry(successor) {
Occupied(_) => continue,
Vacant(e) => {
let n = e.index();
e.insert(index);
n
}
};
let (state, _) = self.parents.get_index(s_index).unwrap();
self.queue.push(Element {
index: successor_index,
estimate,
index: s_index,
estimate: self.heuristic.estimate(task, state),
})
}

None
Err(Error::Unfinished)
}
}
47 changes: 17 additions & 30 deletions src/search/lgbfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,34 @@ impl LGBFS {
let mut parents = FxIndexMap::default();
parents.insert(initial_state.clone(), 0);
Self {
queue: BinaryHeap::from(vec![Element {
index: 0,
estimate: 0,
}]),
queue: BinaryHeap::from(vec![Element { index: 0, estimate: 0 }]),
parents,
heuristic,
}
}
}

impl<'a> SearchAlgorithm<'a> for LGBFS {
fn step(&mut self, task: &'a Task) -> Option<Result<'a>> {
let Element { index, estimate: _ } = match self.queue.pop() {
Some(e) => e,
None => return Some(Err(Error::Unsolvable)),
};

let (node, successors) = {
let (node, _) = self.parents.get_index(index).unwrap();
if node.covers(task, &task.goal) {
return Some(Ok(trace(&self.parents, index)));
}
(node, successors(task, node))
};

fn step(&mut self, task: &'a pddllib::task::Task) -> super::Result<'a> {
let Element { index, estimate: _ } = self.queue.pop().ok_or(Error::Unsolvable)?;
let (node, _) = self.parents.get_index(index).unwrap();
if node.covers(task, &task.goal) {
return Ok(trace(&self.parents, index));
}
let estimate = self.heuristic.estimate(task, node);
for successor in successors.into_iter() {
let successor_index = match self.parents.entry(successor) {
Occupied(_) => continue,
Vacant(e) => {
let n = e.index();
e.insert(index);
n
}
};
for successor in successors(task, node) {
let s_index;
if let Vacant(e) = self.parents.entry(successor) {
s_index = e.index();
e.insert(index);
} else {
continue;
}
self.queue.push(Element {
index: successor_index,
index: s_index,
estimate,
})
}

None
Err(Error::Unfinished)
}
}
55 changes: 17 additions & 38 deletions src/search/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
pub mod gbfs;
pub mod lgbfs;
mod bfs;
pub mod error;
mod gbfs;
mod lgbfs;

use self::error::Error;
use crate::heuristic::{Heuristic, HeuristicKind};
use clap::Subcommand;
use memory_stats::memory_stats;
use pddllib::{state::State, task::Task};
use std::{
fmt::Display,
time::{Duration, Instant},
};

#[derive(Debug)]
pub enum Error {
Unsolvable,
OutOfTime,
OutOfMemory,
}
use std::time::{Duration, Instant};

#[derive(Subcommand, Debug, Clone)]
pub enum SearchKind {
BFS,
/// Greedy Best First Search
GBFS {
#[arg(default_value = "goal-count")]
Expand All @@ -31,32 +25,17 @@ pub enum SearchKind {
},
}

impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Unsolvable => write!(f, "Unsolvable"),
Error::OutOfTime => write!(f, "Out of time"),
Error::OutOfMemory => write!(f, "Out of memory"),
}
}
}

impl std::error::Error for Error {}

pub type Result<'a> = std::result::Result<Vec<State>, Error>;

pub trait SearchAlgorithm<'a> {
fn step(&mut self, task: &'a Task) -> Option<Result<'a>>;
fn step(&mut self, task: &'a Task) -> Result<'a>;
}

pub fn generate<'a>(task: &'a Task, search: &'a SearchKind) -> Box<dyn SearchAlgorithm<'a>> {
match search {
SearchKind::GBFS { heuristic } => {
Box::new(gbfs::GBFS::new(&task.init, Heuristic::new(*heuristic)))
}
SearchKind::LGBFS { heuristic } => {
Box::new(lgbfs::LGBFS::new(&task.init, Heuristic::new(*heuristic)))
}
SearchKind::BFS => Box::new(bfs::BFS::new(&task.init)),
SearchKind::GBFS { heuristic } => Box::new(gbfs::GBFS::new(&task.init, Heuristic::new(*heuristic))),
SearchKind::LGBFS { heuristic } => Box::new(lgbfs::LGBFS::new(&task.init, Heuristic::new(*heuristic))),
}
}

Expand All @@ -67,10 +46,15 @@ pub fn solve<'a>(
searcher: &mut Box<dyn SearchAlgorithm<'a>>,
) -> Result<'a> {
let start = Instant::now();
let result: Result<'a>;
let mut result: Result<'a>;
let mut peak_memory = 0;
let mut steps = 0;
loop {
result = searcher.step(task);
steps += 1;
if result.is_ok() {
break;
}
if let Some(time_limit) = time_limit {
let elapsed = start.elapsed();
if elapsed > time_limit {
Expand All @@ -92,11 +76,6 @@ pub fn solve<'a>(
}
}
}
if let Some(search_result) = searcher.step(task) {
result = search_result;
break;
}
steps += 1;
}
println!("Peak memory: {}MB", peak_memory / 1000000);
println!("Steps: {}", steps);
Expand Down

0 comments on commit 61136e2

Please # to comment.