Skip to content

Commit

Permalink
Breaking changes to "search" planners. Folded action cost calculation…
Browse files Browse the repository at this point in the history
… into the same interface as the one that contains EstimateCost - IHeuristic. Renamed it to IStrategy since its no longer just about the heuristic. This has enabled StateSpaceSearch and GoalSpaceSearch to be a little less ugly.
  • Loading branch information
sdcondon committed Jan 7, 2023
1 parent a49d55c commit 009f647
Show file tree
Hide file tree
Showing 25 changed files with 222 additions and 254 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,16 @@ namespace SCClassicalPlanning.Planning.Search
/// </summary>
public class GoalSpaceSearch_LiftedWithKB : IPlanner
{
private readonly IHeuristic heuristic;
private readonly IStrategy strategy;
private readonly InvariantInspector? invariantInspector;
private readonly Func<Action, float> getActionCost;

/// <summary>
/// Initializes a new instance of the <see cref="GoalSpaceSearch_LiftedWithKB"/> class that attempts to minimise the number of actions in the resulting plan.
/// Initializes a new instance of the <see cref="GoalSpaceSearch_LiftedWithKB"/> class.
/// </summary>
/// <param name="heuristic">The heuristic to use - the returned cost will be interpreted as the estimated number of actions that need to be performed.</param>
public GoalSpaceSearch_LiftedWithKB(IHeuristic heuristic, IKnowledgeBase? invariantsKB = null)
: this(heuristic, a => 1f, invariantsKB)
/// <param name="strategy">The strategy to use.</param>
public GoalSpaceSearch_LiftedWithKB(IStrategy strategy, IKnowledgeBase? invariantsKB = null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="GoalSpaceSearch_LiftedWithKB"/> class that attempts to minimise the total "cost" of actions in the resulting plan.
/// </summary>
/// <param name="heuristic">The heuristic to use - with the returned cost will be interpreted as the estimated total cost of the actions that need to be performed.</param>
/// <param name="getActionCost">A delegate to retrieve the cost of an action.</param>
public GoalSpaceSearch_LiftedWithKB(IHeuristic heuristic, Func<Action, float> getActionCost, IKnowledgeBase? invariantsKB = null)
{
this.heuristic = heuristic;
this.getActionCost = getActionCost;
this.strategy = strategy;
this.invariantInspector = invariantsKB != null ? new InvariantInspector(invariantsKB) : null;
}

Expand All @@ -55,7 +43,7 @@ public GoalSpaceSearch_LiftedWithKB(IHeuristic heuristic, Func<Action, float> ge
/// </summary>
/// <param name="problem">The problem to create a plan for.</param>
/// <returns></returns>
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, heuristic, getActionCost, invariantInspector);
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, strategy, invariantInspector);

/// <inheritdoc />
IPlanningTask IPlanner.CreatePlanningTask(Problem problem) => CreatePlanningTask(problem);
Expand All @@ -70,16 +58,16 @@ public class PlanningTask : SteppablePlanningTask<(Goal, Action, Goal)>
private bool isComplete;
private Plan? result;

internal PlanningTask(Problem problem, IHeuristic heuristic, Func<Action, float> getActionCost, InvariantInspector? invariantInspector)
internal PlanningTask(Problem problem, IStrategy strategy, InvariantInspector? invariantInspector)
{
Domain = problem.Domain;
InvariantInspector = invariantInspector;

search = new AStarSearch<GoalSpaceNode, GoalSpaceEdge>(
source: new GoalSpaceNode(this, problem.Goal),
isTarget: n => problem.InitialState.GetSatisfyingSubstitutions(n.Goal).Any(),
getEdgeCost: e => getActionCost(e.Action),
getEstimatedCostToTarget: n => heuristic.EstimateCost(problem.InitialState, n.Goal));
getEdgeCost: e => strategy.GetCost(e.Action),
getEstimatedCostToTarget: n => strategy.EstimateCost(problem.InitialState, n.Goal));

CheckForSearchCompletion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,23 @@ namespace SCClassicalPlanning.Planning.Search
/// </summary>
public class GoalSpaceSearch_LiftedWithoutKB : IPlanner
{
private readonly IHeuristic heuristic;
private readonly Func<Action, float> getActionCost;
private readonly IStrategy strategy;

/// <summary>
/// Initializes a new instance of the <see cref="GoalSpaceSearch_LiftedWithoutKB"/> class that attempts to minimise the number of actions in the resulting plan.
/// Initializes a new instance of the <see cref="GoalSpaceSearch_LiftedWithoutKB"/> class.
/// </summary>
/// <param name="heuristic">The heuristic to use - the returned cost will be interpreted as the estimated number of actions that need to be performed.</param>
public GoalSpaceSearch_LiftedWithoutKB(IHeuristic heuristic)
: this(heuristic, a => 1f)
/// <param name="strategy">The strategy to use.</param>
public GoalSpaceSearch_LiftedWithoutKB(IStrategy strategy)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="GoalSpaceSearch_LiftedWithoutKB"/> class that attempts to minimise the total "cost" of actions in the resulting plan.
/// </summary>
/// <param name="heuristic">The heuristic to use - with the returned cost will be interpreted as the estimated total cost of the actions that need to be performed.</param>
/// <param name="getActionCost">A delegate to retrieve the cost of an action.</param>
public GoalSpaceSearch_LiftedWithoutKB(IHeuristic heuristic, Func<Action, float> getActionCost)
{
this.heuristic = heuristic;
this.getActionCost = getActionCost;
this.strategy = strategy;
}

/// <summary>
/// Creates a (concretely-typed) planning task to work on solving a given problem.
/// </summary>
/// <param name="problem">The problem to create a plan for.</param>
/// <returns></returns>
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, heuristic, getActionCost);
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, strategy);

