Mercurial > lasercutter
view src/clojure/contrib/datalog/literals.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 ;; literals.clj10 ;;11 ;; A Clojure implementation of Datalog -- Literals12 ;;13 ;; straszheimjeffrey (gmail)14 ;; Created 25 Feburary 200917 (ns clojure.contrib.datalog.literals18 (:use clojure.contrib.datalog.util)19 (:use clojure.contrib.datalog.database)20 (:use [clojure.set :only (intersection)])21 (:use [clojure.contrib.set :only (subset?)]))24 ;;; Type Definitions26 (defstruct atomic-literal27 :predicate ; The predicate name28 :term-bindings ; A map of column names to bindings29 :literal-type) ; ::literal or ::negated31 (derive ::negated ::literal)33 (defstruct conditional-literal34 :fun ; The fun to call35 :symbol ; The fun symbol (for display)36 :terms ; The formal arguments37 :literal-type) ; ::conditional40 ;;; Basics43 (defmulti literal-predicate44 "Return the predicate/relation this conditional operates over"45 :literal-type)47 (defmulti literal-columns48 "Return the column names this applies to"49 :literal-type)51 (defmulti literal-vars52 "Returns the logic vars used by this literal"53 :literal-type)55 (defmulti positive-vars56 "Returns the logic vars used in a positive position"57 :literal-type)59 (defmulti negative-vars60 "Returns the logic vars used in a negative position"61 :literal-type)63 (defmethod literal-predicate ::literal64 [l]65 (:predicate l))67 (defmethod literal-predicate ::conditional68 [l]69 nil)71 (defmethod literal-columns ::literal72 [l]73 (-> l :term-bindings keys set))75 (defmethod literal-columns ::conditional76 [l]77 nil)79 (defmethod literal-vars ::literal80 [l]81 (set (filter is-var? (-> l :term-bindings vals))))83 (defmethod literal-vars ::conditional84 [l]85 (set (filter is-var? (:terms l))))87 (defmethod positive-vars ::literal88 [l]89 (literal-vars l))91 (defmethod positive-vars ::negated92 [l]93 nil)95 (defmethod positive-vars ::conditional96 [l]97 nil)99 (defmethod negative-vars ::literal100 [l]101 nil)103 (defmethod negative-vars ::negated104 [l]105 (literal-vars l))107 (defmethod negative-vars ::conditional108 [l]109 (literal-vars l))111 (defn negated?112 "Is this literal a negated literal?"113 [l]114 (= (:literal-type l) ::negated))116 (defn positive?117 "Is this a positive literal?"118 [l]119 (= (:literal-type l) ::literal))122 ;;; Building Literals124 (def negation-symbol 'not!)125 (def conditional-symbol 'if)127 (defmulti build-literal128 "(Returns an unevaluated expression (to be used in macros) of a129 literal."130 first)132 (defn build-atom133 "Returns an unevaluated expression (to be used in a macro) of an134 atom."135 [f type]136 (let [p (first f)137 ts (map #(if (is-var? %) `(quote ~%) %) (next f))138 b (if (seq ts) (apply assoc {} ts) nil)]139 `(struct atomic-literal ~p ~b ~type)))141 (defmethod build-literal :default142 [f]143 (build-atom f ::literal))145 (defmethod build-literal negation-symbol146 [f]147 (build-atom (rest f) ::negated))149 (defmethod build-literal conditional-symbol150 [f]151 (let [symbol (fnext f)152 terms (nnext f)153 fun `(fn [binds#] (apply ~symbol binds#))]154 `(struct conditional-literal155 ~fun156 '~symbol157 '~terms158 ::conditional)))161 ;;; Display163 (defmulti display-literal164 "Converts a struct representing a literal to a normal list"165 :literal-type)167 (defn- display168 [l]169 (conj (-> l :term-bindings list* flatten) (literal-predicate l)))171 (defmethod display-literal ::literal172 [l]173 (display l))175 (defmethod display-literal ::negated176 [l]177 (conj (display l) negation-symbol))179 (defmethod display-literal ::conditional180 [l]181 (list* conditional-symbol (:symbol l) (:terms l)))184 ;;; Sip computation186 (defmulti get-vs-from-cs187 "From a set of columns, return the vars"188 :literal-type)190 (defmethod get-vs-from-cs ::literal191 [l bound]192 (set (filter is-var?193 (vals (select-keys (:term-bindings l)194 bound)))))196 (defmethod get-vs-from-cs ::conditional197 [l bound]198 nil)201 (defmulti get-cs-from-vs202 "From a set of vars, get the columns"203 :literal-type)205 (defmethod get-cs-from-vs ::literal206 [l bound]207 (reduce conj208 #{}209 (remove nil?210 (map (fn [[k v]] (if (bound v) k nil))211 (:term-bindings l)))))213 (defmethod get-cs-from-vs ::conditional214 [l bound]215 nil)218 (defmulti get-self-bound-cs219 "Get the columns that are bound withing the literal."220 :literal-type)222 (defmethod get-self-bound-cs ::literal223 [l]224 (reduce conj225 #{}226 (remove nil?227 (map (fn [[k v]] (if (not (is-var? v)) k nil))228 (:term-bindings l)))))230 (defmethod get-self-bound-cs ::conditional231 [l]232 nil)235 (defmulti literal-appropriate?236 "When passed a set of bound vars, determines if this literal can be237 used during this point of a SIP computation."238 (fn [b l] (:literal-type l)))240 (defmethod literal-appropriate? ::literal241 [bound l]242 (not (empty? (intersection (literal-vars l) bound))))244 (defmethod literal-appropriate? ::negated245 [bound l]246 (subset? (literal-vars l) bound))248 (defmethod literal-appropriate? ::conditional249 [bound l]250 (subset? (literal-vars l) bound))253 (defmulti adorned-literal254 "When passed a set of bound columns, returns the adorned literal"255 (fn [l b] (:literal-type l)))257 (defmethod adorned-literal ::literal258 [l bound]259 (let [pred (literal-predicate l)260 bnds (intersection (literal-columns l) bound)]261 (if (empty? bound)262 l263 (assoc l :predicate {:pred pred :bound bnds}))))265 (defmethod adorned-literal ::conditional266 [l bound]267 l)270 (defn get-adorned-bindings271 "Get the bindings from this adorned literal."272 [pred]273 (:bound pred))275 (defn get-base-predicate276 "Get the base predicate from this predicate."277 [pred]278 (if (map? pred)279 (:pred pred)280 pred))283 ;;; Magic Stuff285 (defn magic-literal286 "Create a magic version of this adorned predicate."287 [l]288 (assert (-> l :literal-type (isa? ::literal)))289 (let [pred (literal-predicate l)290 pred-map (if (map? pred) pred {:pred pred})291 bound (get-adorned-bindings pred)292 ntb (select-keys (:term-bindings l) bound)]293 (assoc l :predicate (assoc pred-map :magic true) :term-bindings ntb :literal-type ::literal)))295 (defn literal-magic?296 "Is this literal magic?"297 [lit]298 (let [pred (literal-predicate lit)]299 (when (map? pred)300 (:magic pred))))302 (defn build-seed-bindings303 "Given a seed literal, already adorned and in magic form, convert304 its bound constants to new variables."305 [s]306 (assert (-> s :literal-type (isa? ::literal)))307 (let [ntbs (map-values (fn [_] (gensym '?_gen_)) (:term-bindings s))]308 (assoc s :term-bindings ntbs)))311 ;;; Semi-naive support313 (defn negated-literal314 "Given a literal l, return a negated version"315 [l]316 (assert (-> l :literal-type (= ::literal)))317 (assoc l :literal-type ::negated))319 (defn delta-literal320 "Given a literal l, return a delta version"321 [l]322 (let [pred* (:predicate l)323 pred (if (map? pred*) pred* {:pred pred*})]324 (assoc l :predicate (assoc pred :delta true))))327 ;;; Database operations329 (defn- build-partial-tuple330 [lit binds]331 (let [tbs (:term-bindings lit)332 each (fn [[key val :as pair]]333 (if (is-var? val)334 (if-let [n (binds val)]335 [key n]336 nil)337 pair))]338 (into {} (remove nil? (map each tbs)))))340 (defn- project-onto-literal341 "Given a literal, and a materialized tuple, return a set of variable342 bindings."343 [lit tuple]344 (let [step (fn [binds [key val]]345 (if (and (is-var? val)346 (contains? tuple key))347 (assoc binds val (tuple key))348 binds))]349 (reduce step {} (:term-bindings lit))))352 (defn- join-literal*353 [db lit bs fun]354 (let [each (fn [binds]355 (let [pt (build-partial-tuple lit binds)]356 (fun binds pt)))]357 (when (contains? db (literal-predicate lit))358 (apply concat (map each bs)))))360 (defmulti join-literal361 "Given a database (db), a literal (lit) and a seq of bindings (bs),362 return a new seq of bindings by joining this literal."363 (fn [db lit bs] (:literal-type lit)))365 (defmethod join-literal ::literal366 [db lit bs]367 (join-literal* db lit bs (fn [binds pt]368 (map #(merge binds %)369 (map (partial project-onto-literal lit)370 (select db (literal-predicate lit) pt))))))372 (defmethod join-literal ::negated373 [db lit bs]374 (join-literal* db lit bs (fn [binds pt]375 (if (any-match? db (literal-predicate lit) pt)376 nil377 [binds]))))379 (defmethod join-literal ::conditional380 [db lit bs]381 (let [each (fn [binds]382 (let [resolve (fn [term]383 (if (is-var? term)384 (binds term)385 term))386 args (map resolve (:terms lit))]387 (if ((:fun lit) args)388 binds389 nil)))]390 (remove nil? (map each bs))))392 (defn project-literal393 "Project a stream of bindings onto a literal/relation. Returns a new394 db."395 ([db lit bs] (project-literal db lit bs is-var?))396 ([db lit bs var?]397 (assert (= (:literal-type lit) ::literal))398 (let [rel-name (literal-predicate lit)399 columns (-> lit :term-bindings keys)400 idxs (vec (get-adorned-bindings (literal-predicate lit)))401 db1 (ensure-relation db rel-name columns idxs)402 rel (get-relation db1 rel-name)403 step (fn [rel bindings]404 (let [step (fn [t [k v]]405 (if (var? v)406 (assoc t k (bindings v))407 (assoc t k v)))408 tuple (reduce step {} (:term-bindings lit))]409 (add-tuple rel tuple)))]410 (replace-relation db rel-name (reduce step rel bs)))))413 ;; End of file