;;; This was meant to be an easy question. The loops are ;;; straightforward. The core AI point has to do with ;;; implementing "fact1 contradicts fact2," which most ;;; people did poorly on. ;;; The simplest approach I think is to generate new facts by ;;; replacing the old topic with the input topic, and ;;; remove those already in the input or clearly ;;; contradicted. (defun generate-hypotheses (input case) (remove-if #'(lambda (fact) (or (case-contains-p input fact) (case-contradicts-p input fact))) (subst (case-topic input) (case-topic case) (case-facts case)))) (defun case-contains-p (case fact) (member fact (case-facts case) :test #'equal)) (defun case-contradicts-p (case fact) (member fact (case-facts case) :test #'fact-contradicts-p)) ;;; This is the core AI point: When does a fact contradict ;;; another? Too many people assumed that facts are always ;;; binary predicates, that the first argument -- or any ;;; argument for that matter -- had to be the topic, or ;;; that having the same predicate was enough to recognize ;;; an obvious contradiction. ;;; ;;; Clearly, there is no obvious contradiction if the ;;; predicates differ. Almost as clearly, there is no ;;; obvious contradiction if more than one argument differs. ;;; That leaves the case of one differing argument. ;;; Here the answer is not simple. Consider: ;;; ;;; deathtoll(evt1, 10) contradicts deathtoll(evt1, 20) ;;; ;;; but ;;; ;;; deathtoll(evt1, 10) doesn't contradict deathtoll(evt2, 10) ;;; ;;; after(evt1, evt2) doesn't contradict after(evt1, evt3) ;;; ;;; To know when one differing argument is a contradiction, you ;;; have to know which arguments are determined by the other arguments. ;;; The second argument of deathtoll is determined by the other arguments. ;;; No argument of after is determined. If sum(a, b, c) means ;;; "the sum of a + b = c" then all arguments are determined if the ;;; other arguments are known. ;;; ;;; This knowledge can be captured in pure logic, but this can be expensive to ;;; compute on the fly. For simplicity and speed, we can write for each predicate what ;;; arguments are determined when the other arguments are known. ;;; ;;; The examples imply that the second argument of deathtoll, region and isa ;;; are determined. The last item implies an atypical use of isa. ;;; (defparameter *determined* ;; one-based argument indices '((deathtoll 2) (isa 2) (region 2)) ) (defun fact-contradicts-p (fact1 fact2) (and (eql (car fact1) (car fact2)) (let ((indices (get-difference-indices fact1 fact2))) (and (= (length indices) 1) (member (car indices) (cdr (assoc (car fact1) *determined*))))))) (defun get-difference-indices (l1 l2) (loop for i from 0 for x in l1 and y in l2 unless (eql x y) collect i)) (defun case-topic (case) (car case)) (defun case-facts (case) (cdr case)) ;;; Sample stories given in the question (defparameter s1 '(evt1 (region evt1 madrid) (isa evt1 bombing) (deathtoll evt1 190) (injurycount evt1 1240) (target evt1 train))) (defparameter s2 '(evt2 (deathtoll evt2 5) (isa evt2 explosion) (region evt2 baghdad) (target evt2 market))) ;;; Sample input case (defparameter s30 '(evt30 (isa evt30 bombing) (region evt30 tikrit)))