Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Total-order planner #645

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open

Total-order planner #645

wants to merge 42 commits into from

Conversation

bcorfman
Copy link

@bcorfman bcorfman commented Sep 7, 2017

Submitting code for planner.

…-order planning.

Also included air cargo, spare_tire, blocks world and sussman anomaly examples.
… from FolKB.

Changed blocks_world function name to three_block_tower.
Cleaned up imports and PlanningKB __repr__ function.
@norvig
Copy link
Collaborator

norvig commented Sep 9, 2017

This looks really nice -- great work!

@norvig
Copy link
Collaborator

norvig commented Sep 9, 2017

There are too many conflicts for me to resolve -- can you have a look?

@bcorfman
Copy link
Author

bcorfman commented Sep 15, 2017

One of the main issues is that the PlanningKB items are immutable (and therefore hashable and searchable) and the FOLKB items from logic.py aren't. I'll need some time to see if I can make these two object models work together, and probably a decision from you as to which way to go with the implementation.

@norvig
Copy link
Collaborator

norvig commented Mar 15, 2018

Hey @bcorfman, I know that Expr objects have a __hash__ method. I think that FOLKB should only be using these; do you know where they are not used?

@bcorfman
Copy link
Author

bcorfman commented Mar 15, 2018

Hi Dr. Norvig - I use the existing A* algorithm to solve the example planning problems. Recall that the set of explored items is maintained during the search to keep track of repeated states (line 270 in the current search.py). Any items in this set must be hashable and unique, or they will fail when they are added to the set. I use my PlanningKB as the state variable in the search Node, and it's hashable because the PlanningKB only uses frozenset variables instead of regular mutable sets. The FolKB database doesn't work as the Node.state because it's designed (because of its tell and ask methods) to be mutable -- that means its clauses variable is a mutable list, and its state can't be hashed.

@norvig
Copy link
Collaborator

norvig commented Mar 15, 2018

I got it. What about if you use tuple(kb.clauses) for the state? You could then put the clauses back into a KB when you need to.

@bcorfman
Copy link
Author

bcorfman commented Mar 15, 2018

Yep, a tuple should work for the FolKB clauses. (I remember using tuples with the Node setup for one of the Berkeley Pac-Man projects.) I guess my question is whether you want to a) just make the FolKB usable for the search algorithms, 2) combine the existing logic.py and my new planning.py code together, or 3) just pass the FolKB clause list into the PlanningKB init and then use the PlanningKB to conduct the search. I think number 3 is the easiest -- although there'd be two KBs for different purposes, my PlanningKB and Action classes use frozenset methods to quickly perform goal and precondition checks. I can make changes based on what you think is the best long-term approach.

search.py Outdated
@@ -181,7 +181,7 @@ def tree_search(problem, frontier):
while frontier:
node = frontier.pop()
if problem.goal_test(node.state):
return node
yield node
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you can't change the search algorithms to be generators instead of functions. You'll need to define a separate function if you want to yield all possible solutions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip. I will revert this in my next push.

@bcorfman
Copy link
Author

bcorfman commented May 28, 2018

I merged my planning code with the current version that's in the AIMA repo. It's not the cleanest because it was hard for me to tell the state of the code at the moment -- there seems to be a lot of design ideas in progress, and I was hesitant to start melding too much without some evaluation on my own code first.

I built a basic PDDL parser with lots of tests/examples in the pddl_files subdirectory in order to give my total order planner a thorough shakedown before submitting it. The examples are run by executing the test_planning_solutions function inside the planning module. Most of the tests are just translations of the ones that were already inside the existing planning code, but there are a couple additions too. The parser could be better about more specific error reporting when something goes wrong, but it does work solidly otherwise.

Dr. Norvig, I did try a few of your suggestions above to try merging the FolKB class and my PlanningKB class, but these ideas quickly got snarled because my PlanningKB, PlanningProblem and PlanningAction classes are very interdependent and rely heavily on Python's set classes. Modifying the existing AIMA Problem, Node or Action code would have big changes that would affect other modules, so I didn't want to do that without feedback.

Please let me know if you want to proceed any further.

@bcorfman bcorfman reopened this May 28, 2018
bcorfman added 3 commits May 28, 2018 17:11
Fixed bug on build_expr_string to correctly capitalize negative Exprs.
Created classmethods on PlanningProblem and PlanningAction to
  __init__ those classes from existing PDDL and Action objects.
Fixed __repr__ method on PlanningAction.
@bcorfman
Copy link
Author

bcorfman commented May 29, 2018

I also added a couple of classmethods to my PlanningProblem and PlanningAction classes. These alternative constructors (PlanningProblem.from_PDDL_object and PlanningAction.from_action) take existing PDDL and Action objects as parameters in order to convert the KB and actions to be searchable.

If you decide to use my PDDL parser too, I might suggest renaming the PDDL class to something more appropriate -- there's starting to be a lot of things named "PDDL" in the code, and it could create confusion.

