All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.tweety.action.description.reasoner.CTransitionSystemCalculator Maven / Gradle / Ivy

There is a newer version: 1.6
Show newest version
/*
 *  This file is part of "TweetyProject", a collection of Java libraries for
 *  logical aspects of artificial intelligence and knowledge representation.
 *
 *  TweetyProject is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version 3 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 program. If not, see .
 *
 *  Copyright 2016 The TweetyProject Team 
 */
package net.sf.tweety.action.description.reasoner;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.sf.tweety.action.description.syntax.CActionDescription;
import net.sf.tweety.action.description.syntax.DynamicLaw;
import net.sf.tweety.action.description.syntax.StaticLaw;
import net.sf.tweety.action.signature.ActionSignature;
import net.sf.tweety.action.signature.FolAction;
import net.sf.tweety.action.signature.FolActionName;
import net.sf.tweety.action.signature.FolFluentName;
import net.sf.tweety.action.transitionsystem.State;
import net.sf.tweety.action.transitionsystem.Transition;
import net.sf.tweety.action.transitionsystem.TransitionSystem;
import net.sf.tweety.commons.ParserException;
import net.sf.tweety.logics.fol.parser.FolParser;
import net.sf.tweety.logics.fol.syntax.Conjunction;
import net.sf.tweety.logics.fol.syntax.Contradiction;
import net.sf.tweety.logics.fol.syntax.FolAtom;
import net.sf.tweety.logics.fol.syntax.FolFormula;
import net.sf.tweety.logics.fol.syntax.Negation;
import net.sf.tweety.logics.commons.syntax.RelationalFormula;
import net.sf.tweety.logics.fol.syntax.Tautology;
import net.sf.tweety.lp.asp.reasoner.ASPSolver;

/**
 * This class calculates the transition system as it is described by an action
 * description in the action language C using extended logic programming with an
 * approach from: Representing Transition Systems by Logic Programs. by Vladimir
 * Lifschitz and Hudson Turner, LPNMR '99: Proceedings of the 5th International
 * Conference on Logic Programming and Nonmonotonic Reasoning. Pages 92-106,
 * 1999.
 * 
 * @author Sebastian Homann
 * @author Tim Janus (modifictions for asp library)
 */
public class CTransitionSystemCalculator
{
  private ASPSolver aspsolver;
  
  /**
   * Creates a new transition system calculator with the given interface to an
   * answer set solver.
   * 
   * @param aspsolver some ASP solver
   */
  public CTransitionSystemCalculator( ASPSolver aspsolver )
  {
    this.aspsolver = aspsolver;
  }
  
  /**
   * Calculates a transition system as described by the given action description
   * using all symbols in the given action signature.
   * 
   * @param actionDescription an action description.
   * @param signature an action signature.
   * @return a transition system.
   * @throws IOException if IO fails
   * @throws IllegalArgumentException is thrown, when the given action
   *           description is not definite, as this method only works for
   *           definite action descriptions.
   */
  public TransitionSystem calculateTransitionSystem(
    CActionDescription actionDescription, ActionSignature signature )
    throws IOException
  {
    if ( !actionDescription.isDefinite() )
      throw new IllegalArgumentException(
        "Cannot calculate transition system of non-definite action description." );
    
    Set< State > states = calculateStates( actionDescription, signature );
    if ( states == null )
      return new TransitionSystem( signature );
    TransitionSystem transitionSystem =
      new TransitionSystem( states, signature );
    
    String rules = getLpT( actionDescription, signature, 1 );
  
    aspsolver.getModels(rules);
    String[] claspResult =  aspsolver.getOutput().split("\\R+");
    /*
    String[] claspResult = aspsolver.calculateAnswerSets( rules );
    */
    if ( claspResult == null )
      return transitionSystem;
    Set< Map< Integer, Set< FolAtom >>> answerSets =
      parseLpT( claspResult, signature );
    
    for ( Map< Integer, Set< FolAtom >> answerSet : answerSets ) {
      Set< FolAtom > sourceStateFluents = new HashSet< FolAtom >();
      Set< FolAtom > targetStateFluents = new HashSet< FolAtom >();
      Set< FolAtom > actionNames = new HashSet< FolAtom >();
      for ( FolAtom a : answerSet.get( 0 ) ) {
        if ( a.getPredicate() instanceof FolFluentName )
          sourceStateFluents.add( a );
        else if ( a.getPredicate() instanceof FolActionName )
          actionNames.add( a );
      }
      for ( FolAtom a : answerSet.get( 1 ) )
        targetStateFluents.add( a );
      
      State sourceState = transitionSystem.getState( sourceStateFluents );
      State targetState = transitionSystem.getState( targetStateFluents );
      FolAction action = new FolAction( actionNames );
      transitionSystem.addTransition( new Transition( sourceState, action,
        targetState ) );
    }
    
    return transitionSystem;
  }
  
