Package graphplan

Source Code of graphplan.Graphplan

/*
* ---------------------------------------------------------------------------
* Copyright (C) 2010  Felipe Meneguzzi
* JavaGP is distributed under LGPL. See file LGPL.txt in this directory.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* To contact the author:
* http://www.meneguzzi.eu/felipe/contact.html
* ---------------------------------------------------------------------------
*/
package graphplan;

import graphplan.domain.DomainDescription;
import graphplan.domain.Operator;
import graphplan.domain.Proposition;
import graphplan.flyweight.OperatorFactory;
import graphplan.flyweight.OperatorFactoryException;
import graphplan.graph.ActionLevel;
import graphplan.graph.PropositionLevel;
import graphplan.graph.algorithm.SolutionExtractionVisitor;
import graphplan.graph.algorithm.TimeoutSolutionExtractionVisitor;
import graphplan.graph.draw.DotGraphDrawVisitor;
import graphplan.graph.memo.mutexes.StaticMutexesTable;
import graphplan.graph.planning.PlanningGraph;
import graphplan.graph.planning.PlanningGraphException;
import graphplan.graph.planning.cwa.PlanningGraphClosedWorldAssumption;
import graphplan.parser.PDDLPlannerAdapter;
import graphplan.parser.ParseException;
import graphplan.parser.PlannerParser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import pddl4j.ParserException;

/**
* Main class and accessor for the Graphplan algorithm
* @author Felipe Meneguzzi
*
*/
public class Graphplan {
  private static final Logger logger = Logger.getLogger(Graphplan.class.getName());
  public static final String LOGGER_FILE = "logging.properties";
 
  protected int maxLevels = Integer.MAX_VALUE;
  private PlanningGraph planningGraph;
  private SolutionExtractionVisitor solutionExtraction;
  private boolean pddl = true;

  public static boolean noopsFirst = false;
  public static boolean operatorsLatest = true;
  public static boolean propositionsSmallest = true;
  public static boolean sortGoals = false;
 
