Issue1201

Title Analyse whether a configuration guarantees completeness
Priority feature Status chatting
Superseder Nosy List Claudia, gabi, jendrik, malte, simon, tanja
Assigned To Keywords
Optional summary

Created on 2026-02-10.14:54:36 by gabi, last changed by simon.

Messages
msg12032 (view) Author: simon Date: 2026-02-25.22:42:58
Claudia and I converged to one version that is ready to review:  https://github.com/aibasel/downward/pull/283/
msg12025 (view) Author: Claudia Date: 2026-02-20.15:36:43
I ran the tests a couple of times on my machine with and without the extra tests and could not see a significant difference in the runtime. So, I added the extra tests (and one for SEARCH_UNSOLVABLE_WITHIN_BOUND) to the PR.
msg12024 (view) Author: malte Date: 2026-02-20.14:14:02
Usually it's a good first approximation that if a test is worth writing, it's worth keeping. A simple way to see how much time the tests take is to use "time", for example "time tox -e driver".
msg12023 (view) Author: Claudia Date: 2026-02-20.13:39:50
In total they do make the driver tests longer because they all use an unsolvable task (even though it is a small one). I am not sure if the time increase is acceptable and thus committed only five of those.

Furthermore, Simon pointed out that tests for the new SEARCH_UNSOLVABLE_WITHIN_BOUND are missing. I plan to add some for this as well.
msg12022 (view) Author: malte Date: 2026-02-20.12:07:35
Thanks, Claudia! If these are useful tests, I would add them permanently, not just temporarily, provided that they don't take too long.
msg12021 (view) Author: Claudia Date: 2026-02-20.11:31:38
The pull request now also includes five new test cases for `tox -e driver`. For this, I also added a new benchmark file. It is a small blocksworld problem that I adjusted such that it is unsolvable but where unsolvability is not detected by the translator (otherwise the translator would pass a trivially unsolvable SAS-file to the search component).

I also ran `tox -e driver` once with more than those five new test cases. Specifically, I (temporarily) added the following lines to SEARCH_TESTS in downard/misc/tests/test-exitcodes.py:

    ("unsolvable", [], "astar(blind())",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "astar(lmcut())",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "lazy(single(lmcut()))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "eager(alt([single(const(infinity)),single(const(1))]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "eager(type_based([const(infinity),const(1)]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "eager(pareto([const(infinity),const(1)]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "eager(tiebreaking([const(infinity),const(1)],unsafe_pruning=false))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVABLE)),
    ("unsolvable", [], "ehc(blind())",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
    ("unsolvable", [], "eager(single(blind(),pref_only=true))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
    ("unsolvable", [], "eager(pareto([blind()],pref_only=true))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
    ("unsolvable", [], "eager(pareto([cg(),cea()]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
    ("unsolvable", [], "eager(tiebreaking([const(infinity),blind()]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
    ("unsolvable", [], "eager(alt([single(cg()),single(const(infinity))]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
    ("unsolvable", [], "lazy(type_based([cg(),const(infinity)]))",
        defaultdict(lambda: returncodes.SEARCH_UNSOLVED_INCOMPLETE)),
msg12008 (view) Author: simon Date: 2026-02-17.15:19:25
I suggest these three outputs then.
"Search terminated without finding a solution."
"Search terminated -- no solution exists."
"Search terminated -- no solution for cost bound 42 exists."
msg11998 (view) Author: Claudia Date: 2026-02-17.14:33:42
We should also discuss how to handle the line "Completely explored statespace -- no solution" from eager and lazy search which is printed when the open list becomes empty. 

With an incomplete configuration this is not correct as the search might not actually have completely explored the state space. Furthermore, with a too small bound but a configuration that is complete relative to that bound this might be misleading.

One option would be to remove the line altogether. However, keeping this (kind of) information in the planner output is probably preferable. So the question is what else to print.

Some ideas:

- "Search terminated without finding a solution."
- Print different text depending on whether the configuration is complete and / or unbounded (which we can now both check). If it is unbounded and complete we could print the current text.
msg11995 (view) Author: simon Date: 2026-02-16.16:16:16
We implemented completeness checks in the search algorithms (except for IteratedSearch, see below), open lists, and pruning methods. For the evaluators we mostly rely on the existing is_safe() function (previously called dead_ends_are_reliable()) and fixed it for the ConstEvaluator and WeightedEvaluator.

Since the completeness checks are only relative to the given g-bound (if one is given) we added the new exit code SEARCH_UNSOLVABLE_WITHIN_BOUND=13. We also use SEARCH_UNSOLVABLE in more places now since the completeness checks enabled this.

We did not yet implement the completeness check for IteratedSearch because without solving the component interaction problem this is not possible from a static context. Instead of making all other completeness checks non-static we return false for the completeness check of IteratedSearch and print a warning that it is not yet implemented.

The current PR is at: https://github.com/aibasel/downward/pull/283/

We plan to also add some tests for the new exit code in the github actions. This should have no impact on the files in src/search and we think the PR could already be reviewed now and the tests could be reviewed later once they are added. But there is no rush so the reviewers can wait it out until we actually added the tests.
msg11994 (view) Author: Claudia Date: 2026-02-16.11:18:12
Archiving some notes from our February 2026 sprint where we pulled a story to add optimality and completeness checks (we dropped optimality checks at some point during the sprint to focus on completeness checks).


Eager best-first search
-----------------------

- when changing the cost_type we cannot guarantee optimality (with regard to the input task, not necessarily to the configuration)
- without reopening, we cannot guarantee optimality (exception is A* with a consistent heuristic)
- uses is_dead_end (instead of is_reliable_dead_end), which in general also considers unsafe heuristics
- lazy_evaluator is not passed to openlist (and does not do anything if it is not chosen to be the same Evaluator as in the OpenList)
- relevant aspects see the online documentation (except for lazy evaluator)


Notes

- It seems that the OpenLists already have a mechanism to tag unsafe heuristics
- The open list has to check if any of its components (sub-open-lists and heuristics) might violate optimality / completeness.
- completeness is relative to the g bound


Goal for this sprint: report completeness guarantees (not optimality)

- at the moment, there are only pruning methods that preserve completeness, but we should add a method is_complete (for the case that future pruning methods are incomplete) done
- heuristics/evaluators need a method is_safe already have dead_ends_are_reliable
- update exit codes in documentation (11, 12) done
msg11975 (view) Author: gabi Date: 2026-02-10.14:54:36
We would like to adapt the search component so that it can in more unsuccessful
cases return that the task is unsolvable (not only that it did not find a
solution).

The idea is to analyze the configuration in a task-independent way
so that it can be done by the task-independent components once the component
interaction problem has been resolved.
History
Date User Action Args
2026-02-25 22:42:58simonsetmessages: + msg12032
2026-02-20 15:36:43Claudiasetmessages: + msg12025
2026-02-20 14:14:02maltesetmessages: + msg12024
2026-02-20 13:39:50Claudiasetmessages: + msg12023
2026-02-20 12:07:35maltesetmessages: + msg12022
2026-02-20 11:31:38Claudiasetmessages: + msg12021
2026-02-17 15:19:25simonsetmessages: + msg12008
2026-02-17 14:33:42Claudiasetmessages: + msg11998
2026-02-16 16:16:16simonsetmessages: + msg11995
nosy: + simon
2026-02-16 11:18:12Claudiasetmessages: + msg11994
status: unread -> chatting
2026-02-10 14:55:33maltesetnosy: + jendrik
2026-02-10 14:54:36gabicreate