I implemented a prototype for the suggested design. It seems to work, but is very ugly and hacky at the moment. This is why I didn't push it to Jendriks repo but instead made a new repo. You can see the diff here: https://github.com/salome-eriksson/downward-issue751-prototype/compare/41694b17b3117562fd9d6af1098465e954c4152f...issue751
I went with the design to template EvaluationContext with states (=StateID) or edges (=pair<StateID,OperatorID), which also enforces that OpenLists can only have EvaluationContexts of the same type (i.e. you cannot pass a StateEvaluationContext to a EdgeOpenList).
While I at first thought that this makes a lot of sense, there are quite some issues with this design:
1)If we want to evaluate a state in lazy search right before it gets expanded, we need to do this with an EdgeEvaluationContext (with OperatorID::no_operator). This goes against the idea of EdgeEvaluationContexts, which should evaluate the edge if possible and only fall back to evaluating the parent state if they can't do edge evaluation. For GEvaluator that meant that I needed to add an if in the compute_result method which returns the g-value of the parent if no operator is given, and the g-value of the child otherwise.
2) It makes caching messy. If we could use a StateEvaluationContext for evaluating the state before expanding, then it would be clear: cache estimates from StateEvaluationContexts, and don't cache estimates from EdgeEvaluationContexts. (This works because GEvaluator actually caches its estimates in the notify-state-transition method.)
3) Since the template types are identical to the ones from OpenList, we can only pass StateIDs to the constructor of EvaluationContext, when before we passed a const State &. This means that I now need to create the state in the constructor and it lives in the EvaluationContext. If we want several contexts for the same state, we thus have several copies.
There are also several minor things that are still ugly (usually denoted with a TODO in the code):
- EvaluationContext always has a OperatorID member, with OperatorID::no_operator being set for StateEvaluationContext. If we try to access it, we get a cerr message but return no_operator anyways (we could of course also exit instead)
- I defined aliases for the templated EvaluationContexts, but for some reason I needed to repeat them in evaluator.h and don't really understand why this is necessary / what we can do to avoid it.
- I duplicated a lot of code for the compute_result method of Evaluator.
- I'm not sure if we should count it as an evaluation when GEvaluator computes the g-value for a EdgeEvaluationContext
- I needed to move the entire implementation of EvalutionContext in the header file and am not sure if/how we can avoid this.
- When creating the EvaluationContext in lazy search, we had a note about why we pass preferred=true. I don't know if that note still applies.
|