package de.tu_dresden.inf.ggp06_2.resolver;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
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;
/**
*
* @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
*
*/
public class Predicate extends Connective {
/* Stores the logger for this class */
public static Logger logger = Logger.getLogger(Predicate.class);
protected final Atom operator;
/**
* A predicate with single argument, i.e. <i>"(cell 1)"</i>
*
* @param operator Operator, "cell" in this case
* @param operand Single operand. In example given above it would be "1"
*/
public Predicate(Atom operator, Expression operand){
this.operator = operator;
operands.add( operand );
}
/**
* A predicate with two arguments, i.e. <i>"(cell 1 2)"</i>
*
* @param operator Operator, "cell" in this case
* @param operand1 First operand. In example given above it would be "1"
* @param operand2 Second operand. In example given above it would be "1"
*/
public Predicate(Atom operator, Expression operand1, Expression operand2){
this.operator = operator;
operands.add( operand1 );
operands.add( operand2 );
}
/**
* A predicate with multiple arguments, i.e. <i>"(cell 1 2 b)"</i>
*
* @param operator Operator, "cell" in this case.
* @param operands List of expressions that correspond to all the operands of the
* predicate.
*/
public Predicate(Atom operator, ExpressionList operands) {
this.operator = operator;
this.operands.addAll( operands );
}
/**
* @return Returns the operator.
*/
public Atom getOperator() {
return operator;
}
/**
* Apply substitution to premises of current predicate,
* which produces list of new premises. These new premises
* are used to create new predicate, which is returned
* as result.
*
* @param sigma Substitution to apply
* @return Returns a new predicate, obtained
* from a current one, by application
* of substitution.
*/
@Override
public Expression apply(Substitution sigma) {
return new Predicate( operator, operands.apply(sigma) );
}
/**
* <code>Predicate</code> class is actually one of those that make use
* of GDL rules scopes by getting similar expressions from them.
* Trick about <code>Predicate</code> is that there might be implication
* consequences and single predicates (both static and dynamic) recieved
* after matching with existing data. So major trick is a kind of branching.
* Another point is that <code>chain</code> method is looking for all
* possible resolutions. Here is a step by step behavior:
* <ol>
* <li> ask current GDL scope for similar predicates.</li>
* <li> iterate through each of the predicates:
* <ol>
* <li> check type of expression being currently iterated
* <ol>
* <li> if an implication happens to be retrieved:
* <ol>
* <li> ask it's consiquence for a MGU</li>
* <li> proceed to <code>chain</code> of the premises (those are
* stored in an <code>ExpressionList</code></li>
* <li> store the result</li>
* </ol></li>
* <li> otherwise just as for an MGU with iterated expression</li>
* </ol></li>
* <li> store any non-empty results</li>
* </ol>
* </li>
* <li> if no results were accumulated, assume it as resolution failure
* and return empty list of substitutions</li>
* </ol>
*
* @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.
*/
@Override
protected List<Substitution> chainBody( Substitution sigma,
RuleScope scope,
TimerFlag flag )
throws InterruptedException {
if ( scope.isDisproven(this) )
return new ArrayList<Substitution>();
else if ( scope.isProven(this) )
return scope.getProven( sigma, this );
List<Substitution> answers = chainSimilarExpressions(sigma, scope, flag);
// Memoize the values returned
if ( !answers.isEmpty() )
scope.setProven(this, answers);
// memoize the failure
else
scope.setDisproven(this);
return answers;
}
/**
* This private method acts only as an entry point for <code>chain</code>
* method. It hides all the stuff specific to getting similar expressions
* from the rules scope and iterating over them. Implemented only for the
* sake of readability.
*
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#chainBody(Substitution, RuleScope, TimerFlag) Predicate.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.
*/
private List<Substitution> chainSimilarExpressions( Substitution sigma,
RuleScope scope,
TimerFlag flag )
throws InterruptedException {
List<Substitution> answers = new ArrayList<Substitution>();
for ( Expression anExpression : scope.getSimilarExpressions(this) )
if (anExpression instanceof Implication)
chainImplication( sigma,
scope,
flag,
answers,
(Implication) anExpression );
else {
Substitution psi = mgu(anExpression, sigma);
if (psi != null)
answers.add(psi);
}
return answers;
}
/**
* This private method acts only as an entry point for
* <code>chainSimilarExpressions</code> method
* It takes care of handling any implications that appear during iteration.
* Implemented for the sake of readability only.
* sigma
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#chainSimilarExpressions(Substitution, RuleScope, TimerFlag) chainSimilarExpressions
* @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.
* @param accumulator List of substitutions that actually accumulates any
* successful results
* @param imp Current implication to be resolved.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
private void chainImplication( Substitution sigma,
RuleScope scope,
TimerFlag flag,
List<Substitution> accumulator,
Implication imp )
throws InterruptedException {
if ( flag.interrupted() ) throw Const.interrupt;
Substitution psi = mgu(imp.getConsequence(), sigma);
if (psi != null) {
List<Substitution> substitutions =
imp.getPremises().chain(psi, scope, flag);
if ( !substitutions.isEmpty() )
accumulator.addAll(substitutions);
}
}
/**
* Trick about <code>Predicate</code> is that there might be implication consequences
* and single predicates (both static and dynamic) recieved after matching
* with existing data. So major trick is a kind of branching. Here is a step
* by step behavior:
* <ol>
* <li> ask current GDL scope for similar predicates.</li>
* <li> iterate through each of the predicates:
* <ol>
* <li>if an implication happens to be retrieved, ask it's
* consiquence for a MGU, and proceed to <code>chainOne</code>
* of the premises (those are stored in an
* <code>ExpressionList</code></li>
* <li>otherwise just as for an MGU with iterated expression</li>
* <li>if any of iterations produces non-empty result - return it</li>
* </ol>
* </li>
* <li> if no results were returned, assume it as resolution failure
* and return null</li>
* </ol>
*
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#mgu(Expression, Substitution) mgu
* @see de.tu_dresden.inf.ggp06_2.resolver.ExpressionList#chainOne(Substitution, RuleScope, TimerFlag) ExpressionList.chainOne
* @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.
*/
@Override
protected Substitution chainOneBody( Substitution sigma,
RuleScope scope,
TimerFlag flag )
throws InterruptedException {
// Check for memoized values, first
if ( scope.isProven(this) ) {
List<Substitution> allSubs = scope.getProven(sigma,this);
Substitution psi = allSubs.get(0);
return psi;
} else if ( scope.isDisproven(this) )
return null;
Substitution psi = null;
for ( Expression anExpression : scope.getSimilarExpressions(this) ) {
if (anExpression instanceof Implication) {
psi = chainOneImplication(
sigma, scope,
flag, (Implication )anExpression );
} else
psi = mgu(anExpression, sigma);
// Don't set proven for a single unifier--
// we need a full solution-set
if (psi != null)
return psi;
}
// But always memorize failure
scope.setDisproven(this);
return null;
}
/**
* This method acts only as an entry point for <code>chainOne</code> method.
* It actually takes care of handling implication, if an implication
* happens among expression returned by scope.
*
* @see de.tu_dresden.inf.ggp06_2.resolver.Predicate#chainOne(Substitution, RuleScope, TimerFlag) chainOne
* @param sigma State of resolution
* @param scope GDL rules scope
* @param flag Timer flag
* @param implication Actual implication to resolve with
* @return Returns substitution that resolves current predicate to given
* implication, if there is any.
* @throws InterruptedException This exception is thrown once time expires during
* procedure.
*/
private Substitution chainOneImplication( Substitution sigma,
RuleScope scope,
TimerFlag flag,
Implication implication )
throws InterruptedException {
if ( flag.interrupted() ) throw Const.interrupt;
Expression consequence = implication.getConsequence();
Substitution gamma = mgu(consequence, sigma);
return ( gamma != null ) ?
implication.getPremises().chainOne(gamma, scope, flag) :
null;
}
@Override
protected FuzzyResolution fuzzyEvaluateBody( FuzzySubstitution sigma,
GameStateScope scope,
List<Expression> guard,
TimerFlag flag )
throws InterruptedException {
FuzzyResolution sigmas = null;
if (scope.isProven( this )){
sigmas = new FuzzyResolution();
sigmas.addAll( scope.getProvenForFuzzy( sigma, this ) );
sigmas.setFuzzyValue( Expression.fuzzyOne );
return sigmas;
} else if (scope.isFuzzylyEvaluated(this) ) {
return scope.getFuzzyResolutionStage(sigma, this);
} else if ( guard.contains(operator) && sigma.isBottom() ){
sigmas = new FuzzyResolution();
sigmas.add( sigma );
sigmas.setFuzzyValue( Expression.fuzzyZero );
return sigmas;
}
int size = guard.size();
guard.add( size, this.operator);
sigmas = fuzzyEvaluateSimilarExpressions( sigma, scope, guard, flag );
guard.remove( size );
return sigmas;
}
private FuzzyResolution fuzzyEvaluateSimilarExpressions(
FuzzySubstitution sigma,
GameStateScope scope,
List<Expression> guard,
TimerFlag flag )
throws InterruptedException {
/*
* A trick about 'resolved' is that if
* some predicate at some point is resolved,
* we want to stop calculating fuzzy value for it.
* However, we would like to keep collecting successful
* substitutions. Therefore 'resolved' acts as a flag,
* indicating that no modifications to fuzzy value of
* current predicate should be done from now on.
*
* For second issue consider following rules:
* (<= (somepred ?x)
* (succ ?x ?y)
* (somepred ?y)
* )
* (somepred 1)
* (succ 2 1)
* (succ 3 2)
* (succ 4 3)
* The rules of 'somepred' kind should be treated as if they are
* combined with a T-Conorm (fuzzy conjunction),
* whereas the rules of 'succ' kind should result only to fuzzy
* truth or fuzzy false (no fuzzy conjunciton is assumed)
*
* Moreover if some candidate (disreguarding which kind) evaluates to
* fuzzy true (higher than a threashold), fuzzy true should be
* returned as the result.
*/
boolean noImplications = true;
ExpressionList candidates = scope.getSimilarExpressions(this);
FuzzyResolution sigmas = new FuzzyResolution();
for ( Expression e : candidates ) {
if (e instanceof Implication) {
Implication impl = (Implication) e;
Substitution psi = mgu( impl.getConsequence(), sigma );
noImplications = false;
if (null != psi) {
FuzzyResolution fuzzy = fuzzyEvaluatePremises(
new FuzzySubstitution(psi, sigma),
scope,
guard,
flag,
impl );
sigmas.addAlternativeResolution(fuzzy);
}
} else {
Substitution psi = mgu(e, sigma);
if (null != psi)
evaluatePositively( sigma, sigmas, psi );
else
evaluateNegatively( sigma, sigmas );
}
}
if (! noImplications)
scope.setFuzzyEvaluationStage(this, sigmas);
else if (sigmas.getFuzzyValue() < Expression.threshold)
sigmas.setFuzzyValue( Expression.fuzzyZero );
return sigmas;
}
protected void evaluateNegatively( FuzzySubstitution sigma,
FuzzyResolution sigmas ) {
sigma.setBottom( true );
sigma.tNorm(Expression.fuzzyZero);
sigmas.add( sigma );
}
protected void evaluateToFuzzyFalse( FuzzySubstitution sigma,
FuzzyResolution sigmas ) {
sigma.setBottom( true );
sigma.tNorm(Expression.fuzzyZero);
sigmas.add( sigma );
sigmas.setFuzzyValue( Expression.fuzzyZero );
}
protected void evaluatePositively( FuzzySubstitution sigma,
FuzzyResolution sigmas,
Substitution psi ) {
FuzzySubstitution fuzzySubstitution = new FuzzySubstitution(psi, sigma);
fuzzySubstitution.tNorm( Expression.fuzzyOne );
sigmas.add( fuzzySubstitution );
}
private FuzzyResolution fuzzyEvaluatePremises( FuzzySubstitution sigma,
GameStateScope scope,
List<Expression> guard,
TimerFlag flag,
Implication implication )
throws InterruptedException {
if ( flag.interrupted() ) throw Const.interrupt;
return implication.getPremises().fuzzyEvaluate( sigma, scope,
guard, flag );
}
@Override
public Atom firstOperand() {
return operator;
}
/**
* There are three possibilities in total for any connective.
* <ul>
* <li> Either it is unified with a variable, in this case
* unification is just passed to variable itself;</li>
* <li> Or with some kind of <code>Predicate</code>. If that
* is the case first an MGU of operators (operator names)
* is produced and resulting substitution is passed to
* produce MGU of operands;</li>
* <li> Any other case results into failure.</li>
* </ul>
*
* Method assumes that we do not have a variable as function symbol.
*
* @param target Target expression to unify with
* @param sigma Some current resolution stage
* @return Returns the most general unifier substitution, w.r.t
* current resolution stage.
*/
@Override
public Substitution mgu(Expression target, Substitution sigma) {
if (target instanceof Variable)
return ((Variable) target).mgu(this, sigma);
if (target instanceof Predicate) {
Predicate pTarget = (Predicate) target;
if ( operator.equals(pTarget.operator) )
return operands.mgu(pTarget.operands, sigma);
}
return null;
}
/**
* Two predicates are equal, if they have same operator and operands.
*/
@Override
public boolean equals(Object obj) {
return (obj instanceof Predicate) &&
operator.equals( ((Predicate) obj).operator ) &&
operands.equals( ((Predicate) obj).operands );
}
/**
* @return Returns string of form:
* "(" + operator + " " + operands + ")"
*/
@Override
public String toString() {
return (toString == null) ?
toString = "(" + operator + " " + operands + ")" :
toString;
}
@Override
public boolean isGround() {
for (Expression exp : operands)
if ( !exp.isGround() )
return false;
return true;
}
@Override
public Atom getKeyAtom() {
return operator;
}
@Override
public void processVisitor(AbstractVisitor visitor) {
visitor.visitPredicate(this);
}
}