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 spin
8 (defprotocol Spinning
9 (up? [this])
10 (down? [this]))
12 (defn spin
13 "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 spin
20 (deftype Tuple
21 [spin coll]
23 clojure.lang.Seqable
24 (seq [this] (seq (.coll this)))
26 clojure.lang.Counted
27 (count [this] (count (.coll this)))
29 Spinning
30 (up? [this] (= ::up (.spin this)))
31 (down? [this] (= ::down (.spin this))))
33 (defmethod print-method Tuple
34 [o w]
35 (print-simple
36 (if (up? o)
37 (str "u" (.coll o))
38 (str "d" (vec(.coll o))))
39 w))
41 (def tuple? #(= (type %) Tuple))
43 ;; CONSTRUCTORS
45 (defn up
46 "Create a new up-tuple containing the contents of coll."
47 [coll]
48 (Tuple. ::up coll))
50 (defn down
51 "Create a new down-tuple containing the contents of coll."
52 [coll]
53 (Tuple. ::down coll))
55 (defn same-spin
56 "Creates a tuple which has the same spin as tuple and which contains
57 the contents of coll."
58 [tuple coll]
59 (if (up? tuple)
60 (up coll)
61 (down coll)))
63 (defn opposite-spin
64 "Create a tuple which has opposite spin to tuple and which contains
65 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 matrices
74 (defn all-equal? [coll]
75 (if (empty? (rest coll)) true
76 (and (= (first coll) (second coll))
77 (recur (rest coll)))))
80 (defprotocol Matrix
81 (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.Matrix
91 (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-matrix
108 "Define a square matrix of size n-by-n with 1s along the diagonal and
109 0s everywhere else."
110 [n]
111 (incanter.core/identity-matrix n))
114 (defn matrix-by-rows
115 "Define a matrix by giving its rows."
116 [& rows]
117 (if
118 (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-cols
123 "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-matrix
130 "Define a square matrix of size n-by-n with 1s along the diagonal and
131 0s everywhere else."
132 [n]
133 (incanter.core/identity-matrix n))
135 (in-ns 'sicm.utils)
136 (use 'clojure.contrib.generic.arithmetic
137 'clojure.contrib.generic.collection
138 'clojure.contrib.generic.functor
139 '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-surgery
147 "Applies the function f to the items of tuple and the additional
148 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 of
160 components, they have opposite spins, and their elements are
161 pairwise-compatible."
162 [a b]
163 (and
164 (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 #(or
171 (numbers? %1 %2)
172 (contractible? %1 %2))
173 a b))))
175 (defn contract
176 "Contracts two tuples, returning the sum of the
177 products of the corresponding items. Contraction is recursive on
178 nested tuples."
179 [a b]
180 (if (not (contractible? a b))
181 (throw
182 (Exception. "Not compatible for contraction."))
183 (reduce +
184 (map
185 (fn [x y]
186 (if (numbers? x y)
187 (* x y)
188 (contract x y)))
189 a b))))
195 (defmethod conj Tuple
196 [tuple & xs]
197 (tuple-surgery tuple #(apply conj % xs)))
199 (defmethod fmap Tuple
200 [f tuple]
201 (tuple-surgery tuple (partial map f)))
205 ;; TODO: define Scalar, and add it to the hierarchy above Number and Complex
208 (defmethod * [Tuple Tuple] ; tuple*tuple
209 [a b]
210 (if (contractible? a b)
211 (contract a b)
212 (map (partial * a) b)))
215 (defmethod * [java.lang.Number Tuple] ;; scalar * tuple
216 [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 * matrix
222 [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 * matrix
228 [M1 M2]
229 (incanter.core/mmult M1 M2))
231 (defmethod * [incanter.Matrix Tuple] ;; matrix * tuple
232 [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 * Matrix
239 [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.Matrix
247 [M]
248 (incanter.core/exp M))
251 (in-ns 'sicm.utils)
252 (use 'clojure.contrib.seq
253 'clojure.contrib.generic.arithmetic
254 'clojure.contrib.generic.collection
255 'clojure.contrib.generic.math-functions)
257 ;;∂
259 ;; DEFINITION : Differential Term
261 ;; A quantity with infinitesimal components, e.g. x, dxdy, 4dydz. The
262 ;; coefficient of the quantity is returned by the 'coefficient' method,
263 ;; while the sequence of differential parameters is returned by the
264 ;; method 'partials'.
266 ;; Instead of using (potentially ambiguous) letters to denote
267 ;; differential parameters (dx,dy,dz), we use integers. So, dxdz becomes [0 2].
269 ;; The coefficient can be any arithmetic object; the
270 ;; partials must be a nonrepeating sorted sequence of nonnegative
271 ;; integers.
273 (deftype DifferentialTerm [coefficient partials])
275 (defn differential-term
276 "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 Sequence
284 ;; 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 DifferentialSeq
288 [terms]
289 ;;clojure.lang.IPersistentMap
290 clojure.lang.Associative
291 (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 (and
311 (differential? dseq)
312 (every?
313 #(= #{} %)
314 (keys (.terms dseq)))))
316 (defmethod fmap DifferentialSeq
317 [f dseq]
318 (DifferentialSeq.
319 (fmap f (.terms dseq))))
324 ;; BUILDING DIFFERENTIAL OBJECTS
326 (defn differential-seq
327 "Define a differential sequence by specifying an alternating
328 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 (reduce
336 #(assoc %1 (set (second %2)) (first %2))
337 {(set partials) coefficient}
338 (partition 2 cps))))))
342 (defn big-part
343 "Returns the part of the differential sequence that is finite,
344 i.e. not infinitely small. If the sequence is zeroth-order, returns
345 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 (reduce
353 #(assoc %1 (first %2) (second %2))
354 {}
355 (remove #((first %) smallest-var) m))))))
358 (defn small-part
359 "Returns the part of the differential sequence that infinitely
360 small. If the sequence is zeroth-order, returns zero."
361 [dseq]
362 (if (zeroth-order? dseq) 0
363 (let [m (.terms dseq)
364 keys (sort-by count (keys m))
365 smallest-var (last (last keys))]
366 (DifferentialSeq.
367 (reduce
368 #(assoc %1 (first %2) (second %2)) {}
369 (filter #((first %) smallest-var) m))))))
373 (defn cartesian-product [set1 set2]
374 (reduce concat
375 (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 k
385 (nth-subset (- n (pow 2 k)))))))
387 (def all-partials
388 (lazy-seq (map nth-subset (range))))
391 (defn differential-multiply
392 "Multiply two differential sequences. The square of any differential
393 variable is zero since differential variables are infinitesimally
394 small."
395 [dseq1 dseq2]
396 (DifferentialSeq.
397 (reduce
398 (fn [m [[vars1 coeff1] [vars2 coeff2]]]
399 (if (not (empty? (clojure.set/intersection vars1 vars2)))
400 m
401 (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 - DifferentialSeq
432 [x]
433 (fmap - x))
436 ;; DERIVATIVES
440 (defn linear-approximator
441 "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)dx
447 (+ (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 (get
471 (.terms ((D f)x))
472 (set partials)
473 0
474 )))
476 (defmethod exp DifferentialSeq [x]
477 ((linear-approximator exp exp) x))
479 (defmethod sin DifferentialSeq
480 [x]
481 ((linear-approximator sin cos) x))
483 (defmethod cos DifferentialSeq
484 [x]
485 ((linear-approximator cos #(- (sin %))) x))
487 (defmethod log DifferentialSeq
488 [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))