This repository has been archived by the owner on May 5, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnormal.lisp
176 lines (139 loc) · 6.11 KB
/
normal.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
;;; -*- mode:lisp; coding:utf-8 -*-
;;; This file is part of the aimacode from
;;; https://github.com/aimacode/aima-lisp
;;; Copyright (c) 2016 aimacode
;;; Some modify for JSCL and MOREN environment
;;; Copyright © 2017,2018 Vladimir Mezentsev
;;;; Convert Expressions to Normal Form (Conjunctive, Implicative or Horn)
;;; This could be done much more efficiently by using a special
;;; representation for CNF, which eliminates the explicit ANDs
;;; and ORs. This code is meant to be informative, not efficient.
;;;; Top-Level Functions
(defun ->cnf (p &optional vars)
"Convert a sentence p to conjunctive normal form [p 279-280]."
;; That is, return (and (or ...) ...) where
;; each of the conjuncts has all literal disjuncts.
;; VARS is a list of universally quantified variables that P is in scope of.
(setf p (eliminate-implications (logic p)))
(case (op p)
(NOT (let ((p2 (move-not-inwards (arg1 p))))
(if (literal-clause? p2) p2 (->cnf p2 vars))))
(AND (conjunction (mappend (lambda (q) (conjuncts (->cnf q vars)))
(args p))))
(OR (merge-disjuncts (mapcar (lambda (q) (->cnf q vars))
(args p))))
(t p) ; p is atomic
))
(defun ->inf (p)
"Convert a sentence p to implicative normal form [p 282]."
(conjunction (mapcar #'cnf1->inf1 (conjuncts (->cnf p)))))
(defun ->horn (p)
"Try to convert sentence to a Horn clause, or a conjunction of Horn clauses.
Signal an error if this cannot be done."
(let ((q (->inf p)))
(when (not (every #'horn-clause? (conjuncts q)))
(error "~A, converted to ~A, is not a Horn clause." p q))
q))
(defun logic (sentence)
"Canonicalize a sentence not implemented"
(cond ((stringp sentence) (error "Canonicalize isnt release ~a" sentence))
(t sentence)))
;;;; Auxiliary Functions
(defun cnf1->inf1 (p)
;; P is of the form (or (not a) (not b) ... c d ...)
;; Convert to: (=> (and a b ...) (or c d ...))
;; where a,b,c,d ... are positive atomic clauses
(let ((lhs (mapcar #'arg1 (remove-if-not #'negative-clause? (disjuncts p))))
(rhs (remove-if #'negative-clause? (disjuncts p))))
`(=> ,(conjunction lhs) ,(disjunction rhs))))
(defun eliminate-implications (p)
(if (literal-clause? p)
p
(case (intern (symbol-name (op p)) :horn)
(=> `(or ,(arg2 p) (not ,(arg1 p))))
(<=> `(and (or ,(arg1 p) (not ,(arg2 p)))
(or (not ,(arg1 p)) ,(arg2 p))))
(t
(cons (op p) (mapcar #'eliminate-implications (args p)))))))
(defun move-not-inwards (p)
"Given P, return ~P, but with the negation moved as far in as possible."
(case (intern (symbol-name (op p)) :horn)
(TRUE 'false)
(FALSE 'true)
(NOT (arg1 p))
(AND (disjunction (mapcar #'move-not-inwards (args p))))
(OR (conjunction (mapcar #'move-not-inwards (args p))))
(t (make-exp 'not p))))
(defun merge-disjuncts (disjuncts)
"Return a CNF expression for the disjunction."
;; The argument is a list of disjuncts, each in CNF.
;; The second argument is a list of conjuncts built so far.
(case (length disjuncts)
(0 'false)
(1 (first disjuncts))
(t (conjunction
(let ((result nil))
(dolist (y (conjuncts (merge-disjuncts (rest disjuncts))))
(dolist (x (conjuncts (first disjuncts)))
(push (disjunction (append (disjuncts x) (disjuncts y)))
result)))
(nreverse result))))))
(defun renaming? (p q &optional (bindings +no-bindings+))
"Are p and q renamings of each other? (That is, expressions that differ
only in variable names?)"
(cond ((eql bindings +fail+) +fail+)
((equal p q) bindings)
((and (consp p) (consp q))
(renaming? (rest p) (rest q)
(renaming? (first p) (first q) bindings)))
((not (and (variable? p) (variable? q)))
+fail+)
;; P and Q are both variables from here on
((and (not (get-binding p bindings)) (not (get-binding q bindings)))
(extend-bindings p q bindings))
((or (eq (lookup p bindings) q) (eq p (lookup q bindings)))
bindings)
(t +fail+)))
;;;; Utility Predicates and Accessors
(defconstant +logical-connectives+ '(and or not => <=>))
(defun atomic-clause? (sentence)
"An atomic clause has no connectives or quantifiers."
(not (member (intern (symbol-name (op sentence)) :horn) +logical-connectives+)))
(defun literal-clause? (sentence)
"A literal is an atomic clause or a negated atomic clause."
(or (atomic-clause? sentence)
(and (negative-clause? sentence) (atomic-clause? (arg1 sentence)))))
(defun negative-clause? (sentence)
"A negative clause has NOT as the operator."
(eq (intern (symbol-name (op sentence)) :horn) 'not))
(defun horn-clause? (sentence)
"A Horn clause (in INF) is an implication with atoms on the left and one
atom on the right."
(and (eq (intern (symbol-name (op sentence)) :horn) '=>)
(every #'atomic-clause? (conjuncts (arg1 sentence)))
(atomic-clause? (arg2 sentence))))
(defun conjuncts (sentence)
"Return a list of the conjuncts in this sentence."
(cond ((eq (intern (symbol-name (op sentence)) :horn) 'and) (args sentence))
((symbolp sentence)
(if (eq (intern (symbol-name sentence) :horn) 'true) nil))
(t (list sentence))))
(defun disjuncts (sentence)
"Return a list of the disjuncts in this sentence."
(cond ((eq (intern (symbol-name (op sentence)) :horn) 'or) (args sentence))
((symbolp sentence)
(eq (intern (symbol-name sentence) :horn) 'false) nil)
(t (list sentence))))
(defun conjunction (args)
"Form a conjunction with these args."
(case (length args)
(0 'true)
(1 (first args))
(t (cons 'and args))))
(defun disjunction (args)
"Form a disjunction with these args."
(case (length args)
(0 'false)
(1 (first args))
(t (cons 'or args))))
;;; EOF