/**
* SPINdle (version 2.2.2)
* Copyright (C) 2009-2012 NICTA Ltd.
*
* This file is part of SPINdle project.
*
* SPINdle 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 3 of the License, or
* (at your option) any later version.
*
* SPINdle 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 SPINdle. If not, see <http://www.gnu.org/licenses/>.
*
* @author H.-P. Lam (oleklam@gmail.com), National ICT Australia - Queensland Research Laboratory
*/
package spindle.tools.evaluator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import javax.script.ScriptEngine;
import com.app.utils.ComparableEntry;
import spindle.core.MessageType;
import spindle.core.dom.AppConstants;
import spindle.core.dom.DomConst;
import spindle.core.dom.DomUtilities;
import spindle.core.dom.Literal;
import spindle.core.dom.LiteralVariable;
import spindle.core.dom.Rule;
import spindle.core.dom.RuleType;
import spindle.core.dom.Theory;
import spindle.core.dom.TheoryException;
import spindle.io.ParserException;
import spindle.io.parser.DflTheoryParser2;
import spindle.sys.AppConst;
import spindle.sys.AppModuleBase;
import spindle.sys.AppModuleListener;
import spindle.sys.Conf;
import spindle.sys.NullValueException;
import spindle.sys.message.ErrorMessage;
/**
* Literal variable evaluator.
*
* @author H.-P. Lam (oleklam@gmail.com), National ICT Australia - Queensland Research Laboratory
* @since 2011.07.26
* @since version 2.1.0
*/
public class LiteralVariablesEvaluator extends AppModuleBase {
private static ScriptEngine evaluator = Conf.getScriptEngine();
private AppConstants appConstants = AppConstants.getInstance(null);
private Theory theory = null;
Map<LiteralVariable, LiteralVariable> simplifiedLiteralVariables = null;
Map<LiteralVariable, String> literalFunctionAnswers = null;
Map<LiteralVariable,Literal>literalVariablesUpdate=null;
Set<Rule> rulesToAdd = null;
public Theory evaluateLiteralVariables(Theory theory) throws LiteralVariablesEvaluatorException {
if (null == theory) throw new LiteralVariablesEvaluatorException(ErrorMessage.THEORY_NULL_THEORY);
this.theory = theory;
logMessage(Level.FINE, 0, "LiteralVariableEvaluator.evaluateLiteralVariables - start");
simplifiedLiteralVariables = new TreeMap<LiteralVariable, LiteralVariable>();
literalFunctionAnswers = new TreeMap<LiteralVariable, String>();
literalVariablesUpdate=new TreeMap<LiteralVariable, Literal>();
rulesToAdd = new TreeSet<Rule>();
simplifyLiteralVariables();
validateLiteralVariables();
theory.clearLiteralVariables();
simplifyTheoryBooleanFunctions(theory.getLiteralBooleanFunctions());
if (theory.getLiteralBooleanFunctionsInRulesCount() > 0) {
Map<LiteralVariable, LiteralVariable> theoryBooleanFunctionsInRules = new TreeMap<LiteralVariable, LiteralVariable>();
for (LiteralVariable literalVariable : theory.getLiteralBooleanFunctionsInRules()) {
theoryBooleanFunctionsInRules.put(literalVariable, literalVariable);
}
simplifyTheoryBooleanFunctions(theoryBooleanFunctionsInRules);
}
if (!AppConst.isDeploy) {
for (java.util.Map.Entry<LiteralVariable, String> entry : literalFunctionAnswers.entrySet()) {
logMessage(Level.INFO, 1, "* literalFunctionAnswers.entry:- ", entry);
}
}
theory.clearLiteralBooleanFunctions();
try {
for (java.util.Map.Entry<LiteralVariable,Literal>entry:literalVariablesUpdate.entrySet()){
LiteralVariable lv=entry.getKey();
Literal l=entry.getValue();
logMessage(Level.FINER, 1, "update literal variable to regular literal:", lv, " => ", l);
this.theory.updateLiteralVariableInRule(lv,l);
}
for (Rule rule : rulesToAdd) {
logMessage(Level.FINER, 1, "add new rule to theory:", rule);
this.theory.addRule(rule);
}
} catch (TheoryException e) {
throw new LiteralVariablesEvaluatorException(e);
}
logMessage(Level.FINE, 0, "LiteralVariableEvaluator.evaluateLiteralVariables - end");
return this.theory;
}
/**
* Validate the correctness of the literal variables generated.
*
* @throws LiteralVariablesEvaluatorException Signals that exception of some sort has occurred for a literal
* variable.
*/
private void validateLiteralVariables() throws LiteralVariablesEvaluatorException {
LiteralVariable lvName = null;
try {
for (java.util.Map.Entry<LiteralVariable, LiteralVariable> entry : simplifiedLiteralVariables.entrySet()) {
lvName = entry.getKey();
LiteralVariable lv = entry.getValue();
// verify the correctness of a literal variable if it is an application constant
if (appConstants.isAppConstant(lv)) appConstants.getAppConstantAsLiteralVariable(lv);
}
} catch (Exception e) {
throw new LiteralVariablesEvaluatorException(lvName.toString() + " :- " + e.getMessage(), e);
}
}
/**
* Simplify the literal variable values using application constants.
*
* @throws LiteralVariablesEvaluatorException Signals that either a literal variable cannot be simplified using
* application constants or an exception of some sort has occured.
*/
private void simplifyLiteralVariables() throws LiteralVariablesEvaluatorException {
if (theory.getLiteralVariableCount() == 0) return;
// simplify the literal variable values using application constants
Map<LiteralVariable, ComparableEntry<LiteralVariable, Set<LiteralVariable>>> literalVariables = new TreeMap<LiteralVariable, ComparableEntry<LiteralVariable, Set<LiteralVariable>>>();
for (java.util.Map.Entry<LiteralVariable, LiteralVariable> entry : theory.getLiteralVariables().entrySet()) {
Set<LiteralVariable> lvEncountered = new TreeSet<LiteralVariable>();
lvEncountered.add(entry.getValue());
ComparableEntry<LiteralVariable, Set<LiteralVariable>> entryRecord = new ComparableEntry<LiteralVariable, Set<LiteralVariable>>(entry.getValue(), lvEncountered);
literalVariables.put(entry.getKey(), entryRecord);
}
Map<LiteralVariable, LiteralVariable> updateSet = new TreeMap<LiteralVariable, LiteralVariable>();
Set<LiteralVariable> removeSet = new TreeSet<LiteralVariable>();
do {
updateSet.clear();
removeSet.clear();
for (java.util.Map.Entry<LiteralVariable, ComparableEntry<LiteralVariable, Set<LiteralVariable>>> entry : literalVariables.entrySet()) {
logMessage(Level.FINER, 1, "evaluating: ", entry);
LiteralVariable lvValue = entry.getValue().getKey();
if (null != isUserDefinedVariable(lvValue)) {
logMessage(Level.FINEST, 1, AppConst.IDENTATOR + "==> contains user defined variable!");
LiteralVariable lvValueC = lvValue.getComplementClone();
if (simplifiedLiteralVariables.containsKey(lvValue)) {
logMessage(Level.FINEST, 2, "==> in simplified literal variable set");
updateSet.put(entry.getKey(), simplifiedLiteralVariables.get(lvValue));
} else if (simplifiedLiteralVariables.containsKey(lvValueC)) {
logMessage(Level.FINEST, 2, "==> NEGATION in simplified literal variable set");
updateSet.put(entry.getKey(), simplifiedLiteralVariables.get(lvValueC).getComplementClone());
}
} else {
logMessage(Level.FINER, 2, "==> contains NO user defined variable");
removeSet.add(entry.getKey());
simplifiedLiteralVariables.put(entry.getKey(), lvValue);
}
}
for (java.util.Map.Entry<LiteralVariable, LiteralVariable> entry : updateSet.entrySet()) {
ComparableEntry<LiteralVariable, Set<LiteralVariable>> lvEntry = literalVariables.get(entry.getKey());
Set<LiteralVariable> historySet = lvEntry.getValue();
if (historySet.contains(entry.getValue())) {
historySet.add(entry.getKey());
throw new LiteralVariablesEvaluatorException(ErrorMessage.LITERAL_VARIABLE_EVALUATOR_CYCLIC_VARIABLE_DEPENDENCIES,
new Object[] { historySet.toString() });
}
historySet.add(entry.getValue());
lvEntry.setKey(entry.getValue());
literalVariables.remove(entry.getKey());
literalVariables.put(entry.getKey(), lvEntry);
}
for (LiteralVariable literalVariable : removeSet) {
literalVariables.remove(literalVariable);
}
} while (updateSet.size() > 0 || removeSet.size() > 0);
if (!AppConst.isDeploy) {
logMessage(Level.INFO, 1, "simplified variables - start");
for (java.util.Map.Entry<LiteralVariable, LiteralVariable> entry : simplifiedLiteralVariables.entrySet()) {
logMessage(Level.INFO, 2, "+-- " + entry.toString());
}
logMessage(Level.INFO, 1, "simplified variables - end");
}
if (literalVariables.size() > 0) {
Object[] args = new Object[] { literalVariables.keySet() };
throw new LiteralVariablesEvaluatorException(ErrorMessage.LITERAL_VARIABLE_EVALUATOR_THEORY_VARIABLE_UNRESOLVABLE, args);
}
}
private void simplifyTheoryBooleanFunctions(Map<LiteralVariable, LiteralVariable> theoryBooleanFunctions) throws LiteralVariablesEvaluatorException {
if (theoryBooleanFunctions.size() == 0) return;
Set<LiteralVariable>literalVariablesToRemove=new TreeSet<LiteralVariable>();
Map<LiteralVariable, ComparableEntry<LiteralVariable, Set<LiteralVariable>>> theoryBooleanFunctionsToEvaluate = new TreeMap<LiteralVariable, ComparableEntry<LiteralVariable, Set<LiteralVariable>>>();
try {
for (java.util.Map.Entry<LiteralVariable, LiteralVariable> entry : theoryBooleanFunctions.entrySet()) {
String theoryBooleanFunctionStr = entry.getValue().getName();
Set<LiteralVariable> userVariableSet = extractUserLiteralVariablesFromBooleanFunction(theoryBooleanFunctionStr);
if (userVariableSet.size() == 0) {
boolean ans = evaluateBooleanValue(theoryBooleanFunctionStr);
literalFunctionAnswers.put(entry.getKey(), "" + ans);
addBooleanConclusionGenerated(entry.getKey(), ans);
literalVariablesToRemove.add(entry.getKey());
} else {
theoryBooleanFunctionsToEvaluate.put(entry.getKey(), new ComparableEntry<LiteralVariable, Set<LiteralVariable>>(entry.getValue(), userVariableSet));
}
}
Set<LiteralVariable> updatedSet = new TreeSet<LiteralVariable>();
do {
updatedSet.clear();
for (java.util.Map.Entry<LiteralVariable, ComparableEntry<LiteralVariable, Set<LiteralVariable>>> entry : theoryBooleanFunctionsToEvaluate.entrySet()) {
Set<LiteralVariable> missingLiteralVariables = entry.getValue().getValue();
Set<LiteralVariable> removeSet = new TreeSet<LiteralVariable>();
for (LiteralVariable literalVariable : missingLiteralVariables) {
if (literalFunctionAnswers.containsKey(literalVariable)) removeSet.add(literalVariable);
}
missingLiteralVariables.removeAll(removeSet);
if (missingLiteralVariables.size() == 0) {
boolean ans = evaluateBooleanValue(entry.getValue().getKey().getName());
literalFunctionAnswers.put(entry.getKey(), "" + ans);
addBooleanConclusionGenerated(entry.getKey(), ans);
updatedSet.add(entry.getKey());
literalVariablesToRemove.add(entry.getKey());
}
}
for (LiteralVariable literalVariable : updatedSet) {
theoryBooleanFunctionsToEvaluate.remove(literalVariable);
}
} while (updatedSet.size() > 0);
} catch (LiteralVariablesEvaluatorException e) {
throw e;
} catch (Exception e) {
throw new LiteralVariablesEvaluatorException(e);
}
}
private void addBooleanConclusionGenerated(LiteralVariable literalVariable, boolean ans) throws TheoryException {
logMessage(Level.FINE, 1, "literal variable: ", literalVariable, ", ans=", ans);
Literal headLiteral = DomUtilities.getLiteral(literalVariable);
headLiteral.setPlaceHolder(true);
literalVariablesUpdate.put(literalVariable, headLiteral);
if (ans) {
try {
Rule newRule = DomUtilities.getRule(theory.getUniqueRuleLabel(), RuleType.FACT);
newRule.addHeadLiteral(headLiteral);
rulesToAdd.add(newRule);
logMessage(Level.FINER, 2, "generate new fact:", newRule);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private Set<LiteralVariable> extractUserLiteralVariablesFromBooleanFunction(final String literalVariableStr) throws LiteralVariablesEvaluatorException {
Set<LiteralVariable> userLiteralVariables = new TreeSet<LiteralVariable>();
if (null == literalVariableStr || "".equals(literalVariableStr.trim())) return userLiteralVariables;
try {
List<String> tokens = DflTheoryParser2.getTokenizeLiteralFunction(literalVariableStr, simplifiedLiteralVariables, literalFunctionAnswers);
for (String token : tokens) {
LiteralVariable literalVariable = isUserDefinedVariable(token);
if (null != literalVariable) userLiteralVariables.add(literalVariable);
}
return userLiteralVariables;
} catch (LiteralVariablesEvaluatorException e) {
throw e;
} catch (Exception e) {
throw new LiteralVariablesEvaluatorException(e);
}
}
protected LiteralVariable isUserDefinedVariable(String literalVariableStr) throws LiteralVariablesEvaluatorException {
if (literalVariableStr.startsWith("" + DomConst.Literal.LITERAL_VARIABLE_PREFIX)
|| literalVariableStr.startsWith("" + DomConst.Literal.LITERAL_NEGATION_SIGN + DomConst.Literal.LITERAL_VARIABLE_PREFIX)) {
try {
LiteralVariable literalVariable = DflTheoryParser2.extractLiteralVariable(literalVariableStr);
return isUserDefinedVariable(literalVariable);
} catch (ParserException e) {
System.err.println("literalVariableStr [" + literalVariableStr + "] cannot be parsed");
return null;
} catch (Exception e) {
System.err.println("literalVariableStr [" + literalVariableStr + "] " + e.getMessage());
return null;
}
} else return null;
}
private LiteralVariable isUserDefinedVariable(LiteralVariable literalVariable) throws LiteralVariablesEvaluatorException {
try {
if (literalVariable.isLiteralVariable()) {
return (appConstants.isAppConstant(literalVariable)) ? null : literalVariable;
} else throw new LiteralVariablesEvaluatorException("literal variable: [" + literalVariable + "] is a boolean function");
} catch (NullValueException e) {
}
return literalVariable;
}
private boolean evaluateBooleanValue(String str) throws ValueEvaluationException {
try {
String s = DflTheoryParser2.getLiteralFunctionEvaluationString(str, simplifiedLiteralVariables, literalFunctionAnswers);
s = s.substring(1, s.length() - 1);
Object res = evaluator.eval(s);
if (res instanceof Boolean) {
return (Boolean) res;
} else throw new ValueEvaluationException(ErrorMessage.LITERAL_VARIABLE_EVALUATOR_IMPROPER_RESULT_TYPE, new Object[] { "Boolean",
getResultType(res) });
} catch (Exception e) {
throw new ValueEvaluationException(e);
}
}
private String getResultType(Object res) {
if (res instanceof Boolean) {
return "Boolean";
} else if (res instanceof Long) {
return "Long";
} else if (res instanceof Integer) {
return "Integer";
} else if (res instanceof Double) {
return "Double";
} else if (res instanceof Float) { return "Float"; }
return res.getClass().getName();
}
// @SuppressWarnings("unused")
// private void removeLiteralVariable() {
//
// }
public Theory getTheory() {
return theory;
}
public void addLiteralVariablesEvaluatorListener(LiteralVariablesEvaluatorListener listener) {
addAppModuleListener(listener);
}
public void removeLiteralVariablesEvaluatorListener(LiteralVariablesEvaluatorListener listener) {
removeAppModuleListener(listener);
}
protected void fireLiteralVariablesEvaluatorMessage(MessageType messageType, String message) {
for (AppModuleListener listener : getAppModuleListeners()) {
if (listener instanceof LiteralVariablesEvaluatorListener) {
((LiteralVariablesEvaluatorListener) listener).onLiteralVariablesEvaluatorMesage(messageType, message);
}
}
}
}