/// <inheritdoc />
IPlanningTask IPlanner.CreatePlanningTask(Problem problem) => CreatePlanningTask(problem);
Expand All @@ -69,13 +57,13 @@ public class PlanningTask : SteppablePlanningTask<(Goal, Action, Goal)>
private bool isComplete;
private Plan? result;

internal PlanningTask(Problem problem, IHeuristic heuristic, Func<Action, float> getActionCost)
internal PlanningTask(Problem problem, IStrategy strategy)
{
search = new AStarSearch<GoalSpaceNode, GoalSpaceEdge>(
source: new GoalSpaceNode(problem.Domain, problem.Goal),
isTarget: n => problem.InitialState.GetSatisfyingSubstitutions(n.Goal).Any(),
getEdgeCost: e => getActionCost(e.Action),
getEstimatedCostToTarget: n => heuristic.EstimateCost(problem.InitialState, n.Goal));
getEdgeCost: e => strategy.GetCost(e.Action),
getEstimatedCostToTarget: n => strategy.EstimateCost(problem.InitialState, n.Goal));

CheckForSearchCompletion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,16 @@ namespace SCClassicalPlanning.Planning.Search
/// </summary>
public class GoalSpaceSearch_PropositionalWithKB : IPlanner
{
private readonly IHeuristic heuristic;
private readonly IStrategy strategy;
private readonly InvariantInspector? invariantInspector;
private readonly Func<Action, float> getActionCost;

/// <summary>
/// Initializes a new instance of the <see cref="GoalSpaceSearch_PropositionalWithKB"/> class that attempts to minimise the number of actions in the resulting plan.
/// Initializes a new instance of the <see cref="GoalSpaceSearch_PropositionalWithKB"/> class.
/// </summary>
/// <param name="heuristic">The heuristic to use - the returned cost will be interpreted as the estimated number of actions that need to be performed.</param>
public GoalSpaceSearch_PropositionalWithKB(IHeuristic heuristic, IKnowledgeBase? invariantsKB = null)
: this(heuristic, a => 1f, invariantsKB)
/// <param name="strategy">The strategy to use.</param>
public GoalSpaceSearch_PropositionalWithKB(IStrategy strategy, IKnowledgeBase? invariantsKB = null)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="GoalSpaceSearch_PropositionalWithKB"/> class that attempts to minimise the total "cost" of actions in the resulting plan.
/// </summary>
/// <param name="heuristic">The heuristic to use - with the returned cost will be interpreted as the estimated total cost of the actions that need to be performed.</param>
/// <param name="getActionCost">A delegate to retrieve the cost of an action.</param>
public GoalSpaceSearch_PropositionalWithKB(IHeuristic heuristic, Func<Action, float> getActionCost, IKnowledgeBase? invariantsKB = null)
{
this.heuristic = heuristic;
this.getActionCost = getActionCost;
this.strategy = strategy;
this.invariantInspector = invariantsKB != null ? new InvariantInspector(invariantsKB) : null;
}

Expand All @@ -57,7 +45,7 @@ public GoalSpaceSearch_PropositionalWithKB(IHeuristic heuristic, Func<Action, fl
/// </summary>
/// <param name="problem">The problem to create a plan for.</param>
/// <returns></returns>
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, heuristic, getActionCost, invariantInspector);
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, strategy, invariantInspector);