planning.py Outdated
@@ -191,22 +221,22 @@ def air_cargo():
# Load
precond = [expr('At(c, a)'), expr('At(p, a)'), expr('Cargo(c)'), expr('Plane(p)'), expr('Airport(a)')]
effect = [expr('In(c, p)'), expr('~At(c, a)')]
load = Action(expr('Load(c, p, a)'), precond, effect)
load = PlanningAction(expr('Load(c, p, a)'), precond, effect)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better if PlanningAction accepted strings or Exprs, and then this could be

load = PlanningAction('Load(c, p, a)',
         'At(c, a), At(p, a), Cargo(c), Plane(p), Airport(a)', 
         'In(c, p), ~At(c, a)')

and similarly elsewhere.

@norvig
Copy link
Collaborator

norvig commented May 30, 2018

I think this looks pretty good, but there's a lot here stretching over a long time. Can you, @bcorfman , check in with @ad71 and perhaps @samagra14 over in aima-java, who is also working on planning now, and together come to a consensus?

@bcorfman
Copy link
Author

After I checked in, I found a bug that was exposed by one of my test cases, and I realized that the existing Action and PDDL classes were doing some things I should incorporate to fix it. I will make the changes over the next couple days so we're more in sync, and try to come to consensus on the remainder!

@ad71
Copy link
Contributor

ad71 commented May 30, 2018

@bcorfman please also have a look at #927 and tell me if you want me to change something. My total order planner is a misnomer (and can probably be handled by something as simple as a topological sort on a planning graph) and I'll get rid of it in the next commit but apart from that, the PR heavily relies on the current Action and PDDL classes. It might be a problem if those classes are changed. Do inform me if you really want to though, we can find a way around it. You can also have a look at the planning notebook to see how things are handled now. It's quite different from the time this pull request was first opened. Thanks!

@bcorfman
Copy link
Author

@ad71 I will take a look at #927 and get my bearings ... the basic issue for using any of the search algorithms (A*) for the current KBs is that they must be frozen types (i.e., tuples, frozensets, etc.) to be hashable when stored in the frontier. If we get away from a search representation for the planners (i.e., like you did with Graphplan, correct?), we can stay with the tell() and retract() methods that modify the KB. But otherwise, it seems like we'd be stuck with two different KB representations: frozen and searchable, or mutable and unsearchable. We could move between them, but I don't think we can combine the KB types together in my opinion (at least without an unfortunate breaking of the Python object model, e.g., utils.hashabledict). I always could be missing something though.

@ad71
Copy link
Contributor

ad71 commented Jun 1, 2018

@bcorfman we faced similar problems trying to make NQueensProblem and EightPuzzle work with search algorithms, so we decided to let those problems return tuples as states, ie, they didn't use KBs. When we wanted to internally change the state, we converted it to a list, carried out the changes and converted it back. It seems like a lot of work and a lot of duplicated code, but this can be done for KBs as well, though the code would be far from elegant. As of now, I can't think of a better solution...

@bcorfman
Copy link
Author

Sorry I'm so behind -- I fixed the bug I found with my TO planner. I will take a look at #927 over the next couple days and see if we can wrap this up ASAP.

  use strings or Exprs for a simplified API.
@bcorfman
Copy link
Author

bcorfman commented Jun 15, 2018

@ad71 I implemented what Dr. @norvig suggested for PlanningAction and PlanningKB ... both of their init methods take strings or Exprs now for a cleaner API.

I agree with you on translating between representations ... your observation is a good reason why Python has both lists and tuples and sets and frozensets, and they can easily be moved between. Do you think we should try the classmethod approach for moving between the types, or use some other way like a standalone function?

Probably my PlanningProblem should be renamed PlanningSearchProblem or something else to distinguish it from the mutable (PDDL) representations. Same for PlanningAction, so it isn't confused with the existing Action class ... I suggest STRIPSAction because of its negative and positive preconditions and add and remove effects that are implemented with set operations. I'm open to improved naming.

As I mentioned previously, strongly think we should rename the PDDL class though (since it has little to do with the PDDL language) ... could we rename that to PlanningProblem instead?

@ad71
Copy link
Contributor

ad71 commented Jun 15, 2018

@bcorfman I agree that PDDL is a misleading name. I'll change this in the next update. PlanningSearchProblem and STRIPSAction are good names for your classes. I think you can go ahead with it.
As for translation between representations, I'm fine with anything that works, Dr. @norvig can give you a better suggestion.

@bcorfman
Copy link
Author

bcorfman commented Jun 15, 2018 via email

@ad71
Copy link
Contributor

ad71 commented Jun 16, 2018

Editing notebooks will result in merge conflicts with the other PR. You can add the write-ups later.
If you think you won't have time later on, you can go ahead and add it now, I'll take care of merge conflicts when/if they arise.

@bcorfman
Copy link
Author

No problem; I can wait.

@bcorfman
Copy link
Author

During the interim, I'll work on some unit tests to add to test_planning.py as well.

bcorfman added 2 commits June 17, 2018 20:14
Added substitution in PlanningSearchProblem.actions() for valid actions.
Added __eq__ special method for comparing STRIPSAction objects, both for
 correctness and simpler unit testing.
Simplified print_solution() function since argument substitution went
   away above.
Changed test_planning_solutions() function to run_planning_solutions()
   so it wouldn't get triggered by py.test.
weasdown added a commit to weasdown/aima-python that referenced this pull request Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants