I would like to revive a discussion about the axiom evaluator, we had in issue774 ("Get rid of g_axiom_evaluator"). Back then, we moved from a single global axiom evaluator (g_axiom_evaluator) to a global hash map mapping tasks to axiom evaluators (g_axiom_evaluators).
As part of this change, we discussed different alternatives like storing axiom evaluators in a their task or storing a global map as we ended up doing. The argument back then was that we have multiple structures associated to a task that are difficult to compute and in some cases optional (axiom evaluator, successor generator, causal graph, int packer, landmarks). They should be computed on demand but only once per task. Back then, we aimed for a generic solution that fits all of these use cases and ended up with the PerTaskInformation that can store information for each task, makes sure it's computed only once per task and deletes the data if the task is deleted.
But we also saw that this was not perfect solution and some aspects would require more thought. The axiom evaluator is more closely linked to the task then, say, landmarks. The int packer belongs in the registry, and the successor generator should maybe be its own thing and not so integrated into the code of states and tasks. So in the end, it might not be a good solution to go for a uniform solution for these different parts.
In this issue I'm just focusing on the axiom evaluator and want to discuss if it makes sense to move it into the task as we had it in some intermediate version of issue774. One advantage I see is that currently, we map tasks to evaluators, for example a CostAdaptedTask that only changes the cost would get its own axiom evaluator. If we store it in the root task, and have the DelegatingTask forward the getter to its parent by default, then most of our task transformations (CostAdaptedTask, ModifiedGoalsTask, ModifiedOperatorCostsTask) could reuse the axiom evaluator of their parent task. The DomainAbstractedTask doesn't support axioms anyway, and the only remaining task transformation DefaultValueAxiomsTask would override the getter and create its own axiom evaluator (stored in the DefaultValueAxiomsTask).
There is one issue with storing the axiom evaluator inside an AbstractTask but we already have that issue now: the axiom evaluator would like to access the task using the high-level interface that TaskProxy provides but would be constructed in the constructor of an AbstractTask where TaskProxies and even the low-level interface of the abstract tasks are not ready yet. Currently, we use a TaskProxy anyway, relying on the axiom evaluator only accessing already constructed parts of the partially constructed task. This is obviously an ugly hack. In issue774 we deferred this as "still requires further thought". Maybe one solution could be to change the axiom evaluator in a way where it gets the task data not from an incomplete task but in a more basic form (vectors of default values, axiom rules, etc.) It is definitely nicer to use a TaskProxy but if we expect TaskProxy::get_initial_state() to return a state with evaluated axioms, then the code that evaluates the axioms shouldn't depend on TaskProxy.
|