/// <inheritdoc />
IPlanningTask IPlanner.CreatePlanningTask(Problem problem) => CreatePlanningTask(problem);
Expand All @@ -72,16 +60,16 @@ public class PlanningTask : SteppablePlanningTask<(Goal, Action, Goal)>
private bool isComplete;
private Plan? result;

internal PlanningTask(Problem problem, IHeuristic heuristic, Func<Action, float> getActionCost, InvariantInspector? invariantInspector)
internal PlanningTask(Problem problem, IStrategy strategy, InvariantInspector? invariantInspector)
{
Problem = problem;
InvariantInspector = invariantInspector;

search = new AStarSearch<GoalSpaceNode, GoalSpaceEdge>(
source: new GoalSpaceNode(this, problem.Goal),
isTarget: n => n.Goal.IsSatisfiedBy(problem.InitialState),
getEdgeCost: e => getActionCost(e.Action),
getEstimatedCostToTarget: n => heuristic.EstimateCost(problem.InitialState, n.Goal));
getEdgeCost: e => strategy.GetCost(e.Action),
getEstimatedCostToTarget: n => strategy.EstimateCost(problem.InitialState, n.Goal));

CheckForSearchCompletion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,23 @@ namespace SCClassicalPlanning.Planning.Search
/// </summary>
public class GoalSpaceSearch_PropositionalWithoutKB : IPlanner
{
private readonly IHeuristic heuristic;
private readonly Func<Action, float> getActionCost;

/// <summary>
/// Initializes a new instance of the <see cref="ForwardStateSpaceSearch"/> class that attempts to minimise the number of actions in the resulting plan.
/// </summary>
/// <param name="heuristic">The heuristic to use - the returned cost will be interpreted as the estimated number of actions that need to be performed.</param>
public GoalSpaceSearch_PropositionalWithoutKB(IHeuristic heuristic)
: this(heuristic, a => 1f)
{
}
private readonly IStrategy strategy;

/// <summary>
/// Initializes a new instance of the <see cref="ForwardStateSpaceSearch"/> class that attempts to minimise the total "cost" of actions in the resulting plan.
/// Initializes a new instance of the <see cref="GoalSpaceSearch_PropositionalWithoutKB"/> class.
/// </summary>
/// <param name="heuristic">The heuristic to use - with the returned cost will be interpreted as the estimated total cost of the actions that need to be performed.</param>
/// <param name="getActionCost">A delegate to retrieve the cost of an action.</param>
public GoalSpaceSearch_PropositionalWithoutKB(IHeuristic heuristic, Func<Action, float> getActionCost)
/// <param name="strategy">The strategy to use.</param>
public GoalSpaceSearch_PropositionalWithoutKB(IStrategy strategy)
{
this.heuristic = heuristic;
this.getActionCost = getActionCost;
this.strategy = strategy;
}

/// <summary>
/// Creates a (concretely-typed) planning task to work on solving a given problem.
/// </summary>
/// <param name="problem">The problem to create a plan for.</param>
/// <returns></returns>
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, heuristic, getActionCost);
public PlanningTask CreatePlanningTask(Problem problem) => new(problem, strategy);

/// <inheritdoc />
IPlanningTask IPlanner.CreatePlanningTask(Problem problem) => CreatePlanningTask(problem);
Expand All @@ -72,13 +60,13 @@ public class PlanningTask : SteppablePlanningTask<(Goal, Action, Goal)>
private bool isComplete;
private Plan? result;

internal PlanningTask(Problem problem, IHeuristic heuristic, Func<Action, float> getActionCost)
internal PlanningTask(Problem problem, IStrategy strategy)
{
search = new AStarSearch<GoalSpaceNode, GoalSpaceEdge>(
source: new GoalSpaceNode(problem, problem.Goal),
isTarget: n => n.Goal.IsSatisfiedBy(problem.InitialState),
getEdgeCost: e => getActionCost(e.Action),
getEstimatedCostToTarget: n => heuristic.EstimateCost(problem.InitialState, n.Goal));
getEdgeCost: e => strategy.GetCost(e.Action),
getEstimatedCostToTarget: n => strategy.EstimateCost(problem.InitialState, n.Goal));

CheckForSearchCompletion();
}
Expand Down
20 changes: 10 additions & 10 deletions src/SCClassicalPlanning.Benchmarks/Planning/PlannerBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
using SCClassicalPlanning.ExampleDomains.FromAIaMA;
using SCClassicalPlanning.Planning;
using SCClassicalPlanning.Planning.Search;
using SCClassicalPlanning.Planning.Search.Heuristics;
using SCFirstOrderLogic;
using SCFirstOrderLogic.Inference;
using static SCFirstOrderLogic.SentenceCreation.OperableSentenceFactory;
using static SCClassicalPlanning.ExampleDomains.FromAIaMA.BlocksWorld;
using SCFirstOrderLogic.Inference.Resolution;
using SCClassicalPlanning.Planning.Search.Strategies;

namespace SCClassicalPlanning.Benchmarks.Planning
{
[MemoryDiagnoser]
[InProcess]
public class PlannerBenchmarks
{
public record TestCase(string Label, Problem Problem, IHeuristic Heuristic, IKnowledgeBase InvariantsKB)
public record TestCase(string Label, Problem Problem, IStrategy Strategy, IKnowledgeBase InvariantsKB)
{
public override string ToString() => Label;
}
Expand All @@ -25,13 +25,13 @@ public record TestCase(string Label, Problem Problem, IHeuristic Heuristic, IKno
new(
Label: "Air Cargo",
Problem: AirCargo.ExampleProblem,
Heuristic: new IgnorePreconditionsGreedySetCover(AirCargo.Domain),
Strategy: new IgnorePreconditionsGreedySetCover(AirCargo.Domain),
InvariantsKB: MakeInvariantsKB(Array.Empty<Sentence>())),

new(
Label: "Blocks - Small",
Problem: BlocksWorld.ExampleProblem,
Heuristic: new IgnorePreconditionsGreedySetCover(BlocksWorld.Domain),
Strategy: new IgnorePreconditionsGreedySetCover(BlocksWorld.Domain),
InvariantsKB: MakeInvariantsKB(new Sentence[]
{
// TODO: slicker support for unique names assumption worth looking into at some point..
Expand All @@ -53,13 +53,13 @@ public record TestCase(string Label, Problem Problem, IHeuristic Heuristic, IKno
new(
Label: "Spare Tire",
Problem: SpareTire.ExampleProblem,
Heuristic: new IgnorePreconditionsGreedySetCover(SpareTire.Domain),
Strategy: new IgnorePreconditionsGreedySetCover(SpareTire.Domain),
InvariantsKB: MakeInvariantsKB(Array.Empty<Sentence>())),

////new(
//// Label: "Blocks - Large",
//// Problem: BlocksWorld.LargeExampleProblem,
//// Heuristic: new IgnorePreconditionsGreedySetCover(BlocksWorld.Domain),
//// Strategy: new IgnorePreconditionsGreedySetCover(BlocksWorld.Domain),
//// InvariantsKB: MakeInvariantsKB(new Sentence[]
//// {
//// Block(new Constant("blockA")),
Expand Down Expand Up @@ -102,25 +102,25 @@ public record TestCase(string Label, Problem Problem, IHeuristic Heuristic, IKno
[Benchmark]
public Plan StateSpaceSearch()
{
return new StateSpaceSearch(CurrentTestCase!.Heuristic).CreatePlan(CurrentTestCase.Problem);
return new StateSpaceSearch(CurrentTestCase!.Strategy).CreatePlan(CurrentTestCase.Problem);
}

[Benchmark]
public Plan GoalSpaceSearch()
{
return new GoalSpaceSearch(CurrentTestCase!.Heuristic).CreatePlan(CurrentTestCase.Problem);
return new GoalSpaceSearch(CurrentTestCase!.Strategy).CreatePlan(CurrentTestCase.Problem);
}

[Benchmark]
public Plan GoalSpaceSearch_PropositionalWithoutKB()
{
return new GoalSpaceSearch_PropositionalWithoutKB(CurrentTestCase!.Heuristic).CreatePlan(CurrentTestCase.Problem);
return new GoalSpaceSearch_PropositionalWithoutKB(CurrentTestCase!.Strategy).CreatePlan(CurrentTestCase.Problem);
}

[Benchmark]
public Plan GoalSpaceSearch_PropositionalWithKB()
{
return new GoalSpaceSearch_PropositionalWithKB(CurrentTestCase!.Heuristic, CurrentTestCase.InvariantsKB).CreatePlan(CurrentTestCase.Problem);
return new GoalSpaceSearch_PropositionalWithKB(CurrentTestCase!.Strategy, CurrentTestCase.InvariantsKB).CreatePlan(CurrentTestCase.Problem);
}

private static IKnowledgeBase MakeInvariantsKB(IEnumerable<Sentence> invariants)
Expand Down
Loading

0 comments on commit 009f647

Please sign in to comment.