Heuristics
SymbolicPlanners.jl provides a library of search heuristics which estimate the distance between a state and the goal.
SymbolicPlanners.Heuristic — Typeabstract type HeuristicAbstract type for search heuristics, which estimate the distance from a State to a goal specified by a Specification. Once constructed, a heuristic can be called on a domain, state, and specification, returning a Real number (typically Float32 for reduced memory usage).
heuristic(domain, state, spec; precompute=true)Heuristics may precompute and store information that will be used repeatedly during search via the precompute! method. Evaluation of the heuristic on a state is defined by compute.
If the precompute keyword argument is true when calling heuristic as a function, then precompute! will be called before compute is called to perform the heuristic evaluation.
Heuristics define the following interface:
SymbolicPlanners.compute — Functioncompute(h, domain, state, spec)
Computes the heuristic value of state relative to a goal in a given domain.
SymbolicPlanners.precompute! — Functionprecompute!(heuristic, domain, [state, spec])Precomputes heuristic information given a domain, state, and specification. This function is typically called once during the initialization phase of a Planner's search algorithm.
Precomputation can optionally be made independent of the spec or initial state by defining specific precompute! methods.
SymbolicPlanners.is_precomputed — Functionis_precomputed(heuristic)Returns whether heuristic has been precomputed.
SymbolicPlanners.ensure_precomputed! — Functionensure_precomputed!(heuristic, domain, [state, spec])Precomputes a heuristic using the provided arguments if !is_precomputed(h).
Heuristics can also be used to filter the set of available or relevant actions during forward and backward search:
SymbolicPlanners.filter_available — Functionfilter_available(h, domain, state, spec)
Uses heuristic information to filter the set of available actions at a given state. Defaults to returning available(domain, state).
SymbolicPlanners.filter_relevant — Functionfilter_relevant(h, domain, state, spec)
Uses heuristic information to filter the set of relevant actions at a given state. Defaults to returning relevant(domain, state).
Basic Heuristics
Several basic heuristics are provided:
SymbolicPlanners.NullHeuristic — TypeNullHeuristic()Null heuristic that always returns zero.
SymbolicPlanners.GoalCountHeuristic — TypeGoalCountHeuristic(dir=:forward)Heuristic that counts the number of goals un/satisfied. Can be used in either the :forward or :backward direction. The latter should be used for search with BackwardPlanner.
SymbolicPlanners.ReachabilityHeuristic — TypeReachabilityHeuristic(max_steps::Int=100)Heuristic which performs a reachability analysis for the goal via abstract interpretation, returning an optimistic estimate of the number or cost of the actions required to reach the goal, or Inf if the goal is not reached within max_steps of abstract action execution.
For propositional domains (i.e. domains with no non-Boolean fluents), this returns the same value as HMax. For domains with numeric fluents or other datatypes, this provides more informed estimates by performing abstract interpretation of operations on those datatypes (e.g. interval arithmetic).
These heuristics are general but not very informative. As such, they are best used as baselines, or for testing the correctness of planning algorithms on small problems.
Metric Heuristics
In domains with numeric fluents, distance metrics can be used as heuristics for goals with equality constraints:
SymbolicPlanners.MetricHeuristic — TypeMetricHeuristic(metric, fluents[, coeffs])Heuristic that computes a metric distance between the current state and the goals for the specified numeric fluents, which are (optionally) multiplied by scalar coeffs before metric computation.
This heuristic can only be used with goal formulae that contain a list of equality constraints for the provided fluents.
Arguments
metricFunction that returns a scalar value given a vector of differences between the fluent values for the current state and the goal.
fluentsA list of
Terms that refer to numeric fluents in the state.coeffsA list of scalar coefficients which each fluent value will be multiplied by before metric computation. Defaults to
1for all fluents.
SymbolicPlanners.ManhattanHeuristic — TypeManhattanHeuristic(fluents[, coeffs])Computes Manhattan distance to the goal for the specified numeric fluents. An instance of MetricHeuristic which uses the L1 norm.
SymbolicPlanners.EuclideanHeuristic — TypeEuclideanHeuristic(fluents[, coeffs])Computes Euclidean distance to the goal for the specified numeric fluents. An instance of MetricHeuristic which uses the L2 norm.
Relaxed Planning Graph Heuristics
Several relaxed planning graph heuristics are provided by SymbolicPlanners.jl. In contrast to most other planning systems, these implementations also support domains with non-Boolean fluents.
SymbolicPlanners.HSPHeuristic — TypeHSPHeuristic(op::Function)A family of relaxed planning graph heuristics, introduced by the HSP planner [1]. The heuristic precomputes a graph that stores the dependencies between all ground actions and plan-relevant conditions. The cost of achieving each action (and also the goal) is then recursively estimated as the aggregated cost of achieving each (pre)condition the action or goal depends upon, where op is an aggregation function (e.g. max or +).
In turn, the cost of achieving each condition (a.k.a. "fact") is estimated as the minimum cost among all actions that achieve that condition. Once a condition is achieved by an action, it is considered to remain true through the rest of the process, hence the relaxed nature of the heuristic.
This implementation supports domains with negative preconditions, disjunctive preconditions (i.e., or, exists), and functional preconditions (e.g. numeric comparisons, or other Boolean-valued functions of non-Boolean fluents). Functional preconditions are handled by (optimistically) assuming they become true once a constituent fluent is modified by some action.
[1] B. Bonet and H. Geffner, "Planning as Heuristic Search," Artificial Intelligence, vol. 129, no. 1, pp. 5–33, Jun. 2001, https://doi.org/10.1016/S0004-3702(01)00108-4.
SymbolicPlanners.HMax — TypeHMax()HSPHeuristic where an action's cost is the max cost of the conditions it depends upon.
SymbolicPlanners.HAdd — TypeHAdd()HSPHeuristic where an action's cost is the sum of costs of the conditions it depends upon.
SymbolicPlanners.FFHeuristic — TypeFFHeuristic()A relaxed planning graph heuristic introduced by the FastForward planner [1]. Similar to HSPHeuristic, this heuristic precomputes a graph that stores the dependencies between all ground actions and plan-relevant conditions.
To estimate the distance to the goal, a shortest-path search is performed in the graph, starting from the conditions that are true in the current state, and ending when all the goal conditions are reached. Once a condition is achieved by an action, it is considered to remain true through the rest of the search, hence the relaxed nature of the heuristic. A plan that achieves the goal conditions is reconstructed by following the action back-pointers for each achieved condition, and the cost of this plan is used as the heuristic estimate.
This implementation supports domains with negative preconditions, disjunctive preconditions (i.e., or, exists), and functional preconditions (e.g. numeric comparisons, or other Boolean-valued functions of non-Boolean fluents). Functional preconditions are handled by (optimistically) assuming they become true once a constituent fluent is modified by some action.
[1] J. Hoffmann, "FF: The Fast-Forward Planning System," AI Magazine, vol. 22, no. 3, pp. 57–57, Sep. 2001, https://doi.org/10.1609/aimag.v22i3.1572.
Backward Search Heuristics
A few relaxed planning graph heuristics are also provided for backward search:
SymbolicPlanners.HSPRHeuristic — TypeHSPRHeuristic(op::Function)A family of relaxed planning graph heuristics for backward search, introduced by the HSPr planner ("r" stands for regression) [1]. The costs of achieving a condition are estimated in the same way as the forward variant, HSPHeuristic, but this estimation is performed only once during heuristic precomputation. During heuristic evaluation, the cost from the current partial state to the start state is estimated as the aggregated cost of each condition that is true in the partial state.
[1] B. Bonet and H. Geffner, "Planning as Heuristic Search," Artificial Intelligence, vol. 129, no. 1, pp. 5–33, Jun. 2001, https://doi.org/10.1016/S0004-3702(01)00108-4.
SymbolicPlanners.HMaxR — TypeHMaxR()HSPRHeuristic for backward search, where an action's cost is the max cost of its dependencies.
SymbolicPlanners.HAddR — TypeHAddR()HSPRHeuristic for backward search, where an action's cost is the sum of costs of its dependencies.
Wrapper Heuristics
Since plans and policies can be used to estimate the cost of achieving a goal, the following wrapper heuristics are provided.
SymbolicPlanners.PlannerHeuristic — TypePlannerHeuristic(planner, [d_transform, s_transform])Computes distance to the goal based on the solution returned by planner.
If an OrderedSolution is returned, the cost of solution is used as the heuristic estimate, (plus the heuristic value of final state, if the planner is a ForwardPlanner.)
If a PolicySolution is returned, the negated value (returned by get_value) is used as the heuristic estimate.
If d_transform or s_transform are provided, this transforms the input domain or state it is passed to the planner. This can be used to relax the problem (e.g. by simplifying the domain, or adding / deleting predicates in the state).
SymbolicPlanners.PolicyValueHeuristic — TypePolicyValueHeuristic(policy::PolicySolution)Wraps a policy, and returns the negated value estimate of a state (provided by get_value) as the heuristic goal-distance estimate.
SymbolicPlanners.GoalDependentPolicyHeuristic — TypeGoalDependentPolicyHeuristic(policies::Dict, [default])Wraps a dictionary mapping planning Specifications to PolicySolutions. Given a particular specification, the heuristic looks up the corresponding policy and returns its negated estimate of a state's value as the heuristic goal-distance estimate.
If a default is provided, then this is used to construct a new policy policy = default(domain, state, spec) for a specification spec that is not found in the dictionary. Otherwise, an error is thrown.
Action Pruning Heuristics
To combine one heuristic with the action pruning functionality provided by another heuristic, a PruningHeuristic can be used.
SymbolicPlanners.PruningHeuristic — TypePruningHeuristic(heuristic::Heuristic, pruner)Combines an existing heuristic with pruner, an action pruning method that defines the filter_available and filter_relevant functions. For example, pruner may be another heuristic that filters actions.
This can be used to combine domain-specific pruning methods with domain-general goal-cost estimators.
Precomputation and Memoization
Some applications of planning algorithms require repeated calls to a planner (e.g. in Bayesian inverse planning), which may lead to repeated pre-computation of the search heuristic. In such cases, overhead can be substantially reduced by ensuring that precomputation happens only once. This can be done using the precomputed function to construct a PrecomputedHeuristic.
SymbolicPlanners.precomputed — Functionprecomputed(h::Heuristic, domain::Domain, args...)Precomputes a heuristic in advance, returning a PrecomputedHeuristic that prevents repeated pre-computation later.
SymbolicPlanners.PrecomputedHeuristic — TypePrecomputedHeuristic(heuristic::Heuristic, args...)Wraps an existing heuristic and ensures that it is precomputed, preventing repeated pre-computation on subsequent calls to precompute!.
Similarly, if heuristic computation is costly, memoization of heuristic values can lead to faster results. This can be achieved using the memoized function to construct a MemoizedHeuristic
SymbolicPlanners.memoized — Functionmemoized(h::Heuristic)Constructs a memoized version of h which caches outputs in a hash table after each evaluation of the heuristic on a new domain, state, or specification.
SymbolicPlanners.MemoizedHeuristic — TypeMemoizedHeuristic(heuristic::Heuristic)Wraps an existing heuristic and memoizes heuristic evaluations in a hash table.
In cases where both precomputation and memoization are desired, users should perform precomputation before memoization:
heuristic = memoized(precomputed(heuristic))