001 /* 002 * Created on Dec 4, 2004 003 * 004 */ 005 package aima.logic.propositional.algorithms; 006 007 import java.util.ArrayList; 008 import java.util.List; 009 import java.util.Set; 010 011 import aima.logic.propositional.parsing.PEParser; 012 import aima.logic.propositional.parsing.ast.Sentence; 013 import aima.logic.propositional.parsing.ast.Symbol; 014 import aima.logic.propositional.parsing.ast.UnarySentence; 015 import aima.logic.propositional.visitors.CNFClauseGatherer; 016 import aima.logic.propositional.visitors.CNFTransformer; 017 import aima.logic.propositional.visitors.SymbolClassifier; 018 import aima.logic.propositional.visitors.SymbolCollector; 019 import aima.util.Converter; 020 import aima.util.LogicUtils; 021 import aima.util.SetOps; 022 023 /** 024 * @author Ravi Mohan 025 * 026 */ 027 028 public class DPLL { 029 030 private static final Converter<Symbol> SYMBOL_CONVERTER = new Converter<Symbol>(); 031 032 public boolean dpllSatisfiable(Sentence s) { 033 034 return dpllSatisfiable(s, new Model()); 035 } 036 037 public boolean dpllSatisfiable(String string) { 038 Sentence sen = (Sentence) new PEParser().parse(string); 039 return dpllSatisfiable(sen, new Model()); 040 } 041 042 public boolean dpllSatisfiable(Sentence s, Model m) { 043 Set<Sentence> clauses = new CNFClauseGatherer() 044 .getClausesFrom(new CNFTransformer().transform(s)); 045 List symbols = SYMBOL_CONVERTER.setToList(new SymbolCollector() 046 .getSymbolsIn(s)); 047 // System.out.println(" numberOfSymbols = " + symbols.size()); 048 return dpll(clauses, symbols, m); 049 } 050 051 private boolean dpll(Set<Sentence> clauses, List symbols, Model model) { 052 // List<Sentence> clauseList = asList(clauses); 053 List<Sentence> clauseList = new Converter<Sentence>() 054 .setToList(clauses); 055 // System.out.println("clauses are " + clauses.toString()); 056 // if all clauses are true return true; 057 if (areAllClausesTrue(model, clauseList)) { 058 // System.out.println(model.toString()); 059 return true; 060 } 061 // if even one clause is false return false 062 if (isEvenOneClauseFalse(model, clauseList)) { 063 // System.out.println(model.toString()); 064 return false; 065 } 066 // System.out.println("At least one clause is unknown"); 067 // try to find a unit clause 068 SymbolValuePair svp = findPureSymbolValuePair(clauseList, model, 069 symbols); 070 if (svp.notNull()) { 071 List newSymbols = (List) ((ArrayList) symbols).clone(); 072 newSymbols.remove(new Symbol(svp.symbol.getValue())); 073 Model newModel = model.extend(new Symbol(svp.symbol.getValue()), 074 svp.value.booleanValue()); 075 return dpll(clauses, newSymbols, newModel); 076 } 077 078 SymbolValuePair svp2 = findUnitClause(clauseList, model, symbols); 079 if (svp2.notNull()) { 080 List newSymbols = (List) ((ArrayList) symbols).clone(); 081 newSymbols.remove(new Symbol(svp2.symbol.getValue())); 082 Model newModel = model.extend(new Symbol(svp2.symbol.getValue()), 083 svp2.value.booleanValue()); 084 return dpll(clauses, newSymbols, newModel); 085 } 086 087 Symbol symbol = (Symbol) symbols.get(0); 088 // System.out.println("default behaviour selecting " + symbol); 089 List newSymbols = (List) ((ArrayList) symbols).clone(); 090 newSymbols.remove(0); 091 return (dpll(clauses, newSymbols, model.extend(symbol, true)) || dpll( 092 clauses, newSymbols, model.extend(symbol, false))); 093 } 094 095 private boolean isEvenOneClauseFalse(Model model, List clauseList) { 096 for (int i = 0; i < clauseList.size(); i++) { 097 Sentence clause = (Sentence) clauseList.get(i); 098 if (model.isFalse(clause)) { 099 // System.out.println(clause.toString() + " is false"); 100 return true; 101 } 102 103 } 104 105 return false; 106 } 107 108 private boolean areAllClausesTrue(Model model, List clauseList) { 109 110 for (int i = 0; i < clauseList.size(); i++) { 111 Sentence clause = (Sentence) clauseList.get(i); 112 // System.out.println("evaluating " + clause.toString()); 113 if (!isClauseTrueInModel(clause, model)) { // ie if false or 114 // UNKNOWN 115 // System.out.println(clause.toString()+ " is not true"); 116 return false; 117 } 118 119 } 120 return true; 121 } 122 123 private boolean isClauseTrueInModel(Sentence clause, Model model) { 124 List<Symbol> positiveSymbols = SYMBOL_CONVERTER 125 .setToList(new SymbolClassifier().getPositiveSymbolsIn(clause)); 126 List<Symbol> negativeSymbols = SYMBOL_CONVERTER 127 .setToList(new SymbolClassifier().getNegativeSymbolsIn(clause)); 128 129 for (Symbol symbol : positiveSymbols) { 130 if ((model.isTrue(symbol))) { 131 return true; 132 } 133 } 134 for (Symbol symbol : negativeSymbols) { 135 if ((model.isFalse(symbol))) { 136 return true; 137 } 138 } 139 return false; 140 141 } 142 143 public List<Sentence> clausesWithNonTrueValues(List<Sentence> clauseList, 144 Model model) { 145 List<Sentence> clausesWithNonTrueValues = new ArrayList<Sentence>(); 146 for (int i = 0; i < clauseList.size(); i++) { 147 Sentence clause = clauseList.get(i); 148 if (!(isClauseTrueInModel(clause, model))) { 149 if (!(clausesWithNonTrueValues.contains(clause))) {// defensive 150 // programming not really necessary 151 clausesWithNonTrueValues.add(clause); 152 } 153 } 154 155 } 156 return clausesWithNonTrueValues; 157 } 158 159 public SymbolValuePair findPureSymbolValuePair(List<Sentence> clauseList, 160 Model model, List symbols) { 161 List clausesWithNonTrueValues = clausesWithNonTrueValues(clauseList, 162 model); 163 Sentence nonTrueClauses = LogicUtils.chainWith("AND", 164 clausesWithNonTrueValues); 165 // System.out.println("Unsatisfied clauses = " 166 // + clausesWithNonTrueValues.size()); 167 Set<Symbol> symbolsAlreadyAssigned = model.getAssignedSymbols(); 168 169 // debug 170 // List symList = asList(symbolsAlreadyAssigned); 171 // 172 // System.out.println(" assignedSymbols = " + symList.size()); 173 // if (symList.size() == 52) { 174 // System.out.println("untrue clauses = " + clausesWithNonTrueValues); 175 // System.out.println("model= " + model); 176 // } 177 178 // debug 179 List<Symbol> purePositiveSymbols = SYMBOL_CONVERTER 180 .setToList(new SetOps<Symbol>().difference( 181 new SymbolClassifier() 182 .getPurePositiveSymbolsIn(nonTrueClauses), 183 symbolsAlreadyAssigned)); 184 185 List<Symbol> pureNegativeSymbols = SYMBOL_CONVERTER 186 .setToList(new SetOps<Symbol>().difference( 187 new SymbolClassifier() 188 .getPureNegativeSymbolsIn(nonTrueClauses), 189 symbolsAlreadyAssigned)); 190 // if none found return "not found 191 if ((purePositiveSymbols.size() == 0) 192 && (pureNegativeSymbols.size() == 0)) { 193 return new SymbolValuePair();// automatically set to null values 194 } else { 195 if (purePositiveSymbols.size() > 0) { 196 Symbol symbol = new Symbol((purePositiveSymbols.get(0)) 197 .getValue()); 198 if (pureNegativeSymbols.contains(symbol)) { 199 throw new RuntimeException("Symbol " + symbol.getValue() 200 + "misclassified"); 201 } 202 return new SymbolValuePair(symbol, true); 203 } else { 204 Symbol symbol = new Symbol((pureNegativeSymbols.get(0)) 205 .getValue()); 206 if (purePositiveSymbols.contains(symbol)) { 207 throw new RuntimeException("Symbol " + symbol.getValue() 208 + "misclassified"); 209 } 210 return new SymbolValuePair(symbol, false); 211 } 212 } 213 } 214 215 private SymbolValuePair findUnitClause(List clauseList, Model model, 216 List symbols) { 217 for (int i = 0; i < clauseList.size(); i++) { 218 Sentence clause = (Sentence) clauseList.get(i); 219 if ((clause instanceof Symbol) 220 && (!(model.getAssignedSymbols().contains(clause)))) { 221 // System.out.println("found unit clause - assigning"); 222 return new SymbolValuePair(new Symbol(((Symbol) clause) 223 .getValue()), true); 224 } 225 226 if (clause instanceof UnarySentence) { 227 UnarySentence sentence = (UnarySentence) clause; 228 Sentence negated = sentence.getNegated(); 229 if ((negated instanceof Symbol) 230 && (!(model.getAssignedSymbols().contains(negated)))) { 231 // System.out.println("found unit clause type 2 - 232 // assigning"); 233 return new SymbolValuePair(new Symbol(((Symbol) negated) 234 .getValue()), false); 235 } 236 } 237 238 } 239 240 return new SymbolValuePair();// failed to find any unit clause; 241 242 } 243 244 public class SymbolValuePair { 245 public Symbol symbol;// public to avoid unnecessary get and set 246 247 // accessors 248 249 public Boolean value; 250 251 public SymbolValuePair() { 252 // represents "No Symbol found with a boolean value that makes all 253 // its literals true 254 symbol = null; 255 value = null; 256 } 257 258 public SymbolValuePair(Symbol symbol, boolean bool) { 259 // represents "Symbol found with a boolean value that makes all 260 // its literals true 261 this.symbol = symbol; 262 value = new Boolean(bool); 263 } 264 265 public boolean notNull() { 266 return (symbol != null) && (value != null); 267 } 268 269 @Override 270 public String toString() { 271 String symbolString, valueString; 272 if (symbol == null) { 273 symbolString = "NULL"; 274 } else { 275 symbolString = symbol.toString(); 276 } 277 if (value == null) { 278 valueString = "NULL"; 279 } else { 280 valueString = value.toString(); 281 } 282 return symbolString + " -> " + valueString; 283 } 284 } 285 286 }