package de.tu_dresden.inf.ggp06_2.resolver;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.pow;
import java.util.ArrayList;
import java.util.List;
import de.tu_dresden.inf.ggp06_2.resolver.astvisitors.AbstractVisitor;
import de.tu_dresden.inf.ggp06_2.resolver.fuzzy.FuzzyResolution;
import de.tu_dresden.inf.ggp06_2.resolver.fuzzy.FuzzySubstitution;
import de.tu_dresden.inf.ggp06_2.resolver.scope.GameStateScope;
import de.tu_dresden.inf.ggp06_2.resolver.scope.RuleScope;
import de.tu_dresden.inf.ggp06_2.simulator.flags.TimerFlag;
/**
* This is one of the very basic classes for theory construction. Three direct
* children are: Connective, Implication and Term. Use Eclipse Hierarchy window
* to see the complete picture. :)
*
* @author Nick (initial author of JavaProver )
* @author Ingo Keller - General Game Playing course student at TUD
* @author Arsen Kostenko - General Game Playing course student at TUD
* @author Novak Novakovic - General Game Playing course student at TUD
*/
public abstract class Expression {
protected String toString = null;
/**
* This method produces MGU (most general unifier) - a substitution that
* makes current expression equal to given one, w.r.t. current state of
* resolution, which is passed as parameter in another substitution.
*
* @param target Given expression to compare to
* @param in Current state of resolution
* @return Returns substitution that makes current expression
* equal to a given one and preserves as much variables
* as possible. In the case of a fail it returns null.
*/
public abstract Substitution mgu(Expression target, Substitution in);
/**
* This method applies given substitution to current expression.
*
* @param sigma Given substitution.
* @return Returns new expression produced via application
* of given substitution to current expression.
*/
public abstract Expression apply(Substitution sigma);
/**
* This method checks wether an expression is ground or not.
* @return Returns true if expression is ground; otherwise false.
*/
public abstract boolean isGround();
/**
* This method returns the key atom of an expression. This means that this
* atom is used for the mapping within theory class.
* @return Atom which is the key for this expression.
*/
public abstract Atom getKeyAtom();
/**
* This method returns the number of operands of a concrete Expression.
*
* Here are the expected outcomes:
* <ul>
* <li>0 - Term derived classes</li>
* <li>1 - NotOperand</li>
* <li>2 - DistinctOperand</li>
* <li>n - Connective derived classes; essentially operands.size()</li>
* <li>n - Implication returns consequence.getOperandCount()</li>
* </ul>
* @return
*/
public abstract int getOperandCount();
/**
* This method check wether a variable is present or not within the current
* expression.
*
* @param var The variable to check for occurrences
* @return True if the variable appears in the current expression; false
* otherwise.
*/
public abstract boolean isPresent(Variable var);
/**
* Get first operand of current expression, i.e. word
* "and" for conjunction operator.
* @return Returns term that definies current expression.
*/
public abstract Term firstOperand();
/**
* Get second operand of current expression, i.e.
* word "cell" for expression like
* <i>"( true (cell 1) )"</i>
* @return Return term that defines first operand among
* premises of current expression or <b>null</b>
* if none.
*/
public abstract Term secondOperand();
/**
* This method has to be implemented by the final subclasses.
* @param visitor
*/
public abstract void processVisitor(AbstractVisitor visitor);
/**
* There are two similar methods: chain and chainOne. Each of them is using
* the same technique for progression over the tree. This technique may be
* expressed as follows:
* <ol>
* <li>
* Check for interrupted flag within Theory class.
* </li>
* <li>
* Apply the sigma to the current expression to get a ground version.
* </li>
* <li>
* Check whether ground expression is equal to currently
* chained/evaluated expression and
* <ol>
* <li>
* Delegate chaining/evaluation to ground expression, if the are
* NOT equal.
* </li>
* <li>Continue otherwice</li>
* </ol>
* </li>
* <li>
* Continue with corresponding "body" method (whereas "body" methods
* differ from instance to instance)
* </li>
* </ol>
*
* @param sigma Current state of resolution
* @param scope Scope of GDL rules used for resolution
* @param flag Timer flag, which is checked before every step of resolution.
* @return Returns list of substitutions that were produced during resolution
* procedure.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
public List<Substitution> chain( Substitution sigma,
RuleScope scope,
TimerFlag flag )
throws InterruptedException{
if ( flag.interrupted() ) throw Const.interrupt;
Expression s = this.apply(sigma);
// if that is different to current expression
// chaining is passed to a ground version
return !s.equals(this) ? s.chain (sigma, scope, flag):
chainBody(sigma, scope, flag);
}
public static final double fuzzyZero = 0.123456789;
public static final double fuzzyOne = (1 - fuzzyZero);
public static final double threshold = 0.60;
public final static double tConorm(double a, double b){
return 1.0 - tNorm(1.0-a, 1.0-b);
}
public final static double tNorm(double a, double b) {
return (min( a, b ) > 0.5 ) ? max(t(a,b), threshold) : t( a, b );
}
public static double t(double a, double b) {
return 1.0 - s( 1-a, 1-b );
}
public static double s(double a, double b) {
double q = 7;
double a_q = pow( a, q );
double b_q = pow( b, q);
double s = pow(a_q+b_q, 1/q);
double div = Math.pow(2, 1/q);
return min(s, fuzzyOne + (s/div)*(1.0-fuzzyOne));
}
/**
* This method actually performes all the chaining machinery specific to each
* type of expressions. See each concrete implementation in particular.
* @see de.tu_dresden.inf.ggp06_2.resolver.AndOperator#chainBody(Substitution, RuleScope, TimerFlag) AndOperator.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.OrOperator#chainBody(Substitution, RuleScope, TimerFlag) OrOperator.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.NotOperator#chainBody(Substitution, RuleScope, TimerFlag) NotOperator.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.DistinctOperator#chainBody(Substitution, RuleScope, TimerFlag) DistinctOperator.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#chainBody(Substitution, RuleScope, TimerFlag) Predicate.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Implication#chainBody(Substitution, RuleScope, TimerFlag) Implication.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Atom#chainBody(Substitution, RuleScope, TimerFlag) Atom.chainBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Term#chainBody(Substitution, RuleScope, TimerFlag) Term.chainBody
* @param sigma Current state of resolution
* @param scope Scope of GDL rules used for resolution
* @param flag Timer flag, which is checked before every step of resolution.
* @return Returns list of substitutions that were produced during resolution
* procedure.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
protected abstract List<Substitution> chainBody( Substitution sigma,
RuleScope scope,
TimerFlag flag )
throws InterruptedException;
/**
* This method is another entering point for a chain resolution, but this time only
* one (first) solution is reported.
* @see de.tu_dresden.inf.ggp06_2.resolver.Expression#chain(Substitution, RuleScope, TimerFlag) chain
* @param sigma Current state of resulution
* @param scope Scope of GDL rules used for resulution
* @param flag Timer flag, which is checked before every step of resolution.
* @return Returns a substitution that was produced during resolution
* procedure.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
public Substitution chainOne(Substitution sigma, RuleScope scope, TimerFlag flag) throws InterruptedException{
if ( flag.interrupted() ) throw Const.interrupt;
Expression s = this.apply( sigma );
if ( !s.equals(this) )
return s.chainOne(sigma, scope, flag);
return chainOneBody(sigma, scope, flag);
}
/**
* This method hides all the machinery of <code>chainOne</code> method specific
* to concrete implementations of Expressions.
* @see de.tu_dresden.inf.ggp06_2.resolver.AndOperator#chainOneBody(Substitution, RuleScope, TimerFlag) AndOperator.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.OrOperator#chainOneBody(Substitution, RuleScope, TimerFlag) OrOperator.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.NotOperator#chainOneBody(Substitution, RuleScope, TimerFlag) NotOperator.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.DistinctOperator#chainOneBody(Substitution, RuleScope, TimerFlag) DistinctOperator.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#chainOneBody(Substitution, RuleScope, TimerFlag) Predicate.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Implication#chainOneBody(Substitution, RuleScope, TimerFlag) Implication.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Atom#chainOneBody(Substitution, RuleScope, TimerFlag) Atom.chainOneBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Term#chainOneBody(Substitution, RuleScope, TimerFlag) Term.chainOneBody
* @param sigma Current state of resolution
* @param scope Scope of GDL rules used for resolution
* @param flag Timer flag, which is checked before every step of resolution.
* @return Returns one (first) substitution that was produced during resolution
* procedure.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
protected abstract Substitution chainOneBody(
Substitution sigma, RuleScope scope, TimerFlag flag) throws InterruptedException;
/**
* This method performs fuzzy evaluation of an expression against
* some GDL rule-scope. In fact, the initial idea was to evaluate
* terminal and goal statements, so the scope is ment to be an
* instance of <code>GameStateScope</code>
* @param sigma Current state of resolution
* @param scope Scope of GDL rules used for resolution
* @param flag Timer flag, which is checked before every step of resolution.
* @return Returns list of substitutions that were produced during resolution
* procedure.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
public FuzzyResolution fuzzyEvaluate(
FuzzySubstitution sigma, GameStateScope scope,
List<Expression> guard, TimerFlag flag)
throws InterruptedException{
return this.apply( sigma ).fuzzyEvaluateBody(sigma, scope, guard, flag);
}
/**
* This method actually performes all the chaining machinery of <code>fuzzyEvaluate</code>
* specific to each type of expressions. See each concrete implementation in particular.
* @see de.tu_dresden.inf.ggp06_2.resolver.AndOperator#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) AndOperator.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.OrOperator#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) OrOperator.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.NotOperator#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) NotOperator.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.DistinctOperator#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) DistinctOperator.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) Predicate.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Implication#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) Implication.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Atom#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) Atom.evalBody
* @see de.tu_dresden.inf.ggp06_2.resolver.Term#fuzzyEvaluateBody(Substitution, RuleScope, TimerFlag) Term.evalBody
* @param sigma Current state of resolution
* @param sigmas Total list of possible resolutions at this stage.
* @param scope Scope of GDL rules used for resolution
* @param flag Timer flag, which is checked before every step of resolution.
* @return Returns list of substitutions that were produced during resolution
* procedure.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
protected abstract FuzzyResolution fuzzyEvaluateBody(
FuzzySubstitution sigma, GameStateScope scope,
List<Expression> guard, TimerFlag flag)
throws InterruptedException;
/**
* This method returns all variables used within this expression. It is used
* for example in the substitution class for restricting a substitution to a
* domain.
*
* @see Substitution#restrict(Expression)
*/
public abstract List<Variable> getVariables();
/**
* @return Returns a hash code of current expression.
* hash code is obtained via turning expression into
* string and then taking hash code of that string.
*/
@Override
public int hashCode() {
return toString().hashCode();
}
/**
* This method returns a substitution that is either empty if this expression
* does not have a variable or the substitution contains an association
* between a variable from the expression to a new variable for each
* variable within this expression.
* @return
*/
public Substitution uniquifier() {
if ( getVariables() == null )
return new Substitution();
Substitution uni = new Substitution();
List<Variable> culled = new ArrayList<Variable>();
List<Variable> vars = getVariables();
// iterate over list entries and add only once
for ( Variable aVar : vars )
if ( !culled.contains(aVar) ) {
culled.add( aVar );
uni.addAssociation( aVar, new Variable() );
}
return uni;
}
/* No need for this class
* public Expression toDNF(){
if (this instanceof Implication){
return new Implication(((Implication)this).consequence,
((Implication)this).getPremises().flattenExpresionList());
}
Expression nnf = this.moveNegationToAtoms();
nnf.reorderConnectives();
return nnf;
}*/
/* No need for this class
public Expression reorderConnectives(){
if (this instanceof OrOperator){
return new OrOperator(((Connective)this).getOperands().get( 0 ).reorderConnectives(),
((Connective)this).getOperands().get( 1 ).reorderConnectives());
}
if (this instanceof AndOperator){
Expression firstOperand = ((Connective)this).getOperands().get( 0 ).reorderConnectives();
Expression secondOperand = ((Connective)this).getOperands().get( 0 ).reorderConnectives();
if (firstOperand instanceof OrOperator){
Expression aux1 = new AndOperator(((Connective)firstOperand).getOperands().get( 0 ),
secondOperand);
Expression aux2 = new AndOperator(((Connective)firstOperand).getOperands().get( 1 ),
secondOperand);
return new OrOperator(aux1.reorderConnectives(), aux2.reorderConnectives());
}
if (secondOperand instanceof OrOperator){
Expression aux1 = new AndOperator(((Connective)secondOperand).getOperands().get( 0 ),
firstOperand);
Expression aux2 = new AndOperator(((Connective)firstOperand).getOperands().get( 1 ),
firstOperand);
return new OrOperator(aux1.reorderConnectives(), aux2.reorderConnectives());
}
return new AndOperator(firstOperand, secondOperand);
}
return new Predicate((Atom)(this.firstOperand()),
((Predicate)this).getOperands());
} */
public Expression canonize() {
return apply( deriveCanon() );
}
public Substitution deriveCanon() {
List<Variable> vars = this.getVariables();
Substitution canon = new Substitution();
int i = 0;
for (Variable var : vars)
canon.addAssociation( var, new Variable("?var" + i++) );
return canon;
}
}