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    }