package infosapient.system;
/*
* Copyright (c) 2001, Workplace Performance Tools, All Rights Reserved.
*
* LICENSE TO USE THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THE COMMON PUBLIC LICENSE 0.5
* ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
*/
import infosapient.system.FzyRule;
import infosapient.system.FzyRuleComponent;
import infosapient.system.FzyPremise;
import infosapient.system.FzyConsequent;
import infosapient.opr.FzyOperator;
import java.rmi.server.ObjID;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Stack;
import java.util.EmptyStackException;
/**
*
* Class FzyTorquemada is responsible for controlling the execution
* of the rules within the knowledgebase.
* <p>Explaination system is built into FzyTorquemada to help explain:
* <b><ul> <li> Why did the model make that recommendation?</li>
* <li> How much confidence do I have in the recommendation? </li>
* <li> Was there any significant evidence overlooked? </li>
* <li> Is this model working properly? </li> </ul></b>
*
* Note: The explaination facility is not implemented.
* Scheduled for version BOSON.
*
*
* @author:
* @version $Revision: 1.1.1.1 $
*
* @package infosapient.system
*/
public class FzyTorquemada extends FzySystemComponent implements Runnable {
private double[] result = { Double.NaN, Double.NaN };
static final long serialVersionUID = 3333063543440663172L;
private FzyKnowledgebase theKB = null;
private FzySolutionSet outputSpace = null;
private FzyAttribute outputAttribute = null;
private transient java.util.Date theDate = new java.util.Date();
private infosapient.resolution.FzyCorrelation corrMth = null;
private infosapient.resolution.FzyResolutionMethod defuzzMth = null;
private infosapient.resolution.FzyImplicationMethod implicMth = null;
/** The goal to be solved during the consultation session.
*/
private FzyAttribute endGoal = null;
private FSEvent runTimeEvent = null;
/**
* Used to suppress explainatory messages, warnings, etc, when being used in
* Server mode.
*/
private boolean suppressNotification = false;
/**
* Default constructor for this executive
*/
public FzyTorquemada() {
super.setID(new java.rmi.server.ObjID());
}
/**
* Constructor used to execute within visual environment
*/
public FzyTorquemada(FzyKnowledgebase kb) throws IllegalArgumentException {
this();
if ((theKB = kb)==null) throw new IllegalArgumentException("Attempt to construct FzyTorquemada with a null KnowledgeBase.");
corrMth = theKB.getCorrelationMethod();
defuzzMth = theKB.getResolutionMethod();
implicMth = theKB.getImplicationMethod();
runTimeEvent = new FSEvent(this, null, null);
setChanged();
notifyObservers(" Rule execution log for KnowledgeBase '"+ theKB.getName() +"' at " + theDate.toString());
}
/**
* Constructor used to notify Executive that no information messages will be output during
* rule processing. Typically used for runtime only.
*/
public FzyTorquemada(FzyKnowledgebase kb, boolean rt) {
this(kb);
suppressNotification = rt;
}
private FzyAttribute buildOutputSpace() { return null;}
/**
* Attempt to evaluate the FzyClauseComponent. A rule may have one or more separate premise clauses.
* If it does it is represented by the tree:
* <p><code>
* <DL>
* <DL>
* <DL>
* <DL>
* <DL>
* <DL><TT>FzyOperator</TT>
* <DL><TT>/ \</TT></DL>
* </DL>
* </DL>
* </DL>
* </DL>
* </DL>
* <TT>(FzyAttribClause | FzyOperator) (FzyAttribClause)</TT>
* <DL>
* <DL>
* <DL>
* <DL><TT>/ \</TT></DL>
* <TT>(etc.) (FzyAttribClause)</TT></DL>
* </DL>
* </DL>
* </DL>
*
*</code>
* <p> If the FzyClauseComponent has a solution, return the FzyClauseComponent.
*
* <p> If the FzyClauseComponent <b><code>instanceof</code></b> FzyOperator, evaluate the tree, using
* <code>evaluateComponent(FzyClauseComponent)</code>.
*
*
* <p> If the FzyClauseComponent isNOT an instance of FzyOperator, the FzyClauseComponent will
* be an instance of FzyAttribClause. Evaluate FzyAttribClause by invoking <code>solveGoal(FzyAttribClause)</code>.
*
* @param FzyClauseComponent -- the clause component to be evaluated.
* @return FzyClauseComponent -- the clause component WITH a solution.
* @throws CloneNotSupportedException -- if object fails to support Cloneable interface. (Should NEVER occur.)
* @throws FzySystemException -- if FzyAttribClause.getGoal() fails
* @see FzyClauseComponent
* @see infosapient.system.FzyAttribClause
* @see infosapient.system.FzyAttribClause#getGoal
* @see infosapient.opr.FzyOperator
*
*/
private FzyClauseComponent evaluateComponent(FzyClauseComponent fcc)
throws CloneNotSupportedException, infosapient.system.FzySystemException {
double d0, d1;
synchronized (fcc) {
infosapient.opr.FzyOperator fo;
if (fcc.hasSolution())
return fcc;
else
if (fcc instanceof infosapient.opr.FzyOperator) {
fo = (infosapient.opr.FzyOperator) fcc;
if (!fo.getRightChild().hasSolution())
evaluateComponent(fo.getRightChild());
if (!fo.getLeftChild().hasSolution())
evaluateComponent(fo.getLeftChild());
try {
/* get the degree of membership for each solution within each child of the FzyOperator.
* Apply the solution using the algorithm that each specialized class of FzyOperator represents.
*
*/
d0 = fo.getLeftChild().getSolutionDOM();
d1 = fo.getRightChild().getSolutionDOM();
fo.apply(d0, d1);
} catch (infosapient.system.FzySystemException fse) {
fo.setSolutionDOM(Double.NaN);
setChanged();
runTimeEvent.setCmd("Error");
String stackStr = infosapient.util.UtilityOperations.getStackTrace((Throwable) fse);
runTimeEvent.setPackage(fse);
notifyObservers(runTimeEvent);
} finally {
return fo;
}
} else {
/*
* This branch indicates that fcc is an instanceof FzyAttribClause.
* Invoke solveGoal to evaluate the FzyAttribClause.
*/
return (solveGoal((infosapient.system.FzyAttribClause) fcc));
}
}
}
/**
* Evaluate the premise to see if we can determine the consequent. This is done
* by invoking <code><b>solveGoal</b></code> to put the current unknown premise attribute
* on the solveGoal agenda.
* <p>Invoke evaluateComponent to get the DOM(degree of membership) of the premise goal.
* We check for DOM > 0.0 because we don't need to do any work
* if the premise DOM is 0.0.
* <ul>
* <li>1. Apply DOM: Correlate the premise to the consequent using the
* specific algorithm represented by the specialization of
* FzyCorrelation.This will adjust the height (and base if FzyCorrelationProduct) of the
* of the Consequent solution set.</li>
* <li>2. Apply results: to be cumulative or simply the maximum of the premise to the consequent,
* using the specialization of FzyImplicationMethod. </li>
* <li>3. Defuzzify (e.g. GET AN ANSWER) by using the specialization of
* FzyResolutionMethod.</li></ul>
* <p>
* We check for dm < 1.0 because if dm == 1.0 there will be no
* correlation/implication/resolution needed.
* We check altConseqGoal() != null.
* <p><b>If above checks succeed:</b>
* Apply the inverse of the degree of membership (1.0 - dm) of
* the primary consequent goal to the alternateConsequentGoal.
* This is done because the alternate goal has membership only to the
* extent that the primary goal does NOT have membership.
* @param FzyRule the rule to be evaluated.
* @throws IllegalArgumentException -- FzyRule == null
*/
private void evaluateRule(FzyRule aRule) throws IllegalArgumentException {
if (aRule == null)
throw new IllegalArgumentException(" FzyTorquemada: evaluateRule invoked with null Rule");
FzyClauseComponent revisedPGoal = null;
FzySet corrSet = null;
double result = 0.0;
double dm = 0.0;
FzyPremise premise = (FzyPremise) aRule.getPremise();
FzyClauseComponent pGoal = premise.getComponent();
FzyConsequent conseq = (FzyConsequent) aRule.getConsequent();
infosapient.system.FzyAttribClause conseqClause = conseq.getConsequentGoal();
try {
revisedPGoal = evaluateComponent(pGoal);
dm = revisedPGoal.getSolutionDOM();
if (dm > 0.0d) {
//
// Correlate the consequent to the premise using the correlation method specified.
//
corrSet = corrMth.applyCorrelation(dm, (FzySet) conseqClause.getGoal());
//
// Add the correlated set to the solution set using the implication method.
//
implicMth.applyImplicationMethod(corrSet, conseqClause.getSolutionSet());
result = defuzzMth.resolveToScalar(conseqClause.getSolutionSet());
conseqClause.getSolutionSet().setSolution(result);
conseqClause.getAttribute().setCurrentDomainValue(result);
}
if ((conseq.getAltConseqGoal() != null) && (dm < 1.0d)) {
infosapient.system.FzyAttribClause conseqAltGoalClause =
conseq.getAltConseqGoal();
//
// Correlate the consequent to the premise using the correlation method specified.
//
corrSet =
corrMth.applyCorrelation((1.0 - dm), (FzySet) conseqAltGoalClause.getGoal());
//
// Add the correlated set to the solution set using the implication method.
//
implicMth.applyImplicationMethod(corrSet, conseqClause.getSolutionSet());
result = defuzzMth.resolveToScalar(conseqClause.getSolutionSet());
conseqClause.getSolutionSet().setSolution(result);
conseqClause.getAttribute().setCurrentDomainValue(result);
}
} catch (Exception e) {
runTimeEvent.setCmd("Error");
String stackStr =
infosapient.util.UtilityOperations.getStackTrace((Throwable) e);
e.printStackTrace();
runTimeEvent.setPackage(e);
setChanged();
notifyObservers(runTimeEvent);
}
}
/**
* <DL>
* <DD>
* Determine the value of the knowledgebase current goal. Do this by invoking
* (private) method solveGoal on the FzyAttribClause of the goal attribute.
* Procedure solveGoal begins a recursive chain to determine what attributes
* affect the current goal; the rules whose consequents determine those attributes;
* solve for those attributes successively until the current attribute is
* determined.</DD>
*
* <BLOCKQUOTE><B><FONT FACE="Courier New,Courier">Procedure</FONT></B> <I>solveGoal</I>
* (<FONT FACE="Courier New,Courier">FzyAttribClause goal</FONT>)
* <BLOCKQUOTE>FzyAttribClause goal: the FzyAttribClause containing the attribute
* we need to solve.
*
* <P><B><FONT FACE="Courier New,Courier">if</FONT></B> <FONT FACE="Courier New,Courier">FzyAttribClause{goal}
* has a solution</FONT>
* <BLOCKQUOTE>Return <FONT FACE="Courier New,Courier">FzyAttribClause{goal}</FONT>.</BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">end if</FONT></B>
* <BR>Get the list of all rules within the knowledgebase who's consequent
* solves the current goal attribute.
*
* <P><B><FONT FACE="Courier New,Courier">if </FONT></B>the rule list is not
* empty
* <BR>
* <BLOCKQUOTE><B><FONT FACE="Courier New,Courier">for</FONT><TT> </TT></B>each
* rule whose consequent determines the goal attribute
*
* <P><B><FONT FACE="Courier New,Courier">do</FONT></B>
* <BLOCKQUOTE><B><FONT FACE="Courier New,Courier">invoke</FONT><TT> </TT></B><I>evaluateRule(aFuzzyRule)</I>
* with the rule</BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">end do</FONT></B></BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">else </FONT></B>(the rule list <I>is
* </I>empty)
* <BLOCKQUOTE>Ask the user for the value of the goal attribute. Set the current
* domain value of the goal attribute to the value supplied by the user.</BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">end if</FONT></B><B><FONT FACE="Courier New,Courier"></FONT></B>
*
* <P>Return the <FONT FACE="Courier New,Courier">FzyAttribClause</FONT>.</BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">end procedure</FONT></B>
*
* <P><B><FONT FACE="Courier New,Courier">procedure</FONT></B> <B><I>evaluateRule</I></B>(FzyRule
* aRule)
* <BLOCKQUOTE>aRule: the rule to be attempted by <B>evaluateRule</B>
*
* <P><B><FONT FACE="Courier New,Courier">for</FONT></B> each component (<FONT FACE="Courier New,Courier">FzyClauseComponent</FONT>)
* in the premise of aRule <B><FONT FACE="Courier New,Courier">do</FONT></B>
* <BLOCKQUOTE><B><FONT FACE="Courier New,Courier">invoke</FONT></B> <I>evaluateComponent</I>
* with <FONT FACE="Courier New,Courier">FzyClauseComponent fcc</FONT>
*
* <P><B><FONT FACE="Courier New,Courier">if</FONT><TT> </TT></B>the degree
* of membership of the solution of FzyClauseComponent{fcc} is >0.0
*
* <P><B><FONT FACE="Courier New,Courier">then</FONT></B>
* <BLOCKQUOTE>
* <OL>
* <LI>
* correlate the degree of membership of the solution of fcc with the goal
* of the consequent</LI>
*
* <LI>
* use the selected implication method to add the correlated set of the consequents
* goal</LI>
*
* <LI>
* apply the selected resolution method to obtain the degree of membership
* for the consequents goal.</LI>
* </OL>
* </BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">end if</FONT></B>
*
* <P><B><FONT FACE="Courier New,Courier">if </FONT></B>the degree of membership
* of the solution of<B><FONT FACE="Courier New,Courier"> </FONT></B>FzyClauseComponent{fcc}
* is < 1.0
*
* <P><B><FONT FACE="Courier New,Courier">then</FONT></B>
* <BLOCKQUOTE>
* <OL>
* <LI>
* correlate (1.0 - DOM of fcc) with the <I>alternate consequent </I>goal</LI>
*
* <LI>
* use the selected implication method to add the correlated set to the alternate
* consequents goal</LI>
*
* <LI>
* apply the selected resolution method to obtain the degree of membership
* for the alternate consequents goal.</LI>
* </OL>
* </BLOCKQUOTE>
* </BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier"> end if</FONT></B><B><FONT FACE="Courier New,Courier"></FONT></B>
*
* <P><B><FONT FACE="Courier New,Courier">end for</FONT></B>
* <BR> </BLOCKQUOTE>
* <B><FONT FACE="Courier New,Courier">end procedure</FONT></B><B><FONT FACE="Courier New,Courier"></FONT></B>
*
* <P><B><FONT FACE="Courier New,Courier">procedure </FONT></B><I>evaluateComponent<FONT FACE="Courier New,Courier">(</FONT></I><FONT FACE="Courier New,Courier">FzyClauseComponent
* fcc)</FONT><B><I><FONT FACE="Courier New,Courier"></FONT></I></B>
*
* <P><FONT FACE="Courier New,Courier">FzyClauseComponent fcc: </FONT>The
* clause component to be evaluated. A FzyClauseComponent can be an instance
* of FzySet, FzyAttribute, FzyAttribClause, or FzyOperator.<FONT FACE="Courier New,Courier"></FONT>
*
* <P><FONT FACE="Courier New,Courier"> <B>if </B>FzyClauseComponent{fcc}
* has a solution</FONT>
* <BR><FONT FACE="Courier New,Courier"> <B>then</B></FONT>
* <BR><FONT FACE="Courier New,Courier"><B>
* return </B>FzyClauseComponent{fcc}</FONT>
* <BR><B><FONT FACE="Courier New,Courier"> end if</FONT></B>
* <BR><B><FONT FACE="Courier New,Courier"> </FONT></B>
* <BR><FONT FACE="Courier New,Courier"><B> if </B>FzyClauseComponent{fcc}
* is instanceof FzyOperator</FONT>
* <BR><FONT FACE="Courier New,Courier"> <B>then</B></FONT>
* <BR><B><FONT FACE="Courier New,Courier">
* if </FONT></B>(FzyOperator) fcc left child DOES NOT have a solution
* <BR><B><FONT FACE="Courier New,Courier">
* then</FONT></B>
* <BR><B><FONT FACE="Courier New,Courier">
* </FONT></B>invoke <B><I>evaluateComponent</I></B>((FzyOperator) fcc.leftchild);
* <BR><B><FONT FACE="Courier New,Courier">
* end if</FONT></B>
* <BR><B><FONT FACE="Courier New,Courier">
* if </FONT></B>(FzyOperator) fcc right child DOES NOT have a solution
* <BR><B><FONT FACE="Courier New,Courier">
* then</FONT></B>
* <BR><B><FONT FACE="Courier New,Courier">
* </FONT></B>invoke <B><I>evaluateComponent</I></B>((FzyOperator) fcc.rightchild);
* <BR><B><FONT FACE="Courier New,Courier">
* end if</FONT></B><B><FONT FACE="Courier New,Courier"></FONT></B>
*
* <P><B><FONT FACE="Courier New,Courier">
* </FONT></B>invoke (FzyOperator) fcc.<B><I>apply</I></B>(fcc.leftChild,
* fcc.rightChild);
* <BR><B><FONT FACE="Courier New,Courier"> </FONT></B>
* <BR><B><FONT FACE="Courier New,Courier"> else</FONT></B><B><FONT FACE="Courier New,Courier"></FONT></B>
*
* <P><FONT FACE="Courier New,Courier"><B>
* </B>invoke<B> </B></FONT><B><I>solveGoal</I></B><FONT FACE="Courier New,Courier">(FzyClauseComponent{fcc})</FONT>
* <BR><B><FONT FACE="Courier New,Courier"> </FONT></B>
* <BR><B><FONT FACE="Courier New,Courier"> end if</FONT></B>
* <BR><B><FONT FACE="Courier New,Courier"> </FONT></B>
* <BR><B><FONT FACE="Courier New,Courier"> </FONT></B>return
* <FONT FACE="Courier New,Courier">FzyClauseComponent{fcc};</FONT>
* <BR><B><FONT FACE="Courier New,Courier">end procedure</FONT></B><B><FONT FACE="Courier New,Courier"></FONT></B>
*
* <P><B><FONT FACE="Courier New,Courier"> </FONT></B></BLOCKQUOTE>
* </DL>
*
*/
public synchronized void run() {
infosapient.system.FzyAttribClause currGoalClause, endClause;
currGoalClause =
endClause = null;
try {
setChanged();
endGoal = theKB.getGoal();
if (endGoal == null) throw new FzySystemException(this + " The current goal is NULL or undefined.");
java.util.Vector cVector = theKB.getConseqClauses(endGoal);
if (cVector == null) {
endGoal.ask();
return;
} else currGoalClause = (infosapient.system.FzyAttribClause) cVector.firstElement();
if (theKB.isRunBefore()) {
this.buildOutputSpace();
endClause = this.solveGoal(currGoalClause);
}else {
endClause = this.solveGoal(currGoalClause);
this.buildOutputSpace();
}
} catch (Exception e) {
runTimeEvent.setCmd("Error");
String stackStr = infosapient.util.UtilityOperations.getStackTrace((Throwable)e);
runTimeEvent.setPackage(stackStr);
setChanged();
e.printStackTrace();
notifyObservers(runTimeEvent);
}
double df;
if (!endClause.hasSolution()) result[0] = defuzzMth.resolveToScalar(endClause.getSolutionSet());
else result[0] = endClause.getSolution();
com.objectspace.jgl.Array goalArray = endClause.getAttribute().containsValue(result[0]);
Enumeration e = goalArray.elements();
FzySet goalFS = null;
java.util.Properties returnProperties = null;
if (suppressNotification) returnProperties = new java.util.Properties();
try {
while (e.hasMoreElements()) {
goalFS = (FzySet)e.nextElement();
if (!suppressNotification) {
setChanged();
String s;
notifyObservers(s="\n\n\n Solution is: " + goalFS.getQualifiedName()
+ "\n having a resolved value of: " + result[0]
+ "\n and compatibility index of: " + goalFS.getDOM(result[0]));
if (DEBUG_[0]) {
System.err.println(" Correlation method: " + corrMth.getName() + "\n"
+" Implication method: " + this.implicMth.getName() + "\n"
+" Resolution method: " + this.defuzzMth.getName() );
System.err.println(s);
}
} else {
returnProperties.put("AS",goalFS.getQualifiedName());
returnProperties.put("D", Double.toString(result[0]));
returnProperties.put("M", Double.toString(goalFS.getDOM(result[0])));
}
}
if (suppressNotification) {
setChanged();
runTimeEvent = new FSEvent(this, "Solution", returnProperties);
notifyObservers(runTimeEvent);
}
} catch (Exception exc) {
runTimeEvent.setCmd("Error");
String stackStr = infosapient.util.UtilityOperations.getStackTrace((Throwable)exc);
runTimeEvent.setPackage(stackStr);
setChanged();
notifyObservers(runTimeEvent);
}
}
/**
* Attempt to find the solution of the <code> currentGoal</code> by finding out which rules solve for this goal
* and then executing those rules.
* @param infosapient.system.FzyAttribClause the goal to be solved for.
* @returns infosapient.system.FzyAttribClause the solved goal.
* @throws IllegalArgumentException - if the currentGoal is null.
*/
private infosapient.system.FzyAttribClause solveGoal(
infosapient.system.FzyAttribClause currentGoal)
throws IllegalArgumentException {
if (currentGoal == null)
throw new IllegalArgumentException(" FzyTorquemada: solveGoal invoked with null FzyAttribute");
int ruleCnt = 0;
int nbrRules = 0;
FzyAttribute goalAttrib = currentGoal.getAttribute();
try {
if (currentGoal.hasSolution())
return currentGoal;
if (!suppressNotification) {
setChanged();
notifyObservers(
"\n\n We are attempting to determine '"
+ goalAttrib.getName()
+ "' as the current goal.");
}
java.util.Vector deducingRulesList =
(java.util.Vector) theKB.getDeducingRules().get(goalAttrib.getName());
if (deducingRulesList == null) {
if (!currentGoal.hasSolution()) {
goalAttrib.ask();
currentGoal.getSolutionSet().setSolution(goalAttrib.getCurrentDomainValue());
if (!suppressNotification) {
notifyObservers(
"\n Current goal "
+ goalAttrib.getName()
+ " Solution now set at: "
+ goalAttrib.getCurrentDomainValue());
}
}
return currentGoal;
} else {
/*
* Search the rules to see if there are any rules that have this attribClause as its consequent.
*/
int drx;
nbrRules = deducingRulesList.size();
if (!suppressNotification) {
String rNames[] = new String[nbrRules];
for (drx = 0; drx < nbrRules; drx++) {
rNames[drx] =
"\n "
+ ((FzyRule) deducingRulesList.elementAt(drx)).getName()
+ ": "
+ ((FzyRule) deducingRulesList.elementAt(drx)).getText();
}
setChanged();
notifyObservers(
"\n\n\n Relevant Rules for '" + goalAttrib.getName() + "' are:");
setChanged();
notifyObservers(rNames);
}
for (int krx = 0; krx < nbrRules; krx++) {
infosapient.system.FzyRule currRule =
(infosapient.system.FzyRule) deducingRulesList.elementAt(krx);
evaluateRule(currRule);
}
double result;
/*
* We are asking that the solution set not be null and that an answer has
* NOT been computed for it yet.
*/
if ((!currentGoal.getSolutionSet().isEmpty())
&& (!currentGoal.hasSolution())) {
;
} else if (!currentGoal.hasSolution()) {
goalAttrib.ask();
currentGoal.getSolutionSet().setSolution(goalAttrib.getCurrentDomainValue());
if (!suppressNotification) {
notifyObservers(
"\n Current goal "
+ goalAttrib.getName()
+ " Solution now set at: "
+ goalAttrib.getCurrentDomainValue());
}
}
}
} catch (Exception e) {
runTimeEvent.setCmd("Error");
String stackStr =
infosapient.util.UtilityOperations.getStackTrace((Throwable) e);
e.printStackTrace();
runTimeEvent.setPackage(e);
setChanged();
notifyObservers(runTimeEvent);
}
return currentGoal;
}
}