PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/aima-core/src/main/java/aima/core/logic/fol/inference/FOLOTTERLikeTheoremProver.java

http://aima-java.googlecode.com/
Java | 614 lines | 403 code | 76 blank | 135 comment | 78 complexity | c11f21361de79c1eba5aca9e4dd1e6fb MD5 | raw file
Possible License(s): GPL-3.0, Apache-2.0
  1. package aima.core.logic.fol.inference;
  2. import java.util.ArrayList;
  3. import java.util.HashMap;
  4. import java.util.HashSet;
  5. import java.util.LinkedHashSet;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import aima.core.logic.fol.Connectors;
  10. import aima.core.logic.fol.SubsumptionElimination;
  11. import aima.core.logic.fol.inference.otter.ClauseFilter;
  12. import aima.core.logic.fol.inference.otter.ClauseSimplifier;
  13. import aima.core.logic.fol.inference.otter.LightestClauseHeuristic;
  14. import aima.core.logic.fol.inference.otter.defaultimpl.DefaultClauseFilter;
  15. import aima.core.logic.fol.inference.otter.defaultimpl.DefaultClauseSimplifier;
  16. import aima.core.logic.fol.inference.otter.defaultimpl.DefaultLightestClauseHeuristic;
  17. import aima.core.logic.fol.inference.proof.Proof;
  18. import aima.core.logic.fol.inference.proof.ProofFinal;
  19. import aima.core.logic.fol.inference.proof.ProofStepGoal;
  20. import aima.core.logic.fol.kb.FOLKnowledgeBase;
  21. import aima.core.logic.fol.kb.data.Clause;
  22. import aima.core.logic.fol.kb.data.Literal;
  23. import aima.core.logic.fol.parsing.ast.ConnectedSentence;
  24. import aima.core.logic.fol.parsing.ast.NotSentence;
  25. import aima.core.logic.fol.parsing.ast.Sentence;
  26. import aima.core.logic.fol.parsing.ast.Term;
  27. import aima.core.logic.fol.parsing.ast.TermEquality;
  28. import aima.core.logic.fol.parsing.ast.Variable;
  29. /**
  30. * Artificial Intelligence A Modern Approach (2nd Edition): Figure 9.14, page
  31. * 307.<br>
  32. * <br>
  33. *
  34. * <pre>
  35. * procedure OTTER(sos, usable)
  36. * inputs: sos, a set of support-clauses defining the problem (a global variable)
  37. * usable, background knowledge potentially relevant to the problem
  38. *
  39. * repeat
  40. * clause <- the lightest member of sos
  41. * move clause from sos to usable
  42. * PROCESS(INFER(clause, usable), sos)
  43. * until sos = [] or a refutation has been found
  44. *
  45. * --------------------------------------------------------------------------------
  46. *
  47. * function INFER(clause, usable) returns clauses
  48. *
  49. * resolve clause with each member of usable
  50. * return the resulting clauses after applying filter
  51. *
  52. * --------------------------------------------------------------------------------
  53. *
  54. * procedure PROCESS(clauses, sos)
  55. *
  56. * for each clause in clauses do
  57. * clause <- SIMPLIFY(clause)
  58. * merge identical literals
  59. * discard clause if it is a tautology
  60. * sos <- [clause | sos]
  61. * if clause has no literals then a refutation has been found
  62. * if clause has one literal then look for unit refutation
  63. * </pre>
  64. *
  65. * Figure 9.14 Sketch of the OTTER theorem prover. Heuristic control is applied
  66. * in the selection of the "lightest" clause and in the FILTER function that
  67. * eliminates uninteresting clauses from consideration.<br>
  68. * <br>
  69. * <b>Note:</b> The original implementation of OTTER has been retired but its
  70. * successor, <b>Prover9</b>, can be found at:<br>
  71. * <a href="http://www.prover9.org/">http://www.prover9.org/</a><br>
  72. * or<br>
  73. * <a href="http://www.cs.unm.edu/~mccune/mace4/">http://www.cs.unm.edu/~mccune/
  74. * mace4/</a><br>
  75. * Should you wish to play with a mature implementation of a theorem prover :-)<br>
  76. * <br>
  77. * For lots of interesting problems to play with, see <b>The TPTP Problem
  78. * Library for Automated Theorem Proving</b>:<br>
  79. * <a href="http://www.cs.miami.edu/~tptp/">http://www.cs.miami.edu/~tptp/</a><br>
  80. *
  81. * @author Ciaran O'Reilly
  82. *
  83. */
  84. public class FOLOTTERLikeTheoremProver implements InferenceProcedure {
  85. //
  86. // Ten seconds is default maximum query time permitted
  87. private long maxQueryTime = 10 * 1000;
  88. private boolean useParamodulation = true;
  89. private LightestClauseHeuristic lightestClauseHeuristic = new DefaultLightestClauseHeuristic();
  90. private ClauseFilter clauseFilter = new DefaultClauseFilter();
  91. private ClauseSimplifier clauseSimplifier = new DefaultClauseSimplifier();
  92. //
  93. private Paramodulation paramodulation = new Paramodulation();
  94. public FOLOTTERLikeTheoremProver() {
  95. }
  96. public FOLOTTERLikeTheoremProver(long maxQueryTime) {
  97. setMaxQueryTime(maxQueryTime);
  98. }
  99. public FOLOTTERLikeTheoremProver(boolean useParamodulation) {
  100. setUseParamodulation(useParamodulation);
  101. }
  102. public FOLOTTERLikeTheoremProver(long maxQueryTime,
  103. boolean useParamodulation) {
  104. setMaxQueryTime(maxQueryTime);
  105. setUseParamodulation(useParamodulation);
  106. }
  107. public long getMaxQueryTime() {
  108. return maxQueryTime;
  109. }
  110. public void setMaxQueryTime(long maxQueryTime) {
  111. this.maxQueryTime = maxQueryTime;
  112. }
  113. public boolean isUseParamodulation() {
  114. return useParamodulation;
  115. }
  116. public void setUseParamodulation(boolean useParamodulation) {
  117. this.useParamodulation = useParamodulation;
  118. }
  119. public LightestClauseHeuristic getLightestClauseHeuristic() {
  120. return lightestClauseHeuristic;
  121. }
  122. public void setLightestClauseHeuristic(
  123. LightestClauseHeuristic lightestClauseHeuristic) {
  124. this.lightestClauseHeuristic = lightestClauseHeuristic;
  125. }
  126. public ClauseFilter getClauseFilter() {
  127. return clauseFilter;
  128. }
  129. public void setClauseFilter(ClauseFilter clauseFilter) {
  130. this.clauseFilter = clauseFilter;
  131. }
  132. public ClauseSimplifier getClauseSimplifier() {
  133. return clauseSimplifier;
  134. }
  135. public void setClauseSimplifier(ClauseSimplifier clauseSimplifier) {
  136. this.clauseSimplifier = clauseSimplifier;
  137. }
  138. //
  139. // START-InferenceProcedure
  140. public InferenceResult ask(FOLKnowledgeBase KB, Sentence alpha) {
  141. Set<Clause> sos = new HashSet<Clause>();
  142. Set<Clause> usable = new HashSet<Clause>();
  143. // Usable set will be the set of clauses in the KB,
  144. // are assuming this is satisfiable as using the
  145. // Set of Support strategy.
  146. for (Clause c : KB.getAllClauses()) {
  147. c = KB.standardizeApart(c);
  148. c.setStandardizedApartCheckNotRequired();
  149. usable.addAll(c.getFactors());
  150. }
  151. // Ensure reflexivity axiom is added to usable if using paramodulation.
  152. if (isUseParamodulation()) {
  153. // Reflexivity Axiom: x = x
  154. TermEquality reflexivityAxiom = new TermEquality(new Variable("x"),
  155. new Variable("x"));
  156. Clause reflexivityClause = new Clause();
  157. reflexivityClause.addLiteral(new Literal(reflexivityAxiom));
  158. reflexivityClause = KB.standardizeApart(reflexivityClause);
  159. reflexivityClause.setStandardizedApartCheckNotRequired();
  160. usable.add(reflexivityClause);
  161. }
  162. Sentence notAlpha = new NotSentence(alpha);
  163. // Want to use an answer literal to pull
  164. // query variables where necessary
  165. Literal answerLiteral = KB.createAnswerLiteral(notAlpha);
  166. Set<Variable> answerLiteralVariables = KB
  167. .collectAllVariables(answerLiteral.getAtomicSentence());
  168. Clause answerClause = new Clause();
  169. if (answerLiteralVariables.size() > 0) {
  170. Sentence notAlphaWithAnswer = new ConnectedSentence(Connectors.OR,
  171. notAlpha, answerLiteral.getAtomicSentence());
  172. for (Clause c : KB.convertToClauses(notAlphaWithAnswer)) {
  173. c = KB.standardizeApart(c);
  174. c.setProofStep(new ProofStepGoal(c));
  175. c.setStandardizedApartCheckNotRequired();
  176. sos.addAll(c.getFactors());
  177. }
  178. answerClause.addLiteral(answerLiteral);
  179. } else {
  180. for (Clause c : KB.convertToClauses(notAlpha)) {
  181. c = KB.standardizeApart(c);
  182. c.setProofStep(new ProofStepGoal(c));
  183. c.setStandardizedApartCheckNotRequired();
  184. sos.addAll(c.getFactors());
  185. }
  186. }
  187. // Ensure all subsumed clauses are removed
  188. usable.removeAll(SubsumptionElimination.findSubsumedClauses(usable));
  189. sos.removeAll(SubsumptionElimination.findSubsumedClauses(sos));
  190. OTTERAnswerHandler ansHandler = new OTTERAnswerHandler(answerLiteral,
  191. answerLiteralVariables, answerClause, maxQueryTime);
  192. IndexedClauses idxdClauses = new IndexedClauses(
  193. getLightestClauseHeuristic(), sos, usable);
  194. return otter(ansHandler, idxdClauses, sos, usable);
  195. }
  196. // END-InferenceProcedure
  197. //
  198. /**
  199. * <pre>
  200. * procedure OTTER(sos, usable)
  201. * inputs: sos, a set of support-clauses defining the problem (a global variable)
  202. * usable, background knowledge potentially relevant to the problem
  203. * </pre>
  204. */
  205. private InferenceResult otter(OTTERAnswerHandler ansHandler,
  206. IndexedClauses idxdClauses, Set<Clause> sos, Set<Clause> usable) {
  207. getLightestClauseHeuristic().initialSOS(sos);
  208. // * repeat
  209. do {
  210. // * clause <- the lightest member of sos
  211. Clause clause = getLightestClauseHeuristic().getLightestClause();
  212. if (null != clause) {
  213. // * move clause from sos to usable
  214. sos.remove(clause);
  215. getLightestClauseHeuristic().removedClauseFromSOS(clause);
  216. usable.add(clause);
  217. // * PROCESS(INFER(clause, usable), sos)
  218. process(ansHandler, idxdClauses, infer(clause, usable), sos,
  219. usable);
  220. }
  221. // * until sos = [] or a refutation has been found
  222. } while (sos.size() != 0 && !ansHandler.isComplete());
  223. return ansHandler;
  224. }
  225. /**
  226. * <pre>
  227. * function INFER(clause, usable) returns clauses
  228. */
  229. private Set<Clause> infer(Clause clause, Set<Clause> usable) {
  230. Set<Clause> resultingClauses = new LinkedHashSet<Clause>();
  231. // * resolve clause with each member of usable
  232. for (Clause c : usable) {
  233. Set<Clause> resolvents = clause.binaryResolvents(c);
  234. for (Clause rc : resolvents) {
  235. resultingClauses.add(rc);
  236. }
  237. // if using paramodulation to handle equality
  238. if (isUseParamodulation()) {
  239. Set<Clause> paras = paramodulation.apply(clause, c, true);
  240. for (Clause p : paras) {
  241. resultingClauses.add(p);
  242. }
  243. }
  244. }
  245. // * return the resulting clauses after applying filter
  246. return getClauseFilter().filter(resultingClauses);
  247. }
  248. // procedure PROCESS(clauses, sos)
  249. private void process(OTTERAnswerHandler ansHandler,
  250. IndexedClauses idxdClauses, Set<Clause> clauses, Set<Clause> sos,
  251. Set<Clause> usable) {
  252. // * for each clause in clauses do
  253. for (Clause clause : clauses) {
  254. // * clause <- SIMPLIFY(clause)
  255. clause = getClauseSimplifier().simplify(clause);
  256. // * merge identical literals
  257. // Note: Not required as handled by Clause Implementation
  258. // which keeps literals within a Set, so no duplicates
  259. // will exist.
  260. // * discard clause if it is a tautology
  261. if (clause.isTautology()) {
  262. continue;
  263. }
  264. // * if clause has no literals then a refutation has been found
  265. // or if it just contains the answer literal.
  266. if (!ansHandler.isAnswer(clause)) {
  267. // * sos <- [clause | sos]
  268. // This check ensure duplicate clauses are not
  269. // introduced which will cause the
  270. // LightestClauseHeuristic to loop continuously
  271. // on the same pair of objects.
  272. if (!sos.contains(clause) && !usable.contains(clause)) {
  273. for (Clause ac : clause.getFactors()) {
  274. if (!sos.contains(ac) && !usable.contains(ac)) {
  275. idxdClauses.addClause(ac, sos, usable);
  276. // * if clause has one literal then look for unit
  277. // refutation
  278. lookForUnitRefutation(ansHandler, idxdClauses, ac,
  279. sos, usable);
  280. }
  281. }
  282. }
  283. }
  284. if (ansHandler.isComplete()) {
  285. break;
  286. }
  287. }
  288. }
  289. private void lookForUnitRefutation(OTTERAnswerHandler ansHandler,
  290. IndexedClauses idxdClauses, Clause clause, Set<Clause> sos,
  291. Set<Clause> usable) {
  292. Set<Clause> toCheck = new LinkedHashSet<Clause>();
  293. if (ansHandler.isCheckForUnitRefutation(clause)) {
  294. for (Clause s : sos) {
  295. if (s.isUnitClause()) {
  296. toCheck.add(s);
  297. }
  298. }
  299. for (Clause u : usable) {
  300. if (u.isUnitClause()) {
  301. toCheck.add(u);
  302. }
  303. }
  304. }
  305. if (toCheck.size() > 0) {
  306. toCheck = infer(clause, toCheck);
  307. for (Clause t : toCheck) {
  308. // * clause <- SIMPLIFY(clause)
  309. t = getClauseSimplifier().simplify(t);
  310. // * discard clause if it is a tautology
  311. if (t.isTautology()) {
  312. continue;
  313. }
  314. // * if clause has no literals then a refutation has been found
  315. // or if it just contains the answer literal.
  316. if (!ansHandler.isAnswer(t)) {
  317. // * sos <- [clause | sos]
  318. // This check ensure duplicate clauses are not
  319. // introduced which will cause the
  320. // LightestClauseHeuristic to loop continuously
  321. // on the same pair of objects.
  322. if (!sos.contains(t) && !usable.contains(t)) {
  323. idxdClauses.addClause(t, sos, usable);
  324. }
  325. }
  326. if (ansHandler.isComplete()) {
  327. break;
  328. }
  329. }
  330. }
  331. }
  332. // This is a simple indexing on the clauses to support
  333. // more efficient forward and backward subsumption testing.
  334. class IndexedClauses {
  335. private LightestClauseHeuristic lightestClauseHeuristic = null;
  336. // Group the clauses by their # of literals.
  337. private Map<Integer, Set<Clause>> clausesGroupedBySize = new HashMap<Integer, Set<Clause>>();
  338. // Keep track of the min and max # of literals.
  339. private int minNoLiterals = Integer.MAX_VALUE;
  340. private int maxNoLiterals = 0;
  341. public IndexedClauses(LightestClauseHeuristic lightestClauseHeuristic,
  342. Set<Clause> sos, Set<Clause> usable) {
  343. this.lightestClauseHeuristic = lightestClauseHeuristic;
  344. for (Clause c : sos) {
  345. indexClause(c);
  346. }
  347. for (Clause c : usable) {
  348. indexClause(c);
  349. }
  350. }
  351. public void addClause(Clause c, Set<Clause> sos, Set<Clause> usable) {
  352. // Perform forward subsumption elimination
  353. boolean addToSOS = true;
  354. for (int i = minNoLiterals; i < c.getNumberLiterals(); i++) {
  355. Set<Clause> fs = clausesGroupedBySize.get(i);
  356. if (null != fs) {
  357. for (Clause s : fs) {
  358. if (s.subsumes(c)) {
  359. addToSOS = false;
  360. break;
  361. }
  362. }
  363. }
  364. if (!addToSOS) {
  365. break;
  366. }
  367. }
  368. if (addToSOS) {
  369. sos.add(c);
  370. lightestClauseHeuristic.addedClauseToSOS(c);
  371. indexClause(c);
  372. // Have added clause, therefore
  373. // perform backward subsumption elimination
  374. Set<Clause> subsumed = new HashSet<Clause>();
  375. for (int i = c.getNumberLiterals() + 1; i <= maxNoLiterals; i++) {
  376. subsumed.clear();
  377. Set<Clause> bs = clausesGroupedBySize.get(i);
  378. if (null != bs) {
  379. for (Clause s : bs) {
  380. if (c.subsumes(s)) {
  381. subsumed.add(s);
  382. if (sos.contains(s)) {
  383. sos.remove(s);
  384. lightestClauseHeuristic
  385. .removedClauseFromSOS(s);
  386. }
  387. usable.remove(s);
  388. }
  389. }
  390. bs.removeAll(subsumed);
  391. }
  392. }
  393. }
  394. }
  395. //
  396. // PRIVATE METHODS
  397. //
  398. private void indexClause(Clause c) {
  399. int size = c.getNumberLiterals();
  400. if (size < minNoLiterals) {
  401. minNoLiterals = size;
  402. }
  403. if (size > maxNoLiterals) {
  404. maxNoLiterals = size;
  405. }
  406. Set<Clause> cforsize = clausesGroupedBySize.get(size);
  407. if (null == cforsize) {
  408. cforsize = new HashSet<Clause>();
  409. clausesGroupedBySize.put(size, cforsize);
  410. }
  411. cforsize.add(c);
  412. }
  413. }
  414. class OTTERAnswerHandler implements InferenceResult {
  415. private Literal answerLiteral = null;
  416. private Set<Variable> answerLiteralVariables = null;
  417. private Clause answerClause = null;
  418. private long finishTime = 0L;
  419. private boolean complete = false;
  420. private List<Proof> proofs = new ArrayList<Proof>();
  421. private boolean timedOut = false;
  422. public OTTERAnswerHandler(Literal answerLiteral,
  423. Set<Variable> answerLiteralVariables, Clause answerClause,
  424. long maxQueryTime) {
  425. this.answerLiteral = answerLiteral;
  426. this.answerLiteralVariables = answerLiteralVariables;
  427. this.answerClause = answerClause;
  428. //
  429. this.finishTime = System.currentTimeMillis() + maxQueryTime;
  430. }
  431. //
  432. // START-InferenceResult
  433. public boolean isPossiblyFalse() {
  434. return !timedOut && proofs.size() == 0;
  435. }
  436. public boolean isTrue() {
  437. return proofs.size() > 0;
  438. }
  439. public boolean isUnknownDueToTimeout() {
  440. return timedOut && proofs.size() == 0;
  441. }
  442. public boolean isPartialResultDueToTimeout() {
  443. return timedOut && proofs.size() > 0;
  444. }
  445. public List<Proof> getProofs() {
  446. return proofs;
  447. }
  448. // END-InferenceResult
  449. //
  450. public boolean isComplete() {
  451. return complete;
  452. }
  453. public boolean isLookingForAnswerLiteral() {
  454. return !answerClause.isEmpty();
  455. }
  456. public boolean isCheckForUnitRefutation(Clause clause) {
  457. if (isLookingForAnswerLiteral()) {
  458. if (2 == clause.getNumberLiterals()) {
  459. for (Literal t : clause.getLiterals()) {
  460. if (t.getAtomicSentence()
  461. .getSymbolicName()
  462. .equals(answerLiteral.getAtomicSentence()
  463. .getSymbolicName())) {
  464. return true;
  465. }
  466. }
  467. }
  468. } else {
  469. return clause.isUnitClause();
  470. }
  471. return false;
  472. }
  473. public boolean isAnswer(Clause clause) {
  474. boolean isAns = false;
  475. if (answerClause.isEmpty()) {
  476. if (clause.isEmpty()) {
  477. proofs.add(new ProofFinal(clause.getProofStep(),
  478. new HashMap<Variable, Term>()));
  479. complete = true;
  480. isAns = true;
  481. }
  482. } else {
  483. if (clause.isEmpty()) {
  484. // This should not happen
  485. // as added an answer literal to sos, which
  486. // implies the database (i.e. premises) are
  487. // unsatisfiable to begin with.
  488. throw new IllegalStateException(
  489. "Generated an empty clause while looking for an answer, implies original KB or usable is unsatisfiable");
  490. }
  491. if (clause.isUnitClause()
  492. && clause.isDefiniteClause()
  493. && clause
  494. .getPositiveLiterals()
  495. .get(0)
  496. .getAtomicSentence()
  497. .getSymbolicName()
  498. .equals(answerLiteral.getAtomicSentence()
  499. .getSymbolicName())) {
  500. Map<Variable, Term> answerBindings = new HashMap<Variable, Term>();
  501. List<Term> answerTerms = clause.getPositiveLiterals()
  502. .get(0).getAtomicSentence().getArgs();
  503. int idx = 0;
  504. for (Variable v : answerLiteralVariables) {
  505. answerBindings.put(v, answerTerms.get(idx));
  506. idx++;
  507. }
  508. boolean addNewAnswer = true;
  509. for (Proof p : proofs) {
  510. if (p.getAnswerBindings().equals(answerBindings)) {
  511. addNewAnswer = false;
  512. break;
  513. }
  514. }
  515. if (addNewAnswer) {
  516. proofs.add(new ProofFinal(clause.getProofStep(),
  517. answerBindings));
  518. }
  519. isAns = true;
  520. }
  521. }
  522. if (System.currentTimeMillis() > finishTime) {
  523. complete = true;
  524. // Indicate that I have run out of query time
  525. timedOut = true;
  526. }
  527. return isAns;
  528. }
  529. @Override
  530. public String toString() {
  531. StringBuilder sb = new StringBuilder();
  532. sb.append("isComplete=" + complete);
  533. sb.append("\n");
  534. sb.append("result=" + proofs);
  535. return sb.toString();
  536. }
  537. }
  538. }