Mercurial > lasercutter
view src/clojure/contrib/datalog/rules.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
line wrap: on
line source
1 ;; Copyright (c) Jeffrey Straszheim. All rights reserved. The use and2 ;; distribution terms for this software are covered by the Eclipse Public3 ;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can4 ;; be found in the file epl-v10.html at the root of this distribution. By5 ;; using this software in any fashion, you are agreeing to be bound by the6 ;; terms of this license. You must not remove this notice, or any other,7 ;; from this software.8 ;;9 ;; rules.clj10 ;;11 ;; A Clojure implementation of Datalog -- Rules Engine12 ;;13 ;; straszheimjeffrey (gmail)14 ;; Created 2 Feburary 200917 (ns clojure.contrib.datalog.rules18 (:use clojure.contrib.datalog.util)19 (:use clojure.contrib.datalog.literals20 clojure.contrib.datalog.database)21 (:use [clojure.set :only (union intersection difference)])22 (:use [clojure.contrib.set :only (subset?)])23 (:use [clojure.contrib.except :only (throwf)])24 (:import java.io.Writer))27 (defstruct datalog-rule28 :head29 :body)31 (defn display-rule32 "Return the rule in a readable format."33 [rule]34 (list* '<-35 (-> rule :head display-literal)36 (map display-literal (:body rule))))38 (defn display-query39 "Return a query in a readable format."40 [query]41 (list* '?- (display-literal query)))44 ;;; Check rule safety46 (defn is-safe?47 "Is the rule safe according to the datalog protocol?"48 [rule]49 (let [hv (literal-vars (:head rule))50 bpv (apply union (map positive-vars (:body rule)))51 bnv (apply union (map negative-vars (:body rule)))52 ehv (difference hv bpv)53 env (difference bnv bpv)]54 (when-not (empty? ehv)55 (throwf "Head vars %s not bound in body in rule %s" ehv rule))56 (when-not (empty? env)57 (throwf "Body vars %s not bound in negative positions in rule %s" env rule))58 rule))61 ;;; Rule creation and printing63 (defn build-rule64 [hd bd]65 (with-meta (struct datalog-rule hd bd) {:type ::datalog-rule}))67 (defmacro <-68 "Build a datalog rule. Like this:70 (<- (:head :x ?x :y ?y) (:body-1 :x ?x :y ?y) (:body-2 :z ?z) (not! :body-3 :x ?x) (if > ?y ?z))"71 [hd & body]72 (let [head (build-atom hd :clojure.contrib.datalog.literals/literal)73 body (map build-literal body)]74 `(is-safe? (build-rule ~head [~@body]))))76 (defmethod print-method ::datalog-rule77 [rule ^Writer writer]78 (print-method (display-rule rule) writer))80 (defn return-rule-data81 "Returns an untypted rule that will be fully printed"82 [rule]83 (with-meta rule {}))85 (defmacro ?-86 "Define a datalog query"87 [& q]88 (let [qq (build-atom q :clojure.contrib.datalog.literals/literal)]89 `(with-meta ~qq {:type ::datalog-query})))91 (defmethod print-method ::datalog-query92 [query ^Writer writer]93 (print-method (display-query query) writer))97 ;;; SIP99 (defn compute-sip100 "Given a set of bound column names, return an adorned sip for this101 rule. A set of intensional predicates should be provided to102 determine what should be adorned."103 [bindings i-preds rule]104 (let [next-lit (fn [bv body]105 (or (first (drop-while106 #(not (literal-appropriate? bv %))107 body))108 (first (drop-while (complement positive?) body))))109 adorn (fn [lit bvs]110 (if (i-preds (literal-predicate lit))111 (let [bnds (union (get-cs-from-vs lit bvs)112 (get-self-bound-cs lit))]113 (adorned-literal lit bnds))114 lit))115 new-h (adorned-literal (:head rule) bindings)]116 (loop [bound-vars (get-vs-from-cs (:head rule) bindings)117 body (:body rule)118 sip []]119 (if-let [next (next-lit bound-vars body)]120 (recur (union bound-vars (literal-vars next))121 (remove #(= % next) body)122 (conj sip (adorn next bound-vars)))123 (build-rule new-h (concat sip body))))))126 ;;; Rule sets128 (defn make-rules-set129 "Given an existing set of rules, make it a 'rules-set' for130 printing."131 [rs]132 (with-meta rs {:type ::datalog-rules-set}))134 (def empty-rules-set (make-rules-set #{}))136 (defn rules-set137 "Given a collection of rules return a rules set"138 [& rules]139 (reduce conj empty-rules-set rules))141 (defmethod print-method ::datalog-rules-set142 [rules ^Writer writer]143 (binding [*out* writer]144 (do145 (print "(rules-set")146 (doseq [rule rules]147 (println)148 (print " ")149 (print rule))150 (println ")"))))152 (defn predicate-map153 "Given a rules-set, return a map of rules keyed by their predicates.154 Each value will be a set of rules."155 [rs]156 (let [add-rule (fn [m r]157 (let [pred (-> r :head literal-predicate)158 os (get m pred #{})]159 (assoc m pred (conj os r))))]160 (reduce add-rule {} rs)))162 (defn all-predicates163 "Given a rules-set, return all defined predicates"164 [rs]165 (set (map literal-predicate (map :head rs))))167 (defn non-base-rules168 "Return a collection of rules that depend, somehow, on other rules"169 [rs]170 (let [pred (all-predicates rs)171 non-base (fn [r]172 (if (some #(pred %)173 (map literal-predicate (:body r)))174 r175 nil))]176 (remove nil? (map non-base rs))))179 ;;; Database operations181 (def empty-bindings [{}])183 (defn apply-rule184 "Apply the rule against db-1, adding the results to the appropriate185 relation in db-2. The relation will be created if needed."186 ([db rule] (apply-rule db db rule))187 ([db-1 db-2 rule]188 (trace-datalog (println)189 (println)190 (println "--------------- Begin Rule ---------------")191 (println rule))192 (let [head (:head rule)193 body (:body rule)194 step (fn [bs lit]195 (trace-datalog (println bs)196 (println lit))197 (join-literal db-1 lit bs))198 bs (reduce step empty-bindings body)]199 (do (trace-datalog (println bs))200 (project-literal db-2 head bs)))))202 (defn apply-rules-set203 [db rs]204 (reduce (fn [rdb rule]205 (apply-rule db rdb rule)) db rs))208 ;; End of file