Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue1140 #225

Merged
merged 8 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 45 additions & 49 deletions src/search/search_algorithms/eager_search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,16 @@ SearchStatus EagerSearch::step() {
With lazy evaluators (and only with these) we can have dead nodes
in the open list.

For example, consider a state s that is reached twice before it is expanded.
The first time we insert it into the open list, we compute a finite
heuristic value. The second time we insert it, the cached value is reused.

During first expansion, the heuristic value is recomputed and might become
infinite, for example because the reevaluation uses a stronger heuristic or
because the heuristic is path-dependent and we have accumulated more
information in the meantime. Then upon second expansion we have a dead-end
node which we must ignore.
For example, consider a state s that is reached twice before it is
expanded. The first time we insert it into the open list, we
compute a finite heuristic value. The second time we insert it,
the cached value is reused.

During first expansion, the heuristic value is recomputed and
might become infinite, for example because the reevaluation uses a
stronger heuristic or because the heuristic is path-dependent and
we have accumulated more information in the meantime. Then upon
second expansion we have a dead-end node which we must ignore.
*/
if (node->is_dead_end())
continue;
Expand Down Expand Up @@ -216,12 +217,14 @@ SearchStatus EagerSearch::step() {
continue;

if (succ_node.is_new()) {
// We have not seen this state before.
// Evaluate and create a new node.
/*
We have not seen this state before.
Evaluate and create a new node.

// Careful: succ_node.get_g() is not available here yet,
// hence the stupid computation of succ_g.
// TODO: Make this less fragile.
Careful: succ_node.get_g() is not available here yet,
hence the stupid computation of succ_g.
TODO: Make this less fragile.
*/
int succ_g = node->get_g() + get_adjusted_cost(op);

EvaluationContext succ_eval_context(
Expand All @@ -233,7 +236,7 @@ SearchStatus EagerSearch::step() {
statistics.inc_dead_ends();
continue;
}
succ_node.open(*node, op, get_adjusted_cost(op));
succ_node.open_new_node(*node, op, get_adjusted_cost(op));

open_list->insert(succ_eval_context, succ_state.get_id());
if (search_progress.check_progress(succ_eval_context)) {
Expand All @@ -242,49 +245,42 @@ SearchStatus EagerSearch::step() {
}
} else if (succ_node.get_g() > node->get_g() + get_adjusted_cost(op)) {
// We found a new cheapest path to an open or closed state.
if (reopen_closed_nodes) {
if (succ_node.is_closed()) {
/*
TODO: It would be nice if we had a way to test
that reopening is expected behaviour, i.e., exit
with an error when this is something where
reopening should not occur (e.g. A* with a
consistent heuristic).
*/
statistics.inc_reopened();
}
succ_node.reopen(*node, op, get_adjusted_cost(op));

if (succ_node.is_open()) {
succ_node.update_open_node_parent(
*node, op, get_adjusted_cost(op));
EvaluationContext succ_eval_context(
succ_state, succ_node.get_g(), is_preferred, &statistics);

open_list->insert(succ_eval_context, succ_state.get_id());
} else if (succ_node.is_closed() && reopen_closed_nodes) {
/*
Note: our old code used to retrieve the h value from
the search node here. Our new code recomputes it as
necessary, thus avoiding the incredible ugliness of
the old "set_evaluator_value" approach, which also
did not generalize properly to settings with more
than one evaluator.

Reopening should not happen all that frequently, so
the performance impact of this is hopefully not that
large. In the medium term, we want the evaluators to
remember evaluator values for states themselves if
desired by the user, so that such recomputations
will just involve a look-up by the Evaluator object
rather than a recomputation of the evaluator value
from scratch.
TODO: It would be nice if we had a way to test
that reopening is expected behaviour, i.e., exit
with an error when this is something where
reopening should not occur (e.g. A* with a
consistent heuristic).
*/
statistics.inc_reopened();
succ_node.reopen_closed_node(*node, op, get_adjusted_cost(op));
EvaluationContext succ_eval_context(
succ_state, succ_node.get_g(), is_preferred, &statistics);
open_list->insert(succ_eval_context, succ_state.get_id());
} else {
// If we do not reopen closed nodes, we just update the parent pointers.
// Note that this could cause an incompatibility between
// the g-value and the actual path that is traced back.
remochristen marked this conversation as resolved.
Show resolved Hide resolved
succ_node.update_parent(*node, op, get_adjusted_cost(op));
/*
If we do not reopen closed nodes, we just update the parent
pointers. Note that this could cause an incompatibility
between the g-value and the actual path that is traced back.
*/
assert(succ_node.is_closed() && !reopen_closed_nodes);
succ_node.update_closed_node_parent(
*node, op, get_adjusted_cost(op));
}
} else {
/*
We found an equally or more expensive path to an open or closed
state.
*/
}
}

return IN_PROGRESS;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ SearchStatus EnforcedHillClimbingSearch::ehc() {
}

int h = eval_context.get_evaluator_value(evaluator.get());
node.open(parent_node, last_op, get_adjusted_cost(last_op));
node.open_new_node(parent_node, last_op,
get_adjusted_cost(last_op));

if (h < current_eval_context.get_evaluator_value(evaluator.get())) {
++num_ehc_phases;
Expand Down
7 changes: 5 additions & 2 deletions src/search/search_algorithms/lazy_search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,13 @@ SearchStatus LazySearch::step() {
SearchNode parent_node = search_space.get_node(parent_state);
OperatorProxy current_operator = task_proxy.get_operators()[current_operator_id];
if (reopen) {
node.reopen(parent_node, current_operator, get_adjusted_cost(current_operator));
node.reopen_closed_node(parent_node, current_operator,
get_adjusted_cost(
current_operator));
statistics.inc_reopened();
} else {
node.open(parent_node, current_operator, get_adjusted_cost(current_operator));
node.open_new_node(parent_node, current_operator,
get_adjusted_cost(current_operator));
}
}
node.close();
Expand Down
55 changes: 27 additions & 28 deletions src/search/search_space.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,44 +53,43 @@ void SearchNode::open_initial() {
info.creating_operator = OperatorID::no_operator;
}

void SearchNode::open(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::NEW);
info.status = SearchNodeInfo::OPEN;
void SearchNode::update_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
info.g = parent_node.info.g + adjusted_cost;
info.real_g = parent_node.info.real_g + parent_op.get_cost();
info.parent_state_id = parent_node.get_state().get_id();
info.creating_operator = OperatorID(parent_op.get_id());
}

void SearchNode::reopen(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::OPEN ||
info.status == SearchNodeInfo::CLOSED);
void SearchNode::open_new_node(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::NEW);
info.status = SearchNodeInfo::OPEN;
update_parent(parent_node, parent_op, adjusted_cost);
}

// The latter possibility is for inconsistent heuristics, which
// may require reopening closed nodes.
void SearchNode::reopen_closed_node(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::CLOSED);
info.status = SearchNodeInfo::OPEN;
info.g = parent_node.info.g + adjusted_cost;
info.real_g = parent_node.info.real_g + parent_op.get_cost();
info.parent_state_id = parent_node.get_state().get_id();
info.creating_operator = OperatorID(parent_op.get_id());
update_parent(parent_node, parent_op, adjusted_cost);
}

// like reopen, except doesn't change status
void SearchNode::update_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::OPEN ||
info.status == SearchNodeInfo::CLOSED);
// The latter possibility is for inconsistent heuristics, which
// may require reopening closed nodes.
info.g = parent_node.info.g + adjusted_cost;
info.real_g = parent_node.info.real_g + parent_op.get_cost();
info.parent_state_id = parent_node.get_state().get_id();
info.creating_operator = OperatorID(parent_op.get_id());
void SearchNode::update_open_node_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::OPEN);
update_parent(parent_node, parent_op, adjusted_cost);
}

void SearchNode::update_closed_node_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost) {
assert(info.status == SearchNodeInfo::CLOSED);
update_parent(parent_node, parent_op, adjusted_cost);
}

void SearchNode::close() {
Expand Down
21 changes: 14 additions & 7 deletions src/search/search_space.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class LogProxy;
class SearchNode {
State state;
SearchNodeInfo &info;

void update_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
public:
SearchNode(const State &state, SearchNodeInfo &info);

Expand All @@ -32,15 +36,18 @@ class SearchNode {
int get_real_g() const;

void open_initial();
void open(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
void reopen(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
void update_parent(const SearchNode &parent_node,
void open_new_node(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
void reopen_closed_node(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
void update_open_node_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
void update_closed_node_parent(const SearchNode &parent_node,
const OperatorProxy &parent_op,
int adjusted_cost);
void close();
void mark_as_dead_end();

Expand Down
Loading