  /**
   * calculates the set of all states of the transition system described by an
   * action description.
   * 
   * @param actionDescription an action description.
   * @param signature an action signature.
   * @return the set of all states of the transition system described by an
   *         action description.
   * @throws IOException if IO fails
   * @throws IllegalArgumentException is thrown, when the given action
   *           description is not definite.
   */
  public Set< State > calculateStates( CActionDescription actionDescription,
    ActionSignature signature )
    throws IOException
  {
    if ( !actionDescription.isDefinite() )
      throw new IllegalArgumentException(
        "Cannot calculate transition system of non-definite action description." );
    Set< State > result = new HashSet< State >();
    String rules = getLpT( actionDescription, signature, 0 );
    
    aspsolver.getModels(rules);
    String[] claspResult =  aspsolver.getOutput().split("\\R+");
    /*
    String[] claspResult = aspsolver.calculateAnswerSets( rules );
    */
    if ( claspResult == null )
      return null;
    Set< Map< Integer, Set< FolAtom >>> states = parseLpT( claspResult, signature );
    for ( Map< Integer, Set< FolAtom >> state : states ) {
      State s = new State( state.get( 0 ) );
      result.add( s );
    }
    
    return result;
  }
  
  /**
   * Calculates an extended logic programm lp_T(D) for a given action
   * description D and a parameter T, which corresponds to the length of
   * histories in the transition system described by D. See
   * "Representing Transition Systems by Logic Programs." for more information.
   * 
   * @param d an action description.
   * @param signature an action signature.
   * @param T length of histories of the transition system of D, that correspond
   *          to answer sets of lp_T(D).
   * @return an extended logic program.
   */
  public String getLpT( CActionDescription d, ActionSignature signature, int T )
  {
    if ( T < 0 )
      throw new IllegalArgumentException( "T has to be >= 0." );
    if ( !d.isDefinite() )
      throw new IllegalArgumentException(
        "Cannot calculate transition system of non-definite action description." );
    String result = "";
    Set< FolAtom > groundedFluentAtoms = signature.getAllGroundedFluentAtoms();
    
    for ( StaticLaw r : d.getStaticLaws() ) {
      for ( int t = 0; t <= T; t++ ) {
        result += this.getLiteralString( r.getHeadFormula(), t, false );
        if ( !( r.getIfFormula() instanceof Tautology ) ) {
          result += " :- ";
          result += getRuleBodyString( r.getIfFormula(), t, true );
        }
        result += ".\n";
      }
    }
    // -B :- not B. B :- not -B. rules for fluentnames with t=0.
    result += getDefaultNegationRules( groundedFluentAtoms, 0 );
    
    if ( T > 0 ) {
      for ( DynamicLaw r : d.getDynamicLaws() ) {
        for ( int t = 0; t < T; t++ ) {
          result += this.getLiteralString( r.getHeadFormula(), t + 1, false );
          result += " :- ";
          if ( !( r.getIfFormula() instanceof Tautology ) ) {
            result += getRuleBodyString( r.getIfFormula(), t + 1, true );
            result += ",";
          }
          result += getRuleBodyString( r.getAfterFormula(), t, false );
          result += ".\n";
        }
      }
      // -B :- not B. B :- not -B. rules for all actionnames.
      Set< FolAtom > groundedActionNames =
        signature.getAllGroundedActionNameAtoms();
      for ( int t = 0; t < T; t++ ) {
        result += getDefaultNegationRules( groundedActionNames, t );
      }
      // :- not B, not -B. constraints for all fluent names with t>0. removes incomplete answer sets.
      for ( int t = 1; t <= T; t++ ) {
        result += getCompletenessEnforcementRules( groundedFluentAtoms, t );
      }
    }
    
    return result;
  }
  
  /**
   * Returns rules of an extended logic program for the given set of atoms and a
   * parameter t. For each atom a, the result contains the rules
   *   -a(t) :- not  a(t).
   *    a(t) :- not -a(t).
   *
   * @param atoms set of atoms.
   * @param t parameter t.
   * @return rules.
   */
  private String getDefaultNegationRules( Set< FolAtom > atoms, int t )
  {
    String rules = "";
    for ( FolAtom a : atoms ) {
      String atomName = getAtomString( a, t );
      rules += "-" + atomName + " :- not " + atomName + ".\n";
      rules += atomName + " :- not -" + atomName + ".\n";
    }
    return rules;
  }
  
  /**
   * Returns rules, that enforce the existence of each atom in the given set in
   * all answer sets of an extended logic program.
   * 
   * @param atoms a set of atoms.
   * @param t parameter to be added to each atom.
   * @return rules of an extended logic program.
   */
  private String getCompletenessEnforcementRules( Set< FolAtom > atoms, int t )
  {
    String rules = "";
    for ( FolAtom a : atoms ) {
      String atomName = getAtomString( a, t );
      rules += ":- not " + atomName + ", not -" + atomName + ".\n";
    }
    return rules;
  }
  
  /**
   * Removes illegal characters from atom names, which are not parsable by an
   * lparse compatible answer set solver.
   * 
   * @param s an atom name.
   * @return the same atom name with removed illegal characters.
   */
  private String removeIllegalChars( String s )
  {
    return s.replace( "(", "xxx1xxx" ).replace( ")", "xxx2xxx" ).replace( ",", "xxx3xxx" );
  }
  