  public static void main(String[] args) {
    setupLogger();
    Graphplan graphplan = new Graphplan();
    InputStream operators = null;
    InputStream problem = null;
   
    String problemFilename = null;
    String domainFilename = null;
   
    String graphDrawFile = null;
   
    long timeout = 0;
    boolean argsOk = true;
    boolean pddl = true;
   
    for(int i=0; i<args.length && argsOk; i++) {
      if(args[i].equals("-nopddl")) {
        pddl = false;
        graphplan.setPddl(pddl);
      }else if(args[i].equals("-d")) { /* The domain argument */
        if(++i < args.length && !args[i].startsWith("-")) {
          domainFilename = args[i];
        } else {
          logger.warning("-d argument requires a filename with the domain");
          argsOk = false;
        }
      } else if(args[i].equals("-p")) { /* The problem argument */
        if(++i < args.length && !args[i].startsWith("-")) {
          problemFilename = args[i];
        } else {
          logger.warning("-p argument requires a filename with the problem");
          argsOk = false;
        }
      } else if(args[i].equals("-maxlevels")) {
        if(++i < args.length && !args[i].startsWith("-")) {
          try {
            int levels = Integer.parseInt(args[i]);
            if(levels > 0) {
              graphplan.setMaxLevels(levels);
            }
          } catch (NumberFormatException e) {
            logger.warning("-maxlevels argument requires a positive integer number of levels");
          }
        } else {
          logger.warning("-maxlevels argument requires a positive integer number of levels");
          argsOk = false;
        }
      } else if(args[i].equals("-timeout")) {
        if(++i < args.length && !args[i].startsWith("-")) {
          try {
            timeout = Long.parseLong(args[i]);
          } catch (NumberFormatException e) {
            logger.warning("-timeout argument requires a positive integer amount of time");
          }
        } else {
          logger.warning("-timeout argument requires a positive integer amount of time");
          argsOk = false;
        }
      } else if(args[i].equals("-noopsFirst")) {
        noopsFirst = true;
        operatorsLatest = false;
      } else if(args[i].equals("-operatorsLatest")) {
        operatorsLatest = true;
      } else if(args[i].equals("-propositionsSmallest")) {
        propositionsSmallest = true;
      } else if(args[i].equals("-sortGoals")) {
        sortGoals = true;
        propositionsSmallest = false;
      else if(args[i].equals("-noHeuristics")) {
        sortGoals = false;
        propositionsSmallest = false;
        noopsFirst = false;
        operatorsLatest = false;
      } else if(args[i].equals("-draw")) {
        if(++i < args.length && !args[i].startsWith("-")) {
          graphDrawFile = args[i];
        } else {
          logger.warning("-draw argument requires a valid filename");
          argsOk = false;
        }
      }
    }
   
    if(domainFilename == null || problemFilename == null)
      Graphplan.wrongParametersMessage();
   
    File opFile = new File(domainFilename);
    File probFile = new File(problemFilename);
    if(!opFile.exists()) {
      logger.warning("Domain file \'"+domainFilename+"\' does not exist");
      argsOk = false;
    }
    if(!probFile.exists()){
      logger.warning("Problem file \'"+problemFilename+"\' does not exist");
      argsOk = false;
    }
   
    if(!pddl) {
      try {
        operators = new FileInputStream(opFile);
        problem = new FileInputStream(probFile);
      } catch (FileNotFoundException e) {
        logger.warning(e.toString());
        argsOk = false;
      }
      argsOk = (operators != null) && (problem != null);
    }
   
    if(argsOk) {
      long t1 = System.currentTimeMillis();
      DomainDescription domain = null;
      try {
        if(pddl){
          logger.finest("JavaGP - PDDL\n");
          logger.finest("+ DOMAIN: " + domainFilename);
          logger.finest("+ PROBLEM: " + problemFilename);
          PDDLPlannerAdapter parserPDDL = new PDDLPlannerAdapter(domainFilename, problemFilename);
          domain = parserPDDL.getDomainDescriptionFromPddlObject();
        } else {
          logger.finest("JavaGP - STRIPS\n");
          logger.finest("+ DOMAIN: " + domainFilename);
          logger.finest("+ PROBLEM: " + problemFilename);
          PlannerParser parser = new PlannerParser();
          domain = parser.parseProblem(operators, problem);
        }
      } catch (ParserException e) {
        e.printStackTrace();
        System.exit(1);
      } catch (ParseException e) {
        e.printStackTrace();
        System.exit(1);
      }
     
      PlanResult result = null;
     
      logger.fine("Selected Heuristics: ");
      if(Graphplan.noopsFirst)
        logger.fine("\t+ Heuristic for actions: Select Noops first");
     
      if(Graphplan.operatorsLatest)
        logger.fine("\t+ Heuristic for actions: Select actions that appears latest in the Planning Graph.");
     
      if(Graphplan.propositionsSmallest)
        logger.fine("\t+ Heuristic for subgoals: Select firstly propositions that leads to the smallest set of resolvers.");
     
      if(Graphplan.sortGoals)
        logger.fine("\t+ Heuristic for subgoals: Sort goals by proposition that appears earliest in the Planning Graph.");
     
     
      try {
        Runtime runtime = Runtime.getRuntime();
        NumberFormat fm = DecimalFormat.getInstance();
        fm.setMaximumFractionDigits(2);
       
        logger.info("Running planner, maximum memory: "+fm.format(runtime.maxMemory()/Math.pow(1024, 2))+"MB");
        if(timeout > 0) {
          result = graphplan.plan(domain, timeout);
        } else {
          result = graphplan.plan(domain);
        }
        long t2 = System.currentTimeMillis();
        long totalTime = (t2-t1);
        logger.info("Planning took "+(totalTime)+"ms ( " + (totalTime/1000)+"s )");
        logger.info("Total memory used: "+fm.format(runtime.totalMemory()/Math.pow(1024, 2))+"MB");
        if(result.isTrue()) {
          logger.info("Plan found:\n"+result.toString());
          logger.info("Plan length: " + result.getPlanLength());
          if(graphDrawFile != null) {
            logger.info("Drawing planning graph to "+graphDrawFile);
            DotGraphDrawVisitor drawVisitor = new DotGraphDrawVisitor();
            if(graphplan.planningGraph.accept(drawVisitor)) {
              PrintWriter writer = new PrintWriter(new File(graphDrawFile));
              writer.println(drawVisitor.toString());
              writer.flush();
              writer.close();
            }
          }
        } else {
          logger.warning("No plan found");
        }
      } catch (PlanningGraphException e) {
        e.printStackTrace();
        System.exit(1);
      } catch (OperatorFactoryException e) {
        e.printStackTrace();
        System.exit(1);
      } catch (TimeoutException e) {
        e.printStackTrace();
        System.exit(1);
      } catch (FileNotFoundException e) {
        e.printStackTrace();
        System.exit(1);
      } catch (OutOfMemoryError e) {
        e.printStackTrace();
        Runtime runtime = Runtime.getRuntime();
        logger.severe("Memory use exceeded maximum allocated for VM");
        long t2 = System.currentTimeMillis();
        long totalTime = (t2-t1);
        logger.info("Planning took "+(totalTime)+"ms ( " + (totalTime/1000)+"s ) before error");
        logger.severe("Current maximum memory: "+runtime.maxMemory()/1024+"kb");
      }
     
    } else {
      Graphplan.wrongParametersMessage();
    }
  }
 
  private static void wrongParametersMessage(){
    logger.warning("Wrong parameters");
    logger.info("Usage: \'java -jar JavaGP \'" +
        "\n\t>>> STRIPS Language: " +
        "\n\t\t" + "java -jar javagp.jar -nopddl -d examples/strips/ma-prodcell/domain.txt -p examples/strips/ma-prodcell/problem.txt" +
        "\n\n\t>>> PDDL Language: " +
        "\n\t\t" + "java -jar javagp.jar -d examples/pddl/blocksworld/blocksworld.pddl -p examples/pddl/blocksworld/pb1.pddl" +
        "\n\n\t>>> Planner arguments: " +
        "\n\t-maxlevels <NUMBER>, " + "\tMax Graph levels."+
        "\n\t-timeout <NUMBER>, " + "\tPlanning timeout." +
        "\n\n\t-noHeuristics, " + "\t\tNo Heuristics." +
        "\n\n\t[Heuristics for actions]" +
        "\n\t-operatorsLatest, " + "\tSelect actions that appears latest in the Planning Graph." +
        "\n\tor" +
        "\n\t-noopsFirst, " + "\t\tSelect Noops first." +
        "\n\n\t[Heuristic for propositions]" +
        "\n\t-propositionsSmallest,  Select firstly propositions that leads to the smallest set of resolvers." +
        "\n\tor" +
        "\n\t-sortGoals,    Sort goals by proposition that appears earliest in the Planning Graph." +
        "\n\n\t[JavaGP Default Heuristics]" +
        "\n\t-operatorsLatest"+
        "\n\t-propositionsSmallest"
        + "\n\n -v write verbose output"
        );
    System.exit(1);
  }
 
  public static void setupLogger() {
    try {
      if (new File(LOGGER_FILE).exists()) {
        LogManager.getLogManager().readConfiguration(new FileInputStream(new File(LOGGER_FILE)));
      } else {
        LogManager.getLogManager().readConfiguration(Graphplan.class.getResourceAsStream("/" + LOGGER_FILE));
      }
    } catch (Exception e) {
      System.err.println("Error setting up logger:" + e);
    }
  }
 
  /**
   * Empty constructor for testing.
   */
  public Graphplan() {
  }
 
  /**
   * Sets the maximum number of levels to be searched for in creating the graph.
   * @param maxLevels
   */
  public void setMaxLevels(int maxLevels) {
    this.maxLevels = maxLevels;
  }
 
  /**
   *
   * @param domainDescription
   * @return
   * @throws PlanningGraphException
   * @throws OperatorFactoryException
   */
  public PlanResult plan(DomainDescription domainDescription) throws PlanningGraphException, OperatorFactoryException {
    PropositionLevel initialLevel = new PropositionLevel();
    initialLevel.addPropositions(domainDescription.getInitialState());
    this.solutionExtraction = new SolutionExtractionVisitor(domainDescription.getGoalState());
   
    /*Closed World Assumption - Simple Implementation by goals*/
//    for(Proposition g: domainDescription.getGoalState()){
//      if(!initialLevel.hasProposition(g)){
//        //Add negative for proposition g
//        PropositionImpl p = new PropositionImpl(g.negated(), g.getFunctor());
//        p.setTerms(g.getTerms());
//        initialLevel.addProposition(p);
//      }
//    }

    logger.fine("OPTIMIZATION: JavaGP using Static Mutexes Table");
    logger.fine("OPTIMIZATION: JavaGP using Memoization");
   
    if(this.pddl) {
      logger.fine("OPTIMIZATION: JavaGP using Types");
      //If domain has negative preconditions, the planner will use the closed world assumption
      if(domainDescription.isNegativePreconditions()) {
        logger.fine("OPTIMIZATION: JavaGP using Closed World Assumption (Lazily)");
        this.planningGraph = new PlanningGraphClosedWorldAssumption(initialLevel, domainDescription.getTypes(), domainDescription.getParameterTypes(), new StaticMutexesTable(new ArrayList<Operator>(domainDescription.getOperators())));
      } else this.planningGraph = new PlanningGraph(initialLevel, domainDescription.getTypes(), domainDescription.getParameterTypes(), new StaticMutexesTable(new ArrayList<Operator>(domainDescription.getOperators())));
    } else this.planningGraph = new PlanningGraph(initialLevel, new StaticMutexesTable(new ArrayList<Operator>(domainDescription.getOperators())));
   
//    System.out.println();
   
    OperatorFactory.getInstance().resetOperatorTemplates();
   
    for(Operator operator:domainDescription.getOperators()) {
      OperatorFactory.getInstance().addOperatorTemplate(operator);
    }
   
    boolean planFound = false;
   
    while(!planFound && (this.planningGraph.size() <= this.maxLevels)) {
      try {
        logger.info("Expanding graph");
        this.planningGraph.expandGraph();
      } catch (PlanningGraphException e) {
        //If we have a problem with the planning graph
        //Issue the error and quit
        System.err.println(e);
        return new PlanResult(false);
      }
      if(this.planningGraph.goalsPossible(domainDescription.getGoalState(), this.planningGraph.size()-1)) {
        //Extract solution
        logger.info("Extracting solution");
        planFound = this.planningGraph.accept(this.solutionExtraction);
        if(planFound) {
          logger.info("Plan found with "+((int)this.planningGraph.size()/2)+" steps");
        } else {
          logger.info("Plan not found with "+((int)this.planningGraph.size()/2)+" steps");
          if(!planPossible()) {
            throw new PlanningGraphException("Graph has levelled off, plan is not possible.",this.planningGraph.levelOffIndex());
          }
        }
      } else {
        logger.info("Goals not possible with "+((int)this.planningGraph.size()/2)+" steps");
        //If the goals are not possible, and the graph has levelled off,
        //then this problem has no possible plan
        if(this.planningGraph.levelledOff()) {
          throw new PlanningGraphException("Goals are not possible and graph has levelled off, plan is not possible.",this.planningGraph.levelOffIndex());
        }
      }
    }
   
    return this.solutionExtraction.getPlanResult();
  }
 
  /**
   * Executes the Graphplan algorithm with a specified timeout.
   * @param domainDescription
   * @param timeout
   * @return
   * @throws PlanningGraphException
   * @throws OperatorFactoryException
   * @throws TimeoutException
   */
  public PlanResult plan(DomainDescription domainDescription, long timeout) throws PlanningGraphException, OperatorFactoryException, TimeoutException {
    PropositionLevel initialLevel = new PropositionLevel();
    initialLevel.addPropositions(domainDescription.getInitialState());
    this.solutionExtraction = new TimeoutSolutionExtractionVisitor(domainDescription.getGoalState());
    ((TimeoutSolutionExtractionVisitor)solutionExtraction).setTimeout(timeout);
   
    this.planningGraph = new PlanningGraph(initialLevel, new StaticMutexesTable(new ArrayList<Operator>(domainDescription.getOperators())));
    OperatorFactory.getInstance().resetOperatorTemplates();
   
    for(Operator operator:domainDescription.getOperators()) {
      OperatorFactory.getInstance().addOperatorTemplate(operator);
    }
   
    boolean planFound = false;
   
    while(!planFound && (this.planningGraph.size() <= this.maxLevels)) {
      try {
        logger.info("Expanding graph");
        this.planningGraph.expandGraph();
      } catch (PlanningGraphException e) {
        //If we have a problem with the planning graph
        //Issue the error and quit
        System.err.println(e);
        return new PlanResult(false);
      }
      if(planningGraph.goalsPossible(domainDescription.getGoalState(), this.planningGraph.size()-1)) {
        //Extract solution
        logger.info("Extracting solution");
        planFound = this.planningGraph.accept(this.solutionExtraction);
        if(planFound) {
          logger.info("Plan found with "+((int)this.planningGraph.size()/2)+" steps");
        } else {
          if(((TimeoutSolutionExtractionVisitor)solutionExtraction).timedOut()) {
            logger.info("Planner timed out after "+timeout+" milliseconds");
            throw new TimeoutException("No plan possible in "+timeout+" milliseconds");
          }
          logger.info("Plan not found with "+((int)this.planningGraph.size()/2)+" steps");
          if(!planPossible()) {
            throw new PlanningGraphException("Graph has levelled off, plan is not possible.",this.planningGraph.levelOffIndex());
          }
        }
      } else {
        logger.info("Goals not possible with "+((int)this.planningGraph.size()/2)+" steps");
        //If the goals are not possible, and the graph has levelled off,
        //then this problem has no possible plan
        if(this.planningGraph.levelledOff()) {
          throw new PlanningGraphException("Goals are not possible and graph has levelled off, plan is not possible.",this.planningGraph.levelOffIndex());
        }
      }
    }
   
    return solutionExtraction.getPlanResult();
  }
 
  /**
   * Returns whether or not a plan is possible according to both the
   * memoization table criterion and the graph level size criterion.
   * @return
   */
  public boolean planPossible() {
    if(!this.planningGraph.levelledOff()) {
      return true;
    } else {
      return this.solutionExtraction.levelledOff(this.planningGraph.levelOffIndex());
    }
  }
 
  /**
   * Returns a list with the minimum preconditions necessary for the supplied plan to
   * be successful.
   *
   * TODO General cleanup and tuning of this method.
   *
   * @param plan       A list of operator invocations representing a plan
   * @param description  The domain description in which the supplied plan is executed
   * @return        The minimum set of propositions that must be true before execution
   */
  public List<Proposition> getPlanPreconditions(List<String> plan, DomainDescription description) {
    // XXX This variable has been placed here to give us a slight speed up and to ease debug
    // XXX But this might not be a good idea if we try and change the singleton at runtime
    OperatorFactory operatorFactory = OperatorFactory.getInstance();
   
    for(Iterator<Operator> iter = description.getOperators().iterator(); iter.hasNext(); ) {
      try {
        //OperatorFactory.getInstance().addOperatorTemplate(iter.next());
        operatorFactory.addOperatorTemplate(iter.next());
      } catch (OperatorFactoryException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
   
    Operator concreteOperators[] = new Operator[plan.size()];
   
    //First, get operator instances corresponing to the operator invocations
    //in the parameter
    for (int i = 0; i < concreteOperators.length; i++) {
      try {
        //concreteOperators[i] = OperatorFactory.getInstance().getOperator(plan.get(i));
        concreteOperators[i] = operatorFactory.getOperator(plan.get(i));
      } catch (OperatorFactoryException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
   
    //Then Create the planning graph with an empty initial state
    PlanningGraph graph = new PlanningGraph(new PropositionLevel());
    //And populate it with action levels and proposition levels containing the obvious
    //preconditions from the subsequent action level
    for (int i = 0; i < concreteOperators.length; i++) {
      //We always assume our graph has a proposition level as its last level
      PropositionLevel propositionLevel = (PropositionLevel) graph.getGraphLevel(graph.size()-1);
      //into which we add the preconditions for this action
      propositionLevel.addPropositions(concreteOperators[i].getPreconds());
      //We then create an action level to contain this action
      ActionLevel actionLevel = new ActionLevel();
      actionLevel.addAction(concreteOperators[i]);
      graph.addGraphLevel(actionLevel);
      //And create another proposition level for its effects
      PropositionLevel propositionLevel2 = new PropositionLevel();
      propositionLevel2.addPropositions(concreteOperators[i].getEffects());
     
      graph.addGraphLevel(propositionLevel2);
    }
   
    //Then walk the graph backwards propagating the preconditions that were not generated
    //by the previous action level using noops
    for(int i = graph.size()-1; i > 0; i=i-2) {
      //We need to get the proposition levels surrounding our action level
      //As in: precond <- action <- effect
      PropositionLevel effectLevel = (PropositionLevel) graph.getGraphLevel(i);
      PropositionLevel precondLevel = (PropositionLevel) graph.getGraphLevel(i-2);
      ActionLevel actionLevel = (ActionLevel) graph.getGraphLevel(i-1);
      //Then see if each proposition in the effectLevel is connected by some action
      //to the precondLevel
      for (Iterator<Proposition> iter = effectLevel.getPropositions(); iter.hasNext();) {
        Proposition proposition = iter.next();
        //If no action is connected to the effect level
        if(actionLevel.getGeneratingActions(proposition).size() == 0) {
          //We need to propagate this action to the previous level
          actionLevel.addNoop(proposition);
          precondLevel.addProposition(proposition);
        }
      }
    }
   
    //this last bit looks rather nasty, I should review this when time allows
    PropositionLevel level = (PropositionLevel) graph.getGraphLevel(0);
    List<Proposition> planPreconditions = new ArrayList<Proposition>(level.size());
   
    for (Iterator<Proposition> iter = level.getPropositions(); iter.hasNext();) {
      planPreconditions.add(iter.next());
    }

    return planPreconditions;
  }
 
  /**
   * @return the solutionExtraction
   */
  public SolutionExtractionVisitor getSolutionExtraction() {
    return solutionExtraction;
  }

  /**
   * @param solutionExtraction the solutionExtraction to set
   */
  public void setSolutionExtraction(SolutionExtractionVisitor solutionExtraction) {
    this.solutionExtraction = solutionExtraction;
  }

  public void setPddl(boolean pddl) {
    this.pddl = pddl;
  }
}
TOP

Related Classes of graphplan.Graphplan

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.