001    package aima.logic.propositional.algorithms;
002    
003    import java.util.ArrayList;
004    import java.util.Hashtable;
005    import java.util.Iterator;
006    import java.util.List;
007    import java.util.Set;
008    import java.util.Stack;
009    
010    import aima.logic.propositional.parsing.ast.BinarySentence;
011    import aima.logic.propositional.parsing.ast.Sentence;
012    import aima.logic.propositional.parsing.ast.Symbol;
013    import aima.logic.propositional.visitors.SymbolCollector;
014    import aima.util.Converter;
015    
016    /**
017     * @author Ravi Mohan
018     * 
019     */
020    
021    public class PLFCEntails {
022    
023            private Hashtable<HornClause, Integer> count;
024    
025            private Hashtable<Symbol, Boolean> inferred;
026    
027            private Stack<Symbol> agenda;
028    
029            public PLFCEntails() {
030                    count = new Hashtable<HornClause, Integer>();
031                    inferred = new Hashtable<Symbol, Boolean>();
032                    agenda = new Stack<Symbol>();
033            }
034    
035            public boolean plfcEntails(KnowledgeBase kb, String s) {
036                    return plfcEntails(kb, new Symbol(s));
037            }
038    
039            public boolean plfcEntails(KnowledgeBase kb, Symbol q) {
040                    List<HornClause> hornClauses = asHornClauses(kb.getSentences());
041                    while (agenda.size() != 0) {
042                            Symbol p = agenda.pop();
043                            while (!inferred(p)) {
044                                    inferred.put(p, Boolean.TRUE);
045    
046                                    for (int i = 0; i < hornClauses.size(); i++) {
047                                            HornClause hornClause = hornClauses.get(i);
048                                            if (hornClause.premisesContainsSymbol(p)) {
049                                                    decrementCount(hornClause);
050                                                    if (countisZero(hornClause)) {
051                                                            if (hornClause.head().equals(q)) {
052                                                                    return true;
053                                                            } else {
054                                                                    agenda.push(hornClause.head());
055                                                            }
056                                                    }
057                                            }
058                                    }
059                            }
060                    }
061                    return false;
062            }
063    
064            private List<HornClause> asHornClauses(List sentences) {
065                    List<HornClause> hornClauses = new ArrayList<HornClause>();
066                    for (int i = 0; i < sentences.size(); i++) {
067                            Sentence sentence = (Sentence) sentences.get(i);
068                            HornClause clause = new HornClause(sentence);
069                            hornClauses.add(clause);
070                    }
071                    return hornClauses;
072            }
073    
074            private boolean countisZero(HornClause hornClause) {
075    
076                    return (count.get(hornClause)).intValue() == 0;
077            }
078    
079            private void decrementCount(HornClause hornClause) {
080                    int value = (count.get(hornClause)).intValue();
081                    count.put(hornClause, new Integer(value - 1));
082    
083            }
084    
085            private boolean inferred(Symbol p) {
086                    Object value = inferred.get(p);
087                    return ((value == null) || value.equals(Boolean.TRUE));
088            }
089    
090            public class HornClause {
091                    List<Symbol> premiseSymbols;
092    
093                    Symbol head;
094    
095                    public HornClause(Sentence sentence) {
096                            if (sentence instanceof Symbol) {
097                                    head = (Symbol) sentence;
098                                    agenda.push(head);
099                                    premiseSymbols = new ArrayList<Symbol>();
100                                    count.put(this, new Integer(0));
101                                    inferred.put(head, Boolean.FALSE);
102                            } else if (!isImpliedSentence(sentence)) {
103                                    throw new RuntimeException("Sentence " + sentence
104                                                    + " is not a horn clause");
105    
106                            } else {
107                                    BinarySentence bs = (BinarySentence) sentence;
108                                    head = (Symbol) bs.getSecond();
109                                    inferred.put(head, Boolean.FALSE);
110                                    Set<Symbol> symbolsInPremise = new SymbolCollector()
111                                                    .getSymbolsIn(bs.getFirst());
112                                    Iterator<Symbol> iter = symbolsInPremise.iterator();
113                                    while (iter.hasNext()) {
114                                            inferred.put(iter.next(), Boolean.FALSE);
115                                    }
116                                    premiseSymbols = new Converter<Symbol>()
117                                                    .setToList(symbolsInPremise);
118                                    count.put(this, new Integer(premiseSymbols.size()));
119                            }
120    
121                    }
122    
123                    private boolean isImpliedSentence(Sentence sentence) {
124                            return ((sentence instanceof BinarySentence) && ((BinarySentence) sentence)
125                                            .getOperator().equals("=>"));
126                    }
127    
128                    public Symbol head() {
129    
130                            return head;
131                    }
132    
133                    public boolean premisesContainsSymbol(Symbol q) {
134                            return premiseSymbols.contains(q);
135                    }
136    
137                    public List getPremiseSymbols() {
138                            return premiseSymbols;
139                    }
140    
141                    @Override
142                    public boolean equals(Object o) {
143    
144                            if (this == o) {
145                                    return true;
146                            }
147                            if ((o == null) || (this.getClass() != o.getClass())) {
148                                    return false;
149                            }
150                            HornClause ohc = (HornClause) o;
151                            if (premiseSymbols.size() != ohc.premiseSymbols.size()) {
152                                    return false;
153                            }
154                            boolean result = true;
155                            for (Symbol s : premiseSymbols) {
156                                    if (!ohc.premiseSymbols.contains(s)) {
157                                            return false;
158                                    }
159                            }
160    
161                            return true;
162    
163                    }
164    
165                    @Override
166                    public int hashCode() {
167                            int result = 17;
168                            for (Symbol s : premiseSymbols) {
169                                    result = 37 * result + s.hashCode();
170                            }
171                            return result;
172                    }
173    
174                    @Override
175                    public String toString() {
176                            return premiseSymbols.toString() + " => " + head;
177                    }
178            }
179    }