diff --git a/src/main/java/edu/rpi/rair/Action.java b/src/main/java/edu/rpi/rair/Action.java index 3b51a37..ae8bf03 100644 --- a/src/main/java/edu/rpi/rair/Action.java +++ b/src/main/java/edu/rpi/rair/Action.java @@ -2,6 +2,7 @@ package edu.rpi.rair; import com.naveensundarg.shadow.prover.representations.formula.And; import com.naveensundarg.shadow.prover.representations.formula.Formula; +import com.naveensundarg.shadow.prover.representations.value.Compound; import com.naveensundarg.shadow.prover.representations.value.Value; import com.naveensundarg.shadow.prover.representations.value.Variable; import com.naveensundarg.shadow.prover.utils.CollectionUtils; @@ -27,6 +28,8 @@ public class Action { private int weight; + private final Compound shorthand; + private Action(String name, Set preconditions, Set additions, Set deletions, List freeVariables) { this.name = name; this.preconditions = preconditions; @@ -46,6 +49,35 @@ public class Action { this.weight = preconditions.stream().mapToInt(Formula::getWeight).sum() + additions.stream().mapToInt(Formula::getWeight).sum() + deletions.stream().mapToInt(Formula::getWeight).sum(); + + List valuesList = freeVariables.stream().collect(Collectors.toList());; + this.shorthand = new Compound(name, valuesList); + } + + private Action(String name, Set preconditions, Set additions, + Set deletions, List freeVariables, + Compound shorthand + ) { + this.name = name; + this.preconditions = preconditions; + + this.additions = additions; + this.deletions = deletions; + List computedFreeVariables = preconditions. + stream(). + map(x -> Sets.difference(x.variablesPresent(), x.boundVariablesPresent())). + reduce(Sets.newSet(), Sets::union). + stream().sorted().collect(Collectors.toList()); + + this.freeVariables = freeVariables; + + this.precondition = new And(preconditions.stream().collect(Collectors.toList())); + + this.weight = preconditions.stream().mapToInt(Formula::getWeight).sum() + + additions.stream().mapToInt(Formula::getWeight).sum() + + deletions.stream().mapToInt(Formula::getWeight).sum(); + + this.shorthand = shorthand; } @@ -97,16 +129,20 @@ public class Action { newFreeVraibles.add(var); } } - return new Action(name, newPreconditions, newAdditions, newDeletions, newFreeVraibles); + + List valuesList = freeVariables.stream().collect(Collectors.toList());; + Compound shorthand = (Compound)(new Compound(name, valuesList)).apply(binding); + return new Action(name, newPreconditions, newAdditions, newDeletions, newFreeVraibles, shorthand); } + + public String getName() { + return name; + } + + @Override public String toString() { - return "Action{" + - "preconditions=" + preconditions + - ", additions=" + additions + - ", deletions=" + deletions + - ", name='" + name + '\'' + - '}'; + return shorthand.getArguments().length == 0? name: shorthand.toString(); } @Override diff --git a/src/main/java/edu/rpi/rair/utils/Commons.java b/src/main/java/edu/rpi/rair/utils/Commons.java new file mode 100644 index 0000000..787f9ed --- /dev/null +++ b/src/main/java/edu/rpi/rair/utils/Commons.java @@ -0,0 +1,12 @@ +package edu.rpi.rair.utils; + +import edu.rpi.rair.State; +import us.bpsm.edn.parser.Parseable; + +/** + * Created by naveensundarg on 1/15/17. + */ +public class Commons { + + +} diff --git a/src/main/java/edu/rpi/rair/utils/PlanningProblem.java b/src/main/java/edu/rpi/rair/utils/PlanningProblem.java index e78146a..b598cd2 100644 --- a/src/main/java/edu/rpi/rair/utils/PlanningProblem.java +++ b/src/main/java/edu/rpi/rair/utils/PlanningProblem.java @@ -2,6 +2,7 @@ package edu.rpi.rair.utils; import clojure.lang.Obj; import com.naveensundarg.shadow.prover.representations.formula.Formula; +import com.naveensundarg.shadow.prover.representations.value.Value; import com.naveensundarg.shadow.prover.representations.value.Variable; import com.naveensundarg.shadow.prover.utils.CollectionUtils; import com.naveensundarg.shadow.prover.utils.Reader; @@ -16,10 +17,8 @@ import us.bpsm.edn.parser.Token; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.security.Key; +import java.util.*; import java.util.stream.Collectors; /** @@ -27,11 +26,13 @@ import java.util.stream.Collectors; */ public class PlanningProblem { - public Set background; - public Set actions; - public State start; - public State goal; - public String name; + private Set background; + private Set actions; + private State start; + private State goal; + private String name; + private Optional>> expectedActionSequencesOpt; + private Map actionMap; private static final Keyword BACKGROUND = Keyword.newKeyword("background"); private static final Keyword START = Keyword.newKeyword("start"); @@ -45,6 +46,7 @@ public class PlanningProblem { private static final Symbol ACTION_DEFINER = Symbol.newSymbol("define-action"); + private static final Keyword EXPECTED_PLANS = Keyword.newKeyword("expected-plans"); public PlanningProblem(String name, Set background, State start, State goal, Set actions) { @@ -53,6 +55,19 @@ public class PlanningProblem { this.actions = actions; this.goal = goal; this.name = name; + this.actionMap = CollectionUtils.newMap(); + this.expectedActionSequencesOpt = Optional.empty(); + } + + public PlanningProblem(String name, Set background, State start, State goal, Set actions, Set> expectedActionSequences) { + + this.background = background; + this.start = start; + this.actions = actions; + this.goal = goal; + this.name = name; + this.actionMap = CollectionUtils.newMap(); + this.expectedActionSequencesOpt = Optional.of(expectedActionSequences); } public static List readFromFile(InputStream inputStream) throws Reader.ParsingException { @@ -76,9 +91,44 @@ public class PlanningProblem { String name = planningProblemSpec.get(NAME).toString(); Set actions = readActionsFrom(actionDefinitions); + Map actionMap = CollectionUtils.newMap(); - planningProblems.add(new PlanningProblem(name, background, State.initializeWith(start), + actions.stream().forEach(action->{ + actionMap.put(action.getName(), action); + }); + if(planningProblemSpec.containsKey(EXPECTED_PLANS)){ + List plans = (List) planningProblemSpec.get(EXPECTED_PLANS); + + Set> expectedActions = plans.stream().map(plan->{ + + List instantActionList = (List) plan; + + List actionsList = instantActionList.stream().map(x -> { + try { + return readInstantiatedAction(actionMap, x); + } catch (Reader.ParsingException e) { + return null; + } + }).collect(Collectors.toList()); + + if(actionsList.stream().anyMatch(Objects::isNull)){ + return null; + } else { + return actionsList; + } + + }).collect(Collectors.toSet()); + + + planningProblems.add(new PlanningProblem(name, background, State.initializeWith(start), + State.initializeWith(goal), actions, expectedActions)); + } else { + + planningProblems.add(new PlanningProblem(name, background, State.initializeWith(start), State.initializeWith(goal), actions)); + } + + nextValue = parser.nextValue(parseable); } @@ -87,6 +137,44 @@ public class PlanningProblem { } + + + private static Action readInstantiatedAction(Map actionMap, Object instantiatedActionSpec) throws Reader.ParsingException { + + if(instantiatedActionSpec instanceof List){ + + List instActionList = (List) instantiatedActionSpec; + String name = instActionList.get(0).toString(); + Action general = actionMap.get(name); + + List variables = general.openVars(); + if(variables.size()!=instActionList.size()-1){ + + throw new AssertionError("Not a proper instantiation of "+ name); + + } + + Map binding = CollectionUtils.newMap(); + for(int i = 1; i readActionsFrom(List actionSpecs) throws Reader.ParsingException { Set actions = actionSpecs.stream().map(spec -> { @@ -156,6 +244,34 @@ public class PlanningProblem { } + public Set getBackground() { + return background; + } + + public Set getActions() { + return actions; + } + + public State getStart() { + return start; + } + + public State getGoal() { + return goal; + } + + public String getName() { + return name; + } + + public Optional>> getExpectedActionSequencesOpt() { + return expectedActionSequencesOpt; + } + + public Map getActionMap() { + return actionMap; + } + @Override public String toString() { return "PlanningProblem{" + diff --git a/src/main/resources/edu/rpi/rair/completeness_problems.clj b/src/main/resources/edu/rpi/rair/completeness_problems.clj index f02abad..cc8d55a 100644 --- a/src/main/resources/edu/rpi/rair/completeness_problems.clj +++ b/src/main/resources/edu/rpi/rair/completeness_problems.clj @@ -8,7 +8,7 @@ :additions [R] :deletions [Q]})] - :expected-plan [a1] + :expected-plans ([a1]) } @@ -119,4 +119,54 @@ [(define-action post-new-bid (?number) {:preconditions [(bid ?number)] :additions [(bid ($$sum 1 ?number))] - :deletions [(bid ?number)]})]} + :deletions [(bid ?number)]})] + + :expected-plans ([(post-new-bid 0) + (post-new-bid 1) + (post-new-bid 2) + (post-new-bid 3) + (post-new-bid 4)])} + +{:name "Moving Between Rooms" + :background [ (not (= room1 room2))] + :start [(In self room1) + (In commander room2) + (In prisoner room1) + (Open (door room2)) + (not (Open (door room1))) ] + :goal [(In prisoner room2)] + :actions + [(define-action open-door [?room] + {:preconditions [(not (Open (door ?room)))] + :additions [(Open (door ?room))] + :deletions [(not (Open (door ?room)))]}) + + (define-action move-thing-from-to [?thing ?room1 ?room2] + {:preconditions [(not (= ?room1 ?room2)) + (In ?thing ?room1) + (Open (door ?room1)) + (Open (door ?room2))] + + :additions [(In ?thing ?room2)] + :deletions [(In ?thing ?room1) + (In self ?room1)]}) + (define-action accompany-from-to [?thing ?room1 ?room2] + {:preconditions [(not (= ?room1 ?room2)) + (In self ?room1) + (In ?thing ?room1) + (Open (door ?room1)) + (Open (door ?room2))] + + :additions [(In ?thing ?room2) + (In ?self ?room2)] + :deletions [(In ?thing ?room1) + (In self ?room1)]})] + + :expected-plans ([(open-door room1) + (accompany-from-to prisoner room1 room2)] + + [(open-door room1) + (move-thing-from-to prisoner room1 room2)]) + + } + diff --git a/src/main/resources/edu/rpi/rair/debug.clj b/src/main/resources/edu/rpi/rair/debug.clj new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/main/resources/edu/rpi/rair/debug.clj @@ -0,0 +1 @@ + diff --git a/src/main/resources/edu/rpi/rair/goal_tracking_tests.clj b/src/main/resources/edu/rpi/rair/goal_tracking_tests.clj index 572888f..1715e5b 100644 --- a/src/main/resources/edu/rpi/rair/goal_tracking_tests.clj +++ b/src/main/resources/edu/rpi/rair/goal_tracking_tests.clj @@ -1,27 +1,30 @@ -{ - :name "test 1" - :background [ (forall (?x ?y ?room1 ?room2) - (if (and (Interrogates ?x ?y) - (In ?x ?room1) - (In ?y ?room2)) - (= ?room1 ?room2))) - +{:name "Sim" + :background [] + :start [(In self room1) (In commander room2) - () - ] - :start [Be_In_Room - (Closed (door room1)) - (Accompany self prisoner)] + (In prisoner room1) + (Open (door room2)) + (not (Open (door room1))) ] - :goalSequence [ - [G1 1 [(In prisoner1 room1)]] - [G2 1 [(Closed (door room1))]] - [G3 1 [(Accompany self prisoner)]] + :actions [(define-action open-door [?room] + {:preconditions [(not (Open (door ?room)))] + :additions [(Open (door ?room))] + :deletions [(not (Open (door ?room)))]}) - [G4 2 [(Interrogates command robot)]] - [G5 2 []] - ] + (define-action accompany-from-to [?thing ?room1 ?room2] + {:preconditions [(In self ?room1) + (In ?thing ?room1) + (Open (door ?room1)) + (Open (door ?room2))] + + :additions [(In ?thing ?room2) + (In ?self ?room2)] + :deletions [(In ?thing ?room1) + (In self ?room1)]})] + + :expected-plans ([(open-door room1) + (accompany-from-to prisoner room1 room2)]) } \ No newline at end of file diff --git a/src/test/java/edu/rpi/rair/DepthFirstPlannerTest.java b/src/test/java/edu/rpi/rair/DepthFirstPlannerTest.java index 43738be..51ec9e5 100644 --- a/src/test/java/edu/rpi/rair/DepthFirstPlannerTest.java +++ b/src/test/java/edu/rpi/rair/DepthFirstPlannerTest.java @@ -10,6 +10,7 @@ import org.testng.annotations.Test; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import static org.testng.Assert.*; @@ -33,7 +34,7 @@ public class DepthFirstPlannerTest { Planner depthFirstPlanner = new DepthFirstPlanner(); PlanningProblem planningProblem = planningProblemList.get(2); - System.out.println(depthFirstPlanner.plan(planningProblem.background, planningProblem.actions, planningProblem.start, planningProblem.goal)); + System.out.println(depthFirstPlanner.plan(planningProblem.getBackground(), planningProblem.getActions(), planningProblem.getStart(), planningProblem.getGoal())); } @DataProvider @@ -55,16 +56,29 @@ public class DepthFirstPlannerTest { } + @Test(dataProvider = "testCompletenessDataProvider") public void testCompletness(PlanningProblem planningProblem) throws Exception { Optional> possiblePlans = depthFirstPlanner.plan( - planningProblem.background, - planningProblem.actions, - planningProblem.start, - planningProblem.goal); + planningProblem.getBackground(), + planningProblem.getActions(), + planningProblem.getStart(), + planningProblem.getGoal()); Assert.assertTrue(possiblePlans.isPresent()); + Set plans = possiblePlans.get(); + + if(planningProblem.getExpectedActionSequencesOpt().isPresent()){ + + Set> actionSequences = plans.stream().map(Plan::getActions).collect(Collectors.toSet()); + Set> expectedActionSequences = planningProblem.getExpectedActionSequencesOpt().get(); + + + Assert.assertEquals(actionSequences, expectedActionSequences); + } + + } } \ No newline at end of file diff --git a/target/classes/edu/rpi/rair/debug.clj b/target/classes/edu/rpi/rair/debug.clj new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/target/classes/edu/rpi/rair/debug.clj @@ -0,0 +1 @@ + diff --git a/target/classes/edu/rpi/rair/goal_tracking_tests.clj b/target/classes/edu/rpi/rair/goal_tracking_tests.clj new file mode 100644 index 0000000..1715e5b --- /dev/null +++ b/target/classes/edu/rpi/rair/goal_tracking_tests.clj @@ -0,0 +1,30 @@ +{:name "Sim" + :background [] + :start [(In self room1) + (In commander room2) + (In prisoner room1) + (Open (door room2)) + (not (Open (door room1))) ] + + :actions [(define-action open-door [?room] + {:preconditions [(not (Open (door ?room)))] + :additions [(Open (door ?room))] + :deletions [(not (Open (door ?room)))]}) + + + + (define-action accompany-from-to [?thing ?room1 ?room2] + {:preconditions [(In self ?room1) + (In ?thing ?room1) + (Open (door ?room1)) + (Open (door ?room2))] + + :additions [(In ?thing ?room2) + (In ?self ?room2)] + :deletions [(In ?thing ?room1) + (In self ?room1)]})] + + :expected-plans ([(open-door room1) + (accompany-from-to prisoner room1 room2)]) + + } \ No newline at end of file diff --git a/target/classes/edu/rpi/rair/utils/Commons.class b/target/classes/edu/rpi/rair/utils/Commons.class new file mode 100644 index 0000000..c3e389f Binary files /dev/null and b/target/classes/edu/rpi/rair/utils/Commons.class differ