Mercurial > dylan
view sicm/utils.clj @ 2:b4de894a1e2e
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 28 Oct 2011 00:03:05 -0700 |
parents | |
children |
line wrap: on
line source
2 (ns sicm.utils)4 (in-ns 'sicm.utils)6 ;; Let some objects have spin8 (defprotocol Spinning9 (up? [this])10 (down? [this]))12 (defn spin13 "Returns the spin of the Spinning s, either :up or :down"14 [#^Spinning s]15 (cond (up? s) :up (down? s) :down))18 ;; DEFINITION: A tuple is a sequence with spin20 (deftype Tuple21 [spin coll]23 clojure.lang.Seqable24 (seq [this] (seq (.coll this)))26 clojure.lang.Counted27 (count [this] (count (.coll this)))29 Spinning30 (up? [this] (= ::up (.spin this)))31 (down? [this] (= ::down (.spin this))))33 (defmethod print-method Tuple34 [o w]35 (print-simple36 (if (up? o)37 (str "u" (.coll o))38 (str "d" (vec(.coll o))))39 w))41 (def tuple? #(= (type %) Tuple))43 ;; CONSTRUCTORS45 (defn up46 "Create a new up-tuple containing the contents of coll."47 [coll]48 (Tuple. ::up coll))50 (defn down51 "Create a new down-tuple containing the contents of coll."52 [coll]53 (Tuple. ::down coll))55 (defn same-spin56 "Creates a tuple which has the same spin as tuple and which contains57 the contents of coll."58 [tuple coll]59 (if (up? tuple)60 (up coll)61 (down coll)))63 (defn opposite-spin64 "Create a tuple which has opposite spin to tuple and which contains65 the contents of coll."66 [tuple coll]67 (if (up? tuple)68 (down coll)69 (up coll)))70 (in-ns 'sicm.utils)71 (require 'incanter.core) ;; use incanter's fast matrices74 (defn all-equal? [coll]75 (if (empty? (rest coll)) true76 (and (= (first coll) (second coll))77 (recur (rest coll)))))80 (defprotocol Matrix81 (rows [matrix])82 (cols [matrix])83 (diagonal [matrix])84 (trace [matrix])85 (determinant [matrix])86 (transpose [matrix])87 (conjugate [matrix])88 )90 (extend-protocol Matrix incanter.Matrix91 (rows [rs] (map down (apply map vector (apply map vector rs))))92 (cols [rs] (map up (apply map vector rs)))93 (diagonal [matrix] (incanter.core/diag matrix) )94 (determinant [matrix] (incanter.core/det matrix))95 (trace [matrix] (incanter.core/trace matrix))96 (transpose [matrix] (incanter.core/trans matrix)))98 (defn count-rows [matrix]99 ((comp count rows) matrix))101 (defn count-cols [matrix]102 ((comp count cols) matrix))104 (defn square? [matrix]105 (= (count-rows matrix) (count-cols matrix)))107 (defn identity-matrix108 "Define a square matrix of size n-by-n with 1s along the diagonal and109 0s everywhere else."110 [n]111 (incanter.core/identity-matrix n))114 (defn matrix-by-rows115 "Define a matrix by giving its rows."116 [& rows]117 (if118 (not (all-equal? (map count rows)))119 (throw (Exception. "All rows in a matrix must have the same number of elements."))120 (incanter.core/matrix (vec rows))))122 (defn matrix-by-cols123 "Define a matrix by giving its columns"124 [& cols]125 (if (not (all-equal? (map count cols)))126 (throw (Exception. "All columns in a matrix must have the same number of elements."))127 (incanter.core/matrix (vec (apply map vector cols)))))129 (defn identity-matrix130 "Define a square matrix of size n-by-n with 1s along the diagonal and131 0s everywhere else."132 [n]133 (incanter.core/identity-matrix n))135 (in-ns 'sicm.utils)136 (use 'clojure.contrib.generic.arithmetic137 'clojure.contrib.generic.collection138 'clojure.contrib.generic.functor139 'clojure.contrib.generic.math-functions)141 (defn numbers?142 "Returns true if all arguments are numbers, else false."143 [& xs]144 (every? number? xs))146 (defn tuple-surgery147 "Applies the function f to the items of tuple and the additional148 arguments, if any. Returns a Tuple of the same type as tuple."149 [tuple f & xs]150 ((if (up? tuple) up down)151 (apply f (seq tuple) xs)))155 ;;; CONTRACTION collapses two compatible tuples into a number.157 (defn contractible?158 "Returns true if the tuples a and b are compatible for contraction,159 else false. Tuples are compatible if they have the same number of160 components, they have opposite spins, and their elements are161 pairwise-compatible."162 [a b]163 (and164 (isa? (type a) Tuple)165 (isa? (type b) Tuple)166 (= (count a) (count b))167 (not= (spin a) (spin b))169 (not-any? false?170 (map #(or171 (numbers? %1 %2)172 (contractible? %1 %2))173 a b))))175 (defn contract176 "Contracts two tuples, returning the sum of the177 products of the corresponding items. Contraction is recursive on178 nested tuples."179 [a b]180 (if (not (contractible? a b))181 (throw182 (Exception. "Not compatible for contraction."))183 (reduce +184 (map185 (fn [x y]186 (if (numbers? x y)187 (* x y)188 (contract x y)))189 a b))))195 (defmethod conj Tuple196 [tuple & xs]197 (tuple-surgery tuple #(apply conj % xs)))199 (defmethod fmap Tuple200 [f tuple]201 (tuple-surgery tuple (partial map f)))205 ;; TODO: define Scalar, and add it to the hierarchy above Number and Complex208 (defmethod * [Tuple Tuple] ; tuple*tuple209 [a b]210 (if (contractible? a b)211 (contract a b)212 (map (partial * a) b)))215 (defmethod * [java.lang.Number Tuple] ;; scalar * tuple216 [a x] (fmap (partial * a) x))218 (defmethod * [Tuple java.lang.Number]219 [x a] (* a x))221 (defmethod * [java.lang.Number incanter.Matrix] ;; scalar * matrix222 [x M] (incanter.core/mult x M))224 (defmethod * [incanter.Matrix java.lang.Number]225 [M x] (* x M))227 (defmethod * [incanter.Matrix incanter.Matrix] ;; matrix * matrix228 [M1 M2]229 (incanter.core/mmult M1 M2))231 (defmethod * [incanter.Matrix Tuple] ;; matrix * tuple232 [M v]233 (if (and (apply numbers? v) (up? v))234 (* M (matrix-by-cols v))235 (throw (Exception. "Currently, you can only multiply a matrix by a tuple of *numbers*"))236 ))238 (defmethod * [Tuple incanter.Matrix] ;; tuple * Matrix239 [v M]240 (if (and (apply numbers? v) (down? v))241 (* (matrix-by-rows v) M)242 (throw (Exception. "Currently, you can only multiply a matrix by a tuple of *numbers*"))243 ))246 (defmethod exp incanter.Matrix247 [M]248 (incanter.core/exp M))251 (in-ns 'sicm.utils)252 (use 'clojure.contrib.seq253 'clojure.contrib.generic.arithmetic254 'clojure.contrib.generic.collection255 'clojure.contrib.generic.math-functions)257 ;;∂259 ;; DEFINITION : Differential Term261 ;; A quantity with infinitesimal components, e.g. x, dxdy, 4dydz. The262 ;; coefficient of the quantity is returned by the 'coefficient' method,263 ;; while the sequence of differential parameters is returned by the264 ;; method 'partials'.266 ;; Instead of using (potentially ambiguous) letters to denote267 ;; differential parameters (dx,dy,dz), we use integers. So, dxdz becomes [0 2].269 ;; The coefficient can be any arithmetic object; the270 ;; partials must be a nonrepeating sorted sequence of nonnegative271 ;; integers.273 (deftype DifferentialTerm [coefficient partials])275 (defn differential-term276 "Make a differential term from a coefficient and list of partials."277 [coefficient partials]278 (if (and (coll? partials) (every? #(and (integer? %) (not(neg? %))) partials))279 (DifferentialTerm. coefficient (set partials))280 (throw (java.lang.IllegalArgumentException. "Partials must be a collection of integers."))))283 ;; DEFINITION : Differential Sequence284 ;; A differential sequence is a sequence of differential terms, all with different partials.285 ;; Internally, it is a map from the partials of each term to their coefficients.287 (deftype DifferentialSeq288 [terms]289 ;;clojure.lang.IPersistentMap290 clojure.lang.Associative291 (assoc [this key val]292 (DifferentialSeq.293 (cons (differential-term val key) terms)))294 (cons [this x]295 (DifferentialSeq. (cons x terms)))296 (containsKey [this key]297 (not(nil? (find-first #(= (.partials %) key) terms))))298 (count [this] (count (.terms this)))299 (empty [this] (DifferentialSeq. []))300 (entryAt [this key]301 ((juxt #(.partials %) #(.coefficient %))302 (find-first #(= (.partials %) key) terms)))303 (seq [this] (seq (.terms this))))305 (def differential? #(= (type %) DifferentialSeq))307 (defn zeroth-order?308 "Returns true if the differential sequence has at most a constant term."309 [dseq]310 (and311 (differential? dseq)312 (every?313 #(= #{} %)314 (keys (.terms dseq)))))316 (defmethod fmap DifferentialSeq317 [f dseq]318 (DifferentialSeq.319 (fmap f (.terms dseq))))324 ;; BUILDING DIFFERENTIAL OBJECTS326 (defn differential-seq327 "Define a differential sequence by specifying an alternating328 sequence of coefficients and lists of partials."329 ([coefficient partials]330 (DifferentialSeq. {(set partials) coefficient}))331 ([coefficient partials & cps]332 (if (odd? (count cps))333 (throw (Exception. "differential-seq requires an even number of terms."))334 (DifferentialSeq.335 (reduce336 #(assoc %1 (set (second %2)) (first %2))337 {(set partials) coefficient}338 (partition 2 cps))))))342 (defn big-part343 "Returns the part of the differential sequence that is finite,344 i.e. not infinitely small. If the sequence is zeroth-order, returns345 the coefficient of the zeroth-order term instead. "346 [dseq]347 (if (zeroth-order? dseq) (get (.terms dseq) #{})348 (let [m (.terms dseq)349 keys (sort-by count (keys m))350 smallest-var (last (last keys))]351 (DifferentialSeq.352 (reduce353 #(assoc %1 (first %2) (second %2))354 {}355 (remove #((first %) smallest-var) m))))))358 (defn small-part359 "Returns the part of the differential sequence that infinitely360 small. If the sequence is zeroth-order, returns zero."361 [dseq]362 (if (zeroth-order? dseq) 0363 (let [m (.terms dseq)364 keys (sort-by count (keys m))365 smallest-var (last (last keys))]366 (DifferentialSeq.367 (reduce368 #(assoc %1 (first %2) (second %2)) {}369 (filter #((first %) smallest-var) m))))))373 (defn cartesian-product [set1 set2]374 (reduce concat375 (for [x set1]376 (for [y set2]377 [x y]))))379 (defn nth-subset [n]380 (if (zero? n) []381 (let [lg2 #(/ (log %) (log 2))382 k (int(java.lang.Math/floor (lg2 n)))383 ]384 (cons k385 (nth-subset (- n (pow 2 k)))))))387 (def all-partials388 (lazy-seq (map nth-subset (range))))391 (defn differential-multiply392 "Multiply two differential sequences. The square of any differential393 variable is zero since differential variables are infinitesimally394 small."395 [dseq1 dseq2]396 (DifferentialSeq.397 (reduce398 (fn [m [[vars1 coeff1] [vars2 coeff2]]]399 (if (not (empty? (clojure.set/intersection vars1 vars2)))400 m401 (assoc m (clojure.set/union vars1 vars2) (* coeff1 coeff2))))402 {}403 (cartesian-product (.terms dseq1) (.terms dseq2)))))407 (defmethod * [DifferentialSeq DifferentialSeq]408 [dseq1 dseq2]409 (differential-multiply dseq1 dseq2))411 (defmethod + [DifferentialSeq DifferentialSeq]412 [dseq1 dseq2]413 (DifferentialSeq.414 (merge-with + (.terms dseq1) (.terms dseq2))))416 (defmethod * [java.lang.Number DifferentialSeq]417 [x dseq]418 (fmap (partial * x) dseq))420 (defmethod * [DifferentialSeq java.lang.Number]421 [dseq x]422 (fmap (partial * x) dseq))424 (defmethod + [java.lang.Number DifferentialSeq]425 [x dseq]426 (+ (differential-seq x []) dseq))427 (defmethod + [DifferentialSeq java.lang.Number]428 [dseq x]429 (+ dseq (differential-seq x [])))431 (defmethod - DifferentialSeq432 [x]433 (fmap - x))436 ;; DERIVATIVES440 (defn linear-approximator441 "Returns an operator that linearly approximates the given function."442 ([f df|dx]443 (fn [x]444 (let [big-part (big-part x)445 small-part (small-part x)]446 ;; f(x+dx) ~= f(x) + f'(x)dx447 (+ (f big-part)448 (* (df|dx big-part) small-part)449 ))))451 ([f df|dx df|dy]452 (fn [x y]453 (let [X (big-part x)454 Y (big-part y)455 DX (small-part x)456 DY (small-part y)]457 (+ (f X Y)458 (* DX (f df|dx X Y))459 (* DY (f df|dy X Y)))))))465 (defn D[f]466 (fn[x] (f (+ x (differential-seq 1 [0] 1 [1] 1 [2])))))468 (defn d[partials f]469 (fn [x]470 (get471 (.terms ((D f)x))472 (set partials)473 0474 )))476 (defmethod exp DifferentialSeq [x]477 ((linear-approximator exp exp) x))479 (defmethod sin DifferentialSeq480 [x]481 ((linear-approximator sin cos) x))483 (defmethod cos DifferentialSeq484 [x]485 ((linear-approximator cos #(- (sin %))) x))487 (defmethod log DifferentialSeq488 [x]489 ((linear-approximator log (fn [x] (/ x)) ) x))491 (defmethod / [DifferentialSeq DifferentialSeq]492 [x y]493 ((linear-approximator /494 (fn [x y] (/ 1 y))495 (fn [x y] (- (/ x (* y y)))))496 x y))