  /**
   * Regains all illegal characters from answer sets. This regains the original
   * names for each literal in an answer set.
   * 
   * @param s an atom name in lparse-compatible format.
   * @return the original atom name.
   */
  private String regainIllegalChars( String s )
  {
    return s.replace( "xxx1xxx", "(" ).replace( "xxx2xxx", ")" ).replace( "xxx3xxx", "," );
  }
  
  /**
   * Parses the resulting answer sets of an lp_T(D) program as a set. Each
   * answer set is parsed as a map from integers to sets of atoms, where the
   * integer represents the timestamp for each atom in the corresponding set.
   * 
   * @param lines the output of the answer set solver.
   * @param signature the action signature of the original description.
   * @return a set of maps each mapping timestamps to sets of atoms.
   */
  private Set< Map< Integer, Set< FolAtom >>> parseLpT( String[] lines,
    ActionSignature signature )
  {
    Set< Map< Integer, Set< FolAtom >>> result =
      new HashSet< Map< Integer, Set< FolAtom >>>();
    
    for ( String line : lines ) {
      result.add( parseLpTSingleLine( line, signature ) );
    }
    return result;
  }
  
  /**
   * Utility function parsing a single answer set to a map from timestamp to the
   * set of atoms with that particular timestamp.
   * 
   * @param s a single answer set from the output of a solver.
   * @param signature the action signature of the original action description.
   * @return a map from timestamps to sets of atoms.
   * @throws ParserException if parsing fails
   */
  private Map< Integer, Set< FolAtom >> parseLpTSingleLine( String s,
    ActionSignature signature )
    throws ParserException
  {
    Map< Integer, Set< FolAtom >> map = new HashMap< Integer, Set< FolAtom >>();
    
    String[] tokens = s.split( " " );
    for ( String token : tokens ) {
      token = token.trim();
      int i =
        Integer.parseInt( token.substring( token.indexOf( "(" ) + 1, token
          .indexOf( ")" ) ) );
      if ( map.get( i ) == null )
        map.put( i, new HashSet< FolAtom >() );
      if ( !token.startsWith( "-" ) ) {
        FolParser p = new FolParser();
        p.setSignature( signature );
        FolAtom a = null;
        try {
          a =
            (FolAtom) p.parseFormula( regainIllegalChars( token.substring( 0,
              token.indexOf( "(" ) ) ) );
        }
        catch ( IOException e ) {
          // Impossible, no io involved
          e.printStackTrace();
        }
        map.get( i ).add( a );
      }
    }
    return map;
  }
  
  /**
   * Calculates the representation of an inner formula of a causal rule
   * according to the translation in
   * "Representing Transition Systems by Logic Programs.". Since the "if"-part
   * of a rule has to be represented by default negation and the "after"-part by
   * a direct translation, this function allows both kinds of translations which
   * may be selected by the boolean parameter "negated".
   * 
   * @param f the inner formula of a causal rule.
   * @param t the timestamp for this translation.
   * @param negated true iff the formula should be represented using default
   *          negation.
   * @return a part of a logic rule representing the given formula.
   */
  private String getRuleBodyString( FolFormula f, int t, boolean negated )
  {
    String result = "";
    if ( f instanceof Conjunction ) {
      Conjunction c = (Conjunction) f;
      for ( RelationalFormula literal : c ) {
        if ( !result.equals( "" ) )
          result += ",";
        result += getLiteralString( (FolFormula) literal, t, negated );
      }
    }
    else {
      result = getLiteralString( f, t, negated );
    }
    return result;
  }
  
  /**
   * Utility function representing a single literal either in a direct manner or
   * using default negation.
   * 
   * @param f the literal to be represented.
   * @param t a timestamp.
   * @param negated true, iff the literal should be represented using default
   *          negation.
   * @return a representation of the given literal.
   */
  private String getLiteralString( FolFormula f, int t, boolean negated )
  {
    String result = negated ? "not " : "";
    if ( f instanceof Negation ) {
      result += negated ? "" : "-";
      result += getAtomString( ( (Negation) f ).getFormula(), t );
    }
    else if ( f instanceof FolAtom ) {
      result += negated ? "-" : "";
      result += getAtomString( f, t );
    }
    else if ( f instanceof Tautology ) {
      result = "";
    }
    else if ( f instanceof Contradiction ) {
      result = "";
    }
    else {
      throw new IllegalArgumentException( "Not a valid Literal." );
    }
    
    return result;
  }
  
  /**
   * Utility function representing a given atom, either an actionname or a
   * fluentname, to be used in a rule in an extended logic program.
   * 
   * @param f an atom.
   * @param t a timestamp.
   * @return the string representation of the atom.
   */
  private String getAtomString( RelationalFormula f, int t )
  {
    if ( !( f instanceof FolAtom ) )
      throw new IllegalArgumentException(
        "Cannot calculate transition system. Causal rule is not definite." );
    return removeIllegalChars( f.toString() ) + "(" + Integer.toString( t ) +
      ")";
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy