/prop2cnf.rkt

http://github.com/ianj/Typed-Racket-SMT-type-checker · Racket · 190 lines · 137 code · 16 blank · 37 comment · 20 complexity · 4e308d02d43ab4068cf05347b48d562f MD5 · raw file

  1. #lang racket
  2. (provide prop->cnf bconn? hash-foldr single-ify)
  3. ; A Boolean connective (bconn) is
  4. (define (bconn? x) (memq x '(or and not implies)))
  5. ;A Prop is one of
  6. ; - `(,bconn ,Prop ...)
  7. ; - #f
  8. ; - #t
  9. ; - other ;;; interpreted
  10. ;A Literal is one of
  11. ; - Symbol
  12. ; - `(not Symbol)
  13. ;A Depth1Prop is one of
  14. ; - `(,bconn ,Literal ...)
  15. ; - #f
  16. ; - #t
  17. ; - other
  18. (define (uninterpreted-prop? x)
  19. (not (or (boolean? x)
  20. (and (pair? x)
  21. (bconn? (car x))))))
  22. (define (single-ify bconn base lst)
  23. (cond [(empty? lst) base]
  24. [(empty? (cdr lst)) (car lst)]
  25. [else (cons bconn lst)]))
  26. (define (remove-prop-booleans prop)
  27. (match prop
  28. [`(not ,prop*)
  29. (let ([rprop* (remove-prop-booleans prop*)])
  30. (if (boolean? rprop*)
  31. (not rprop*)
  32. `(not ,rprop*)))]
  33. [`(implies ,prop1 ,prop2)
  34. (let ([rprop1 (remove-prop-booleans prop1)])
  35. (cond [(eqv? rprop1 #t) ;; TT => P == P
  36. (remove-prop-booleans prop2)]
  37. [(false? rprop1) ;; FF => P == TT
  38. #t]
  39. [else
  40. (let ([rprop2 (remove-prop-booleans prop2)])
  41. (cond [(eqv? rprop2 #t);; P => TT == P
  42. rprop1]
  43. [(false? rprop2) ;; P => FF == (not P)
  44. `(not ,rprop1)]
  45. [else ;; general case
  46. `(implies ,rprop1 ,rprop2)]))]))]
  47. [`(and ,props ...)
  48. (let ([rprops (map remove-prop-booleans props)])
  49. (cond [(memq #f rprops)
  50. #f]
  51. [else (let ([rprops-no-t (remove* (list #t) rprops)])
  52. (or (empty? rprops-no-t)
  53. (single-ify 'and #t rprops-no-t)))]))]
  54. [`(or ,props ...)
  55. (let ([rprops (map remove-prop-booleans props)])
  56. (cond [(memq #t rprops)
  57. #t]
  58. [else (let ([rprops-no-f (remove* (list #f) rprops)])
  59. (and (pair? rprops-no-f)
  60. (single-ify 'or #f rprops-no-f)))]))]
  61. [other other]))
  62. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  63. ;; Utility functions
  64. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  65. (define (hash-foldr fn base hash)
  66. (let recur ((i (hash-iterate-first hash)))
  67. (if i
  68. (fn (hash-iterate-key hash i)
  69. (hash-iterate-value hash i)
  70. (recur (hash-iterate-next hash i)))
  71. base)))
  72. (define (hash-reduce fn acc base hash)
  73. (let recur ([itr (hash-iterate-first hash)])
  74. (if itr
  75. (acc (fn (hash-iterate-key hash itr)
  76. (hash-iterate-value hash itr))
  77. (recur (hash-iterate-next hash itr)))
  78. base)))
  79. (define (incbox! b) (set-box! b (add1 (unbox b))))
  80. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  81. ;; Tseitin transform functions
  82. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  83. ; shred : Prop * Box Natural * Hash<Prop,Natural> -> Literal
  84. ; where Env is a Hash<Depth1Prop to DimacsLit>
  85. ; We do an initial structural hashing in order to spot
  86. ; sharing.
  87. ; XXX: For finding more sharing, we should normalize associations
  88. ; and order literal by some total order.
  89. (define (shred prop varnum env)
  90. ;; shredrec : Prop -> Sym
  91. (let shredrec ([p prop])
  92. (match p
  93. [`(not ,p)
  94. (- (shredrec p))]
  95. [`(,(? bconn? op) ,ps ...)
  96. (let ([d1props (map shredrec ps)])
  97. (begin0 (hash-ref! env (cons op d1props) (unbox varnum))
  98. (incbox! varnum)))]
  99. [(? boolean? x)
  100. (error "[Internal error] Literal false or true found!")]
  101. [other ;; intepreted
  102. (begin0 (hash-ref! env p (unbox varnum))
  103. (incbox! varnum))])))
  104. ;; Tseitin transform depth1props to Dimacs format.
  105. ;; This means counting the number of clauses created.
  106. (define (wff-hash->cnf env num-clauses)
  107. ;; some shorthand for easy reading
  108. (define (implies p . q) (list* (- p) q))
  109. (define (implies* p q) (list* (- p) q))
  110. (define (or . ps) ps)
  111. (define (not p) (- p))
  112. (hash-reduce
  113. (lambda (prop dimacsvar)
  114. (match prop
  115. [`(not ,sym)
  116. (error "[Internal error] Naked NOT should not survive shredding.")]
  117. [`(implies ,p ,q)
  118. (set-box! num-clauses (+ 3 (unbox num-clauses)))
  119. (list (implies dimacsvar (not p) q)
  120. (implies q dimacsvar)
  121. ;; implication not true => p had to be true.
  122. (or dimacsvar p))]
  123. [`(and ,ps ...)
  124. ;; and gate is true iff all of ps are true
  125. (let ([andlits (map - ps)]
  126. [impclauses (map (lambda (p)
  127. (incbox! num-clauses)
  128. (implies dimacsvar p))
  129. ps)])
  130. (incbox! num-clauses)
  131. (cons (cons dimacsvar andlits) ; ~andgate => (not (and ,@ps))
  132. impclauses))] ; for all p in ps, andgate => p
  133. [`(or ,ps ...)
  134. ;; or gate is true iff one of ps is true.
  135. (let ([impclauses (map (lambda (p)
  136. (incbox! num-clauses)
  137. (implies p dimacsvar))
  138. ps)])
  139. (incbox! num-clauses)
  140. (list* (implies* dimacsvar ps) ; orgate => p1 or p2 or ...
  141. impclauses))] ; for all p in ps, p => orgate
  142. [other ;; ground proposition. Not in clause.
  143. '()]))
  144. append '() env))
  145. ; prop->cnf : Prop -> CNF * T-State
  146. (define (prop->cnf initialize-t-state prop)
  147. (let ((simp-prop (remove-prop-booleans prop)))
  148. (printf "Simplified: ~a~%" simp-prop)
  149. (match simp-prop
  150. [#t (values #t (initialize-t-state (make-hash)))]
  151. [#f (values #f (initialize-t-state (make-hash)))]
  152. [non-trivial
  153. (let* ([env (make-hash)]
  154. [total-vars (box 1)] ;; start variables at 1 since 0 can't be negated
  155. [total-clauses (box 0)] ;; start with 0 clauses and count upwards
  156. [top-sym (shred non-trivial total-vars env)]
  157. [cnf (wff-hash->cnf env total-clauses)]
  158. [atomic-propositions
  159. ;; collect all theory literals in a dictionary keyed
  160. ;; by their propositional variable.
  161. (make-immutable-hash
  162. (hash-foldr
  163. (Îť (prop dimacs-lit atomic-assoc)
  164. ;; dimacs-lit should always be positive for an uninterpreted prop
  165. (if (uninterpreted-prop? prop)
  166. (dict-set atomic-assoc prop dimacs-lit)
  167. atomic-assoc))
  168. '()
  169. env))])
  170. (values (list (sub1 (unbox total-vars)) ; one too many due to needing fresh names
  171. (add1 (unbox total-clauses)) ; add 1 for top level assertion
  172. (cons (list top-sym) ; assert the top level
  173. cnf))
  174. (initialize-t-state atomic-propositions)))])))