1#ifndef GUARD_INCLUDE_PROBFD_PREPROCESSING_END_COMPONENT_DECOMPOSITION_H
2#error "This file should only be included from end_component_decomposition.h"
5#include "probfd/utils/language.h"
7#include "downward/utils/countdown_timer.h"
15inline void ECDStatistics::print(std::ostream& out)
const
17 out <<
" Terminal states: " << terminals <<
" (" << goals
18 <<
" goal states, " << selfloops <<
" self loop states)" << std::endl;
19 out <<
" Singleton SCC(s): " << sccs1 <<
" (" << sccs1_dead <<
" dead)"
21 out <<
" Non-singleton SCC(s): " << sccsk <<
" (" << sccsk_dead <<
" dead)"
23 out <<
" Singleton end component(s): " << ec1 << std::endl;
24 out <<
" Non-singleton end component(s): " << eck << std::endl;
25 out <<
" End-component transitions: " << ec_transitions << std::endl;
26 out <<
" Recursive calls: " << recursions << std::endl;
27 out <<
" End component computation: " << time << std::endl;
30template <
typename State,
typename Action>
31bool EndComponentDecomposition<State, Action>::StateInfo::onstack()
const
33 return stackid < UNDEF;
36template <
typename State,
typename Action>
37auto EndComponentDecomposition<State, Action>::StateInfo::get_status()
const
39 return explored ? (onstack() ? ONSTACK : CLOSED) : NEW;
42template <
typename State,
typename Action>
43EndComponentDecomposition<State, Action>::ExpansionInfo::ExpansionInfo(
45 std::vector<Action> aops,
46 std::vector<std::vector<StateID>> successors)
49 , nz_or_leaves_scc(false)
50 , aops(
std::move(aops))
51 , successors(
std::move(successors))
55template <
typename State,
typename Action>
56EndComponentDecomposition<State, Action>::ExpansionInfo::ExpansionInfo(
58 std::vector<Action> aops,
59 std::vector<std::vector<StateID>> successors,
63 , nz_or_leaves_scc(mdp.get_action_cost(aops.back()) != 0_vt)
64 , aops(
std::move(aops))
65 , successors(
std::move(successors))
69template <
typename State,
typename Action>
70bool EndComponentDecomposition<State, Action>::ExpansionInfo::next_action(
73 assert(aops.size() == successors.size());
75 successors.pop_back();
76 nz_or_leaves_scc =
false;
80template <
typename State,
typename Action>
81bool EndComponentDecomposition<State, Action>::ExpansionInfo::next_action(
84 assert(aops.size() == successors.size());
86 successors.pop_back();
89 nz_or_leaves_scc = mdp.get_action_cost(aops.back()) != 0_vt;
96template <
typename State,
typename Action>
97bool EndComponentDecomposition<State, Action>::ExpansionInfo::next_successor()
99 auto& succs = successors.back();
101 return !succs.empty();
104template <
typename State,
typename Action>
106EndComponentDecomposition<State, Action>::ExpansionInfo::get_current_successor()
108 return successors.back().back();
111template <
typename State,
typename Action>
113EndComponentDecomposition<State, Action>::ExpansionInfo::get_current_action()
118template <
typename State,
typename Action>
119struct EndComponentDecomposition<State, Action>::StackInfo {
123 std::vector<Action> aops;
124 std::vector<std::vector<StateID>> successors;
126 explicit StackInfo(StateID sid)
133 friend auto& get(StackInfo& info)
135 if constexpr (i == 0)
return info.stateid;
136 if constexpr (i == 1)
return info.aops;
140 friend const auto& get(
const StackInfo& info)
142 if constexpr (i == 0)
return info.stateid;
143 if constexpr (i == 1)
return info.aops;
147template <
typename State,
typename Action>
148EndComponentDecomposition<State, Action>::EndComponentDecomposition(
150 : expand_goals_(expand_goals)
154template <
typename State,
typename Action>
159 double max_time) -> std::unique_ptr<QSystem>
161 utils::CountdownTimer timer(max_time);
164 auto sys = std::make_unique<QSystem>(mdp);
166 auto init_id = mdp.get_state_id(initial_state);
168 if (push(init_id, state_infos_[init_id], mdp, pruning_function)) {
169 find_and_decompose_sccs(*sys, 0, timer, mdp, pruning_function);
172 assert(stack_.empty());
173 assert(expansion_queue_.empty());
179template <
typename State,
typename Action>
181 std::ostream& out)
const
186template <
typename State,
typename Action>
187ECDStatistics EndComponentDecomposition<State, Action>::get_statistics()
const
192template <
typename State,
typename Action>
193bool EndComponentDecomposition<State, Action>::push(
195 StateInfo& state_info,
197 const EvaluatorType* pruning_function)
199 state_info.explored = 1;
200 State state = mdp.get_state(state_id);
202 const auto term = mdp.get_termination_info(state);
204 if (term.is_goal_state()) {
208 if (!expand_goals_) {
212 state_info.expandable_goal = 1;
214 pruning_function !=
nullptr &&
215 pruning_function->evaluate(state) == term.get_cost()) {
220 std::vector<Action> aops;
221 mdp.generate_applicable_actions(state, aops);
224 if (expand_goals_ && state_info.expandable_goal) {
225 state_info.expandable_goal = 0;
233 std::vector<std::vector<StateID>> successors;
234 successors.reserve(aops.size());
236 unsigned non_loop_actions = 0;
237 for (
unsigned i = 0; i < aops.size(); ++i) {
238 Distribution<StateID> transition;
239 mdp.generate_action_transitions(state, aops[i], transition);
241 std::vector<StateID> succ_ids;
243 for (StateID succ_id : transition.support()) {
244 if (succ_id != state_id) {
245 succ_ids.push_back(succ_id);
249 if (!succ_ids.empty()) {
250 successors.emplace_back(std::move(succ_ids));
252 if (i != non_loop_actions) {
253 aops[non_loop_actions] = aops[i];
261 if (non_loop_actions == 0) {
262 if (expand_goals_ && state_info.expandable_goal) {
263 state_info.expandable_goal = 0;
272 aops.erase(aops.begin() + non_loop_actions, aops.end());
274 expansion_queue_.emplace_back(
277 std::move(successors),
280 state_info.stackid = stack_.size();
281 stack_.emplace_back(state_id);
286template <
typename State,
typename Action>
287bool EndComponentDecomposition<State, Action>::push(
291 assert(!info.explored);
292 assert(info.onstack());
294 info.explored =
true;
295 StackInfo& scc_info = stack_[info.stackid];
297 if (scc_info.successors.empty()) {
298 info.stackid = StateInfo::UNDEF;
303 const auto stack_size = stack_.size();
304 info.stackid = stack_size;
306 expansion_queue_.emplace_back(
308 std::move(scc_info.aops),
309 std::move(scc_info.successors));
311 stack_.emplace_back(state_id);
316template <
typename State,
typename Action>
317void EndComponentDecomposition<State, Action>::find_and_decompose_sccs(
319 const unsigned limit,
320 utils::CountdownTimer& timer,
323 if (expansion_queue_.size() <= limit) {
327 ExpansionInfo* e = &expansion_queue_.back();
328 StackInfo* s = &stack_[e->stck];
332 while (push_successor(*e, *s, timer, mdp_and_h...)) {
333 e = &expansion_queue_.back();
334 s = &stack_[e->stck];
339 assert(s->successors.back().empty());
340 s->successors.pop_back();
342 assert(e->successors.empty() && e->aops.empty());
344 const bool recurse = e->recurse;
345 const unsigned int stck = e->stck;
346 const unsigned int lstck = e->lstck;
348 assert(stck >= lstck);
350 const bool scc_root = stck == lstck;
353 scc_found<
sizeof...(mdp_and_h) != 0>(sys, *e, *s, timer);
356 expansion_queue_.pop_back();
358 if (expansion_queue_.size() <= limit) {
362 timer.throw_if_expired();
364 e = &expansion_queue_.back();
365 s = &stack_[e->stck];
367 auto& stk_successors = s->successors.back();
371 e->recurse = e->recurse || !stk_successors.empty();
372 e->nz_or_leaves_scc =
true;
374 e->lstck = std::min(e->lstck, lstck);
376 e->recurse = e->recurse || recurse || e->nz_or_leaves_scc;
377 stk_successors.emplace_back(e->get_current_successor());
381 if (e->next_successor()) {
386 if (!e->nz_or_leaves_scc) {
387 assert(!stk_successors.empty());
388 s->aops.push_back(e->get_current_action());
389 s->successors.emplace_back();
391 stk_successors.clear();
393 }
while (!e->next_action(select_opt<0>(mdp_and_h...)));
397template <
typename State,
typename Action>
398bool EndComponentDecomposition<State, Action>::push_successor(
401 utils::CountdownTimer& timer,
405 std::vector<StateID>& s_succs = s.successors.back();
408 timer.throw_if_expired();
410 const StateID succ_id = e.get_current_successor();
411 StateInfo& succ_info = state_infos_[succ_id];
413 switch (succ_info.get_status()) {
415 if (push(succ_id, succ_info, mdp_and_h...)) {
421 case StateInfo::CLOSED:
422 e.recurse = e.recurse || !s_succs.empty();
423 e.nz_or_leaves_scc =
true;
426 case StateInfo::ONSTACK:
427 e.lstck = std::min(e.lstck, succ_info.stackid);
429 e.recurse = e.recurse || e.nz_or_leaves_scc;
430 s_succs.emplace_back(succ_id);
432 }
while (e.next_successor());
435 if (!e.nz_or_leaves_scc) {
436 assert(!s_succs.empty());
437 s.successors.emplace_back();
438 s.aops.push_back(e.get_current_action());
442 }
while (e.next_action(select_opt<0>(mdp_and_h...)));
447template <
typename State,
typename Action>
448template <
bool RootIteration>
449void EndComponentDecomposition<State, Action>::scc_found(
453 utils::CountdownTimer& timer)
455 auto scc = stack_ | std::views::drop(e.stck);
458 assert(s.aops.empty());
459 const StateID scc_repr_id = s.stateid;
460 StateInfo& info = state_infos_[scc_repr_id];
461 info.stackid = StateInfo::UNDEF;
468 if constexpr (RootIteration) {
473 for (
auto& stk_info : scc) {
474 assert(stk_info.successors.size() == stk_info.aops.size());
475 StateInfo& info = state_infos_[stk_info.stateid];
476 if (info.expandable_goal) {
477 stk_info.successors.clear();
478 stk_info.aops.clear();
487 if constexpr (RootIteration) {
491 for (
const auto& stk_info : scc) {
492 assert(stk_info.successors.size() == stk_info.aops.size());
493 state_infos_[stk_info.stateid].explored = 0;
496 decompose(sys, e.stck, timer);
498 unsigned transitions = 0;
500 for (
const auto& stk_info : scc) {
501 assert(stk_info.successors.size() == stk_info.aops.size());
502 StateInfo& info = state_infos_[stk_info.stateid];
503 info.stackid = StateInfo::UNDEF;
505 transitions += stk_info.aops.size();
508 sys.build_new_quotient(scc, s);
509 stack_.erase(scc.begin(), scc.end());
513 stats_.ec_transitions += transitions;
515 if constexpr (RootIteration) {
521 assert(stack_.size() == e.stck);
524template <
typename State,
typename Action>
525void EndComponentDecomposition<State, Action>::decompose(
528 utils::CountdownTimer& timer)
530 const unsigned limit = expansion_queue_.size();
532 for (
unsigned i = start; i < stack_.size(); ++i) {
533 const StateID
id = stack_[i].stateid;
534 StateInfo& state_info = state_infos_[id];
536 if (!state_info.explored && push(
id, state_info)) {
538 find_and_decompose_sccs(sys, limit, timer);
542 stack_.erase(stack_.begin() + start, stack_.end());
544 assert(expansion_queue_.size() == limit);
A builder class that implements end component decomposition.
Definition end_component_decomposition.h:86
This namespace contains preprocessing algorithms for SSPs.
Definition end_component_decomposition.h:24
typename std::conditional_t< is_cheap_to_copy_v< T >, T, const T & > param_type
Alias template defining the best way to pass a parameter of a given type.
Definition type_traits.h:25
Contains printable statistics for the end component decomposition.
Definition end_component_decomposition.h:29