package aima.core.logic.propositional.algorithms;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import aima.core.logic.propositional.parsing.PEParser;
import aima.core.logic.propositional.parsing.ast.Sentence;
import aima.core.logic.propositional.parsing.ast.Symbol;
import aima.core.logic.propositional.parsing.ast.UnarySentence;
import aima.core.logic.propositional.visitors.CNFClauseGatherer;
import aima.core.logic.propositional.visitors.CNFTransformer;
import aima.core.logic.propositional.visitors.SymbolClassifier;
import aima.core.logic.propositional.visitors.SymbolCollector;
import aima.core.util.Converter;
import aima.core.util.SetOps;
/**
* @author Ravi Mohan
* @author Mike Stampone
*/
public class DPLL {
private static final Converter<Symbol> SYMBOL_CONVERTER = new Converter<Symbol>();
/**
* Returns <code>true</code> if the specified sentence is satisfiable. A
* sentence is satisfiable if it is true in, or satisfied by, some model.
*
* @param s
* a sentence in propositional logic
*
* @return <code>true</code> if the specified sentence is satisfiable.
*/
public boolean dpllSatisfiable(Sentence s) {
return dpllSatisfiable(s, new Model());
}
/**
* Returns <code>true</code> if the specified sentence is satisfiable. A
* sentence is satisfiable if it is true in, or satisfied by, some model.
*
* @param string
* a String representation of a Sentence in propositional logic
*
* @return <code>true</code> if the specified sentence is satisfiable.
*/
public boolean dpllSatisfiable(String string) {
Sentence sen = (Sentence) new PEParser().parse(string);
return dpllSatisfiable(sen, new Model());
}
/**
* Returns <code>true</code> if the specified sentence is satisfiable. A
* sentence is satisfiable if it is true in, or satisfied by, some model.
*
* @param s
* a sentence in propositional logic
* @param m
* a model the sentence must be true in
*
* @return <code>true</code> if the specified sentence is satisfiable.
*/
public boolean dpllSatisfiable(Sentence s, Model m) {
Set<Sentence> clauses = new CNFClauseGatherer()
.getClausesFrom(new CNFTransformer().transform(s));
List<Symbol> symbols = SYMBOL_CONVERTER.setToList(new SymbolCollector()
.getSymbolsIn(s));
// System.out.println(" numberOfSymbols = " + symbols.size());
return dpll(clauses, symbols, m);
}
public List<Sentence> clausesWithNonTrueValues(List<Sentence> clauseList,
Model model) {
List<Sentence> clausesWithNonTrueValues = new ArrayList<Sentence>();
for (int i = 0; i < clauseList.size(); i++) {
Sentence clause = clauseList.get(i);
if (!(isClauseTrueInModel(clause, model))) {
if (!(clausesWithNonTrueValues.contains(clause))) {// defensive
// programming not really necessary
clausesWithNonTrueValues.add(clause);
}
}
}
return clausesWithNonTrueValues;
}
public SymbolValuePair findPureSymbolValuePair(List<Sentence> clauseList,
Model model, List<Symbol> symbols) {
List<Sentence> clausesWithNonTrueValues = clausesWithNonTrueValues(
clauseList, model);
Sentence nonTrueClauses = LogicUtils.chainWith("AND",
clausesWithNonTrueValues);
// System.out.println("Unsatisfied clauses = "
// + clausesWithNonTrueValues.size());
Set<Symbol> symbolsAlreadyAssigned = model.getAssignedSymbols();
// debug
// List symList = asList(symbolsAlreadyAssigned);
//
// System.out.println(" assignedSymbols = " + symList.size());
// if (symList.size() == 52) {
// System.out.println("untrue clauses = " + clausesWithNonTrueValues);
// System.out.println("model= " + model);
// }
// debug
List<Symbol> purePositiveSymbols = SYMBOL_CONVERTER.setToList(SetOps
.difference(new SymbolClassifier()
.getPurePositiveSymbolsIn(nonTrueClauses),
symbolsAlreadyAssigned));
List<Symbol> pureNegativeSymbols = SYMBOL_CONVERTER.setToList(SetOps
.difference(new SymbolClassifier()
.getPureNegativeSymbolsIn(nonTrueClauses),
symbolsAlreadyAssigned));
// if none found return "not found
if ((purePositiveSymbols.size() == 0)
&& (pureNegativeSymbols.size() == 0)) {
return new SymbolValuePair();// automatically set to null values
} else {
if (purePositiveSymbols.size() > 0) {
Symbol symbol = new Symbol(
(purePositiveSymbols.get(0)).getValue());
if (pureNegativeSymbols.contains(symbol)) {
throw new RuntimeException("Symbol " + symbol.getValue()
+ "misclassified");
}
return new SymbolValuePair(symbol, true);
} else {
Symbol symbol = new Symbol(
(pureNegativeSymbols.get(0)).getValue());
if (purePositiveSymbols.contains(symbol)) {
throw new RuntimeException("Symbol " + symbol.getValue()
+ "misclassified");
}
return new SymbolValuePair(symbol, false);
}
}
}
//
// PRIVATE METHODS
//
private boolean dpll(Set<Sentence> clauses, List<Symbol> symbols,
Model model) {
// List<Sentence> clauseList = asList(clauses);
List<Sentence> clauseList = new Converter<Sentence>()
.setToList(clauses);
// System.out.println("clauses are " + clauses.toString());
// if all clauses are true return true;
if (areAllClausesTrue(model, clauseList)) {
// System.out.println(model.toString());
return true;
}
// if even one clause is false return false
if (isEvenOneClauseFalse(model, clauseList)) {
// System.out.println(model.toString());
return false;
}
// System.out.println("At least one clause is unknown");
// try to find a unit clause
SymbolValuePair svp = findPureSymbolValuePair(clauseList, model,
symbols);
if (svp.notNull()) {
List<Symbol> newSymbols = new ArrayList<Symbol>(symbols);
newSymbols.remove(new Symbol(svp.symbol.getValue()));
Model newModel = model.extend(new Symbol(svp.symbol.getValue()),
svp.value.booleanValue());
return dpll(clauses, newSymbols, newModel);
}
SymbolValuePair svp2 = findUnitClause(clauseList, model, symbols);
if (svp2.notNull()) {
List<Symbol> newSymbols = new ArrayList<Symbol>(symbols);
newSymbols.remove(new Symbol(svp2.symbol.getValue()));
Model newModel = model.extend(new Symbol(svp2.symbol.getValue()),
svp2.value.booleanValue());
return dpll(clauses, newSymbols, newModel);
}
Symbol symbol = (Symbol) symbols.get(0);
// System.out.println("default behaviour selecting " + symbol);
List<Symbol> newSymbols = new ArrayList<Symbol>(symbols);
newSymbols.remove(0);
return (dpll(clauses, newSymbols, model.extend(symbol, true)) || dpll(
clauses, newSymbols, model.extend(symbol, false)));
}
private boolean isEvenOneClauseFalse(Model model, List<Sentence> clauseList) {
for (int i = 0; i < clauseList.size(); i++) {
Sentence clause = clauseList.get(i);
if (model.isFalse(clause)) {
// System.out.println(clause.toString() + " is false");
return true;
}
}
return false;
}
private boolean areAllClausesTrue(Model model, List<Sentence> clauseList) {
for (int i = 0; i < clauseList.size(); i++) {
Sentence clause = clauseList.get(i);
// System.out.println("evaluating " + clause.toString());
if (!isClauseTrueInModel(clause, model)) { // ie if false or
// UNKNOWN
// System.out.println(clause.toString()+ " is not true");
return false;
}
}
return true;
}
private boolean isClauseTrueInModel(Sentence clause, Model model) {
List<Symbol> positiveSymbols = SYMBOL_CONVERTER
.setToList(new SymbolClassifier().getPositiveSymbolsIn(clause));
List<Symbol> negativeSymbols = SYMBOL_CONVERTER
.setToList(new SymbolClassifier().getNegativeSymbolsIn(clause));
for (Symbol symbol : positiveSymbols) {
if ((model.isTrue(symbol))) {
return true;
}
}
for (Symbol symbol : negativeSymbols) {
if ((model.isFalse(symbol))) {
return true;
}
}
return false;
}
private SymbolValuePair findUnitClause(List<Sentence> clauseList,
Model model, List<Symbol> symbols) {
for (int i = 0; i < clauseList.size(); i++) {
Sentence clause = (Sentence) clauseList.get(i);
if ((clause instanceof Symbol)
&& (!(model.getAssignedSymbols().contains(clause)))) {
// System.out.println("found unit clause - assigning");
return new SymbolValuePair(new Symbol(
((Symbol) clause).getValue()), true);
}
if (clause instanceof UnarySentence) {
UnarySentence sentence = (UnarySentence) clause;
Sentence negated = sentence.getNegated();
if ((negated instanceof Symbol)
&& (!(model.getAssignedSymbols().contains(negated)))) {
// System.out.println("found unit clause type 2 -
// assigning");
return new SymbolValuePair(new Symbol(
((Symbol) negated).getValue()), false);
}
}
}
return new SymbolValuePair();// failed to find any unit clause;
}
public class SymbolValuePair {
public Symbol symbol;// public to avoid unnecessary get and set
// accessors
public Boolean value;
public SymbolValuePair() {
// represents "No Symbol found with a boolean value that makes all
// its literals true
symbol = null;
value = null;
}
public SymbolValuePair(Symbol symbol, boolean bool) {
// represents "Symbol found with a boolean value that makes all
// its literals true
this.symbol = symbol;
value = new Boolean(bool);
}
public boolean notNull() {
return (symbol != null) && (value != null);
}
@Override
public String toString() {
String symbolString, valueString;
if (symbol == null) {
symbolString = "NULL";
} else {
symbolString = symbol.toString();
}
if (value == null) {
valueString = "NULL";
} else {
valueString = value.toString();
}
return symbolString + " -> " + valueString;
}
}
}