comparison sicm/utils.org @ 2:b4de894a1e2e

initial import
author Robert McIntyre <rlm@mit.edu>
date Fri, 28 Oct 2011 00:03:05 -0700
parents
children
comparison
equal deleted inserted replaced
1:8d8278e09888 2:b4de894a1e2e
1 #+TITLE: Building a Classical Mechanics Library in Clojure
2 #+author: Dylan Holmes
3 #+EMAIL: rlm@mit.edu
4 #+MATHJAX: align:"left" mathml:t path:"../MathJax/MathJax.js"
5 #+STYLE: <link rel="stylesheet" type="text/css" href="../css/aurellem.css" />
6 #+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
7 #+SETUPFILE: ../templates/level-0.org
8 #+INCLUDE: ../templates/level-0.org
9 #+BABEL: :noweb yes :results silent
10
11 * Useful data types
12
13 ** Complex numbers
14
15 ** Power series
16
17 ** Tuples and tensors
18
19 *** Tuples are \ldquo{}sequences with spin\rdquo{}
20
21 #+srcname: tuples
22 #+begin_src clojure
23 (in-ns 'sicm.utils)
24
25 ;; Let some objects have spin
26
27 (defprotocol Spinning
28 (up? [this])
29 (down? [this]))
30
31 (defn spin
32 "Returns the spin of the Spinning s, either :up or :down"
33 [#^Spinning s]
34 (cond (up? s) :up (down? s) :down))
35
36
37 ;; DEFINITION: A tuple is a sequence with spin
38
39 (deftype Tuple
40 [spin coll]
41
42 clojure.lang.Seqable
43 (seq [this] (seq (.coll this)))
44
45 clojure.lang.Counted
46 (count [this] (count (.coll this)))
47
48 Spinning
49 (up? [this] (= ::up (.spin this)))
50 (down? [this] (= ::down (.spin this))))
51
52 (defmethod print-method Tuple
53 [o w]
54 (print-simple
55 (if (up? o)
56 (str "u" (.coll o))
57 (str "d" (vec(.coll o))))
58 w))
59
60 (def tuple? #(= (type %) Tuple))
61
62 ;; CONSTRUCTORS
63
64 (defn up
65 "Create a new up-tuple containing the contents of coll."
66 [coll]
67 (Tuple. ::up coll))
68
69 (defn down
70 "Create a new down-tuple containing the contents of coll."
71 [coll]
72 (Tuple. ::down coll))
73
74 (defn same-spin
75 "Creates a tuple which has the same spin as tuple and which contains
76 the contents of coll."
77 [tuple coll]
78 (if (up? tuple)
79 (up coll)
80 (down coll)))
81
82 (defn opposite-spin
83 "Create a tuple which has opposite spin to tuple and which contains
84 the contents of coll."
85 [tuple coll]
86 (if (up? tuple)
87 (down coll)
88 (up coll)))
89 #+end_src
90
91
92
93 *** Matrices
94 #+srcname:matrices
95 #+begin_src clojure
96 (in-ns 'sicm.utils)
97 (require 'incanter.core) ;; use incanter's fast matrices
98
99
100 (defn all-equal? [coll]
101 (if (empty? (rest coll)) true
102 (and (= (first coll) (second coll))
103 (recur (rest coll)))))
104
105
106 (defprotocol Matrix
107 (rows [matrix])
108 (cols [matrix])
109 (diagonal [matrix])
110 (trace [matrix])
111 (determinant [matrix])
112 (transpose [matrix])
113 (conjugate [matrix])
114 )
115
116 (extend-protocol Matrix incanter.Matrix
117 (rows [rs] (map down (apply map vector (apply map vector rs))))
118 (cols [rs] (map up (apply map vector rs)))
119 (diagonal [matrix] (incanter.core/diag matrix) )
120 (determinant [matrix] (incanter.core/det matrix))
121 (trace [matrix] (incanter.core/trace matrix))
122 (transpose [matrix] (incanter.core/trans matrix)))
123
124 (defn count-rows [matrix]
125 ((comp count rows) matrix))
126
127 (defn count-cols [matrix]
128 ((comp count cols) matrix))
129
130 (defn square? [matrix]
131 (= (count-rows matrix) (count-cols matrix)))
132
133 (defn identity-matrix
134 "Define a square matrix of size n-by-n with 1s along the diagonal and
135 0s everywhere else."
136 [n]
137 (incanter.core/identity-matrix n))
138
139
140 (defn matrix-by-rows
141 "Define a matrix by giving its rows."
142 [& rows]
143 (if
144 (not (all-equal? (map count rows)))
145 (throw (Exception. "All rows in a matrix must have the same number of elements."))
146 (incanter.core/matrix (vec rows))))
147
148 (defn matrix-by-cols
149 "Define a matrix by giving its columns"
150 [& cols]
151 (if (not (all-equal? (map count cols)))
152 (throw (Exception. "All columns in a matrix must have the same number of elements."))
153 (incanter.core/matrix (vec (apply map vector cols)))))
154
155 (defn identity-matrix
156 "Define a square matrix of size n-by-n with 1s along the diagonal and
157 0s everywhere else."
158 [n]
159 (incanter.core/identity-matrix n))
160
161 #+end_src
162
163 ** Generic Operations
164 #+srcname:arith-tuple
165 #+begin_src clojure
166 (in-ns 'sicm.utils)
167 (use 'clojure.contrib.generic.arithmetic
168 'clojure.contrib.generic.collection
169 'clojure.contrib.generic.functor
170 'clojure.contrib.generic.math-functions)
171
172 (defn numbers?
173 "Returns true if all arguments are numbers, else false."
174 [& xs]
175 (every? number? xs))
176
177 (defn tuple-surgery
178 "Applies the function f to the items of tuple and the additional
179 arguments, if any. Returns a Tuple of the same type as tuple."
180 [tuple f & xs]
181 ((if (up? tuple) up down)
182 (apply f (seq tuple) xs)))
183
184
185
186 ;;; CONTRACTION collapses two compatible tuples into a number.
187
188 (defn contractible?
189 "Returns true if the tuples a and b are compatible for contraction,
190 else false. Tuples are compatible if they have the same number of
191 components, they have opposite spins, and their elements are
192 pairwise-compatible."
193 [a b]
194 (and
195 (isa? (type a) Tuple)
196 (isa? (type b) Tuple)
197 (= (count a) (count b))
198 (not= (spin a) (spin b))
199
200 (not-any? false?
201 (map #(or
202 (numbers? %1 %2)
203 (contractible? %1 %2))
204 a b))))
205
206 (defn contract
207 "Contracts two tuples, returning the sum of the
208 products of the corresponding items. Contraction is recursive on
209 nested tuples."
210 [a b]
211 (if (not (contractible? a b))
212 (throw
213 (Exception. "Not compatible for contraction."))
214 (reduce +
215 (map
216 (fn [x y]
217 (if (numbers? x y)
218 (* x y)
219 (contract x y)))
220 a b))))
221
222
223
224
225
226 (defmethod conj Tuple
227 [tuple & xs]
228 (tuple-surgery tuple #(apply conj % xs)))
229
230 (defmethod fmap Tuple
231 [f tuple]
232 (tuple-surgery tuple (partial map f)))
233
234
235
236 ;; TODO: define Scalar, and add it to the hierarchy above Number and Complex
237
238
239 (defmethod * [Tuple Tuple] ; tuple*tuple
240 [a b]
241 (if (contractible? a b)
242 (contract a b)
243 (map (partial * a) b)))
244
245
246 (defmethod * [java.lang.Number Tuple] ;; scalar * tuple
247 [a x] (fmap (partial * a) x))
248
249 (defmethod * [Tuple java.lang.Number]
250 [x a] (* a x))
251
252 (defmethod * [java.lang.Number incanter.Matrix] ;; scalar * matrix
253 [x M] (incanter.core/mult x M))
254
255 (defmethod * [incanter.Matrix java.lang.Number]
256 [M x] (* x M))
257
258 (defmethod * [incanter.Matrix incanter.Matrix] ;; matrix * matrix
259 [M1 M2]
260 (incanter.core/mmult M1 M2))
261
262 (defmethod * [incanter.Matrix Tuple] ;; matrix * tuple
263 [M v]
264 (if (and (apply numbers? v) (up? v))
265 (* M (matrix-by-cols v))
266 (throw (Exception. "Currently, you can only multiply a matrix by a tuple of *numbers*"))
267 ))
268
269 (defmethod * [Tuple incanter.Matrix] ;; tuple * Matrix
270 [v M]
271 (if (and (apply numbers? v) (down? v))
272 (* (matrix-by-rows v) M)
273 (throw (Exception. "Currently, you can only multiply a matrix by a tuple of *numbers*"))
274 ))
275
276
277 (defmethod exp incanter.Matrix
278 [M]
279 (incanter.core/exp M))
280
281 #+end_src
282
283 * Operators and Differentiation
284 ** Operators
285 #+scrname: operators
286 #+begin_src clojure
287 (in-ns 'sicm.utils)
288 (use 'clojure.contrib.seq
289 'clojure.contrib.generic.arithmetic
290 'clojure.contrib.generic.collection
291 'clojure.contrib.generic.math-functions)
292
293 (defmethod + [clojure.lang.IFn clojure.lang.IFn]
294 [f g]
295 (fn [& args]
296 (+ (apply f args) (apply g args))))
297
298 (defmethod * [clojure.lang.IFn clojure.lang.IFn]
299 [f g]
300 (fn [& args]
301 (* (apply f args) (apply g args))))
302
303 (defmethod / [clojure.lang.IFn java.lang.Number]
304 [f x]
305 (fn [& args]
306 (/ (apply f args) x)))
307
308
309 (defmethod - [clojure.lang.IFn]
310 [f]
311 (fn [& args]
312 (- (apply f args))))
313
314 (defmethod - [clojure.lang.IFn clojure.lang.IFn]
315 [f g]
316 (fn [& args]
317 (- (apply f args) (apply g args))))
318
319 (defmethod pow [clojure.lang.IFn java.lang.Number]
320 [f x]
321 (fn [& args]
322 (pow (apply f args) x)))
323
324
325 (defmethod + [java.lang.Number clojure.lang.IFn]
326 [x f]
327 (fn [& args]
328 (+ x (apply f args))))
329
330 (defmethod * [java.lang.Number clojure.lang.IFn]
331 [x f]
332 (fn [& args]
333 (* x (apply f args))))
334
335 (defmethod * [clojure.lang.IFn java.lang.Number]
336 [f x]
337 (* x f))
338 (defmethod + [clojure.lang.IFn java.lang.Number]
339 [f x]
340 (+ x f))
341
342 #+end_src
343
344 ** Differential Terms and Sequences
345 #+srcname: differential
346 #+begin_src clojure
347 (in-ns 'sicm.utils)
348 (use 'clojure.contrib.seq
349 'clojure.contrib.generic.arithmetic
350 'clojure.contrib.generic.collection
351 'clojure.contrib.generic.math-functions)
352
353 ;;∂
354
355 ;; DEFINITION : Differential Term
356
357 ;; A quantity with infinitesimal components, e.g. x, dxdy, 4dydz. The
358 ;; coefficient of the quantity is returned by the 'coefficient' method,
359 ;; while the sequence of differential parameters is returned by the
360 ;; method 'partials'.
361
362 ;; Instead of using (potentially ambiguous) letters to denote
363 ;; differential parameters (dx,dy,dz), we use integers. So, dxdz becomes [0 2].
364
365 ;; The coefficient can be any arithmetic object; the
366 ;; partials must be a nonrepeating sorted sequence of nonnegative
367 ;; integers.
368
369 ;; (deftype DifferentialTerm [coefficient partials])
370
371 ;; (defn differential-term
372 ;; "Make a differential term from a coefficient and list of partials."
373 ;; [coefficient partials]
374 ;; (if (and (coll? partials) (every? #(and (integer? %) (not(neg? %))) partials))
375 ;; (DifferentialTerm. coefficient (set partials))
376 ;; (throw (java.lang.IllegalArgumentException. "Partials must be a collection of integers."))))
377
378
379 ;; DEFINITION : Differential Sequence
380 ;; A differential sequence is a sequence of differential terms, all with different partials.
381 ;; Internally, it is a map from the partials of each term to their coefficients.
382
383 (deftype DifferentialSeq
384 [terms]
385 ;;clojure.lang.IPersistentMap
386 clojure.lang.Associative
387 (assoc [this key val]
388 (DifferentialSeq.
389 (cons (differential-term val key) terms)))
390 (cons [this x]
391 (DifferentialSeq. (cons x terms)))
392 (containsKey [this key]
393 (not(nil? (find-first #(= (.partials %) key) terms))))
394 (count [this] (count (.terms this)))
395 (empty [this] (DifferentialSeq. []))
396 (entryAt [this key]
397 ((juxt #(.partials %) #(.coefficient %))
398 (find-first #(= (.partials %) key) terms)))
399 (seq [this] (seq (.terms this))))
400
401 (def differential? #(= (type %) DifferentialSeq))
402
403 (defn zeroth-order?
404 "Returns true if the differential sequence has at most a constant term."
405 [dseq]
406 (and
407 (differential? dseq)
408 (every?
409 #(= #{} %)
410 (keys (.terms dseq)))))
411
412 (defmethod fmap DifferentialSeq
413 [f dseq]
414 (DifferentialSeq.
415 (fmap f (.terms dseq))))
416
417
418
419
420 ;; BUILDING DIFFERENTIAL OBJECTS
421
422 (defn differential-seq
423 "Define a differential sequence by specifying an alternating
424 sequence of coefficients and lists of partials."
425 ([coefficient partials]
426 (DifferentialSeq. {(set partials) coefficient}))
427 ([coefficient partials & cps]
428 (if (odd? (count cps))
429 (throw (Exception. "differential-seq requires an even number of terms."))
430 (DifferentialSeq.
431 (reduce
432 #(assoc %1 (set (second %2)) (first %2))
433 {(set partials) coefficient}
434 (partition 2 cps))))))
435
436
437
438 (defn big-part
439 "Returns the part of the differential sequence that is finite,
440 i.e. not infinitely small. If the sequence is zeroth-order, returns
441 the coefficient of the zeroth-order term instead. "
442 [dseq]
443 (if (zeroth-order? dseq) (get (.terms dseq) #{})
444 (let [m (.terms dseq)
445 keys (sort-by count (keys m))
446 smallest-var (last (last keys))]
447 (DifferentialSeq.
448 (reduce
449 #(assoc %1 (first %2) (second %2))
450 {}
451 (remove #((first %) smallest-var) m))))))
452
453
454 (defn small-part
455 "Returns the part of the differential sequence that infinitely
456 small. If the sequence is zeroth-order, returns zero."
457 [dseq]
458 (if (zeroth-order? dseq) 0
459 (let [m (.terms dseq)
460 keys (sort-by count (keys m))
461 smallest-var (last (last keys))]
462 (DifferentialSeq.
463 (reduce
464 #(assoc %1 (first %2) (second %2)) {}
465 (filter #((first %) smallest-var) m))))))
466
467
468
469 (defn cartesian-product [set1 set2]
470 (reduce concat
471 (for [x set1]
472 (for [y set2]
473 [x y]))))
474
475 (defn nth-subset [n]
476 (if (zero? n) []
477 (let [lg2 #(/ (log %) (log 2))
478 k (int(java.lang.Math/floor (lg2 n)))
479 ]
480 (cons k
481 (nth-subset (- n (pow 2 k)))))))
482
483 (def all-partials
484 (lazy-seq (map nth-subset (range))))
485
486
487 (defn differential-multiply
488 "Multiply two differential sequences. The square of any differential
489 variable is zero since differential variables are infinitesimally
490 small."
491 [dseq1 dseq2]
492 (DifferentialSeq.
493 (reduce
494 (fn [m [[vars1 coeff1] [vars2 coeff2]]]
495 (if (not (empty? (clojure.set/intersection vars1 vars2)))
496 m
497 (assoc m (clojure.set/union vars1 vars2) (* coeff1 coeff2))))
498 {}
499 (cartesian-product (.terms dseq1) (.terms dseq2)))))
500
501
502
503 (defmethod * [DifferentialSeq DifferentialSeq]
504 [dseq1 dseq2]
505 (differential-multiply dseq1 dseq2))
506
507 (defmethod + [DifferentialSeq DifferentialSeq]
508 [dseq1 dseq2]
509 (DifferentialSeq.
510 (merge-with + (.terms dseq1) (.terms dseq2))))
511
512 (defmethod * [java.lang.Number DifferentialSeq]
513 [x dseq]
514 (fmap (partial * x) dseq))
515
516 (defmethod * [DifferentialSeq java.lang.Number]
517 [dseq x]
518 (fmap (partial * x) dseq))
519
520 (defmethod + [java.lang.Number DifferentialSeq]
521 [x dseq]
522 (+ (differential-seq x []) dseq))
523 (defmethod + [DifferentialSeq java.lang.Number]
524 [dseq x]
525 (+ dseq (differential-seq x [])))
526
527 (defmethod - DifferentialSeq
528 [x]
529 (fmap - x))
530
531
532 ;; DERIVATIVES
533
534
535
536 (defn linear-approximator
537 "Returns an operator that linearly approximates the given function."
538 ([f df|dx]
539 (fn [x]
540 (let [big-part (big-part x)
541 small-part (small-part x)]
542 ;; f(x+dx) ~= f(x) + f'(x)dx
543 (+ (f big-part)
544 (* (df|dx big-part) small-part)
545 ))))
546
547 ([f df|dx df|dy]
548 (fn [x y]
549 (let [X (big-part x)
550 Y (big-part y)
551 DX (small-part x)
552 DY (small-part y)]
553 (+ (f X Y)
554 (* DX (f df|dx X Y))
555 (* DY (f df|dy X Y)))))))
556
557
558
559
560
561 (defn D[f]
562 (fn[x] (f (+ x (differential-seq 1 [0] 1 [1] 1 [2])))))
563
564 (defn d[partials f]
565 (fn [x]
566 (get
567 (.terms ((D f)x))
568 (set partials)
569 0
570 )))
571
572 (defmethod exp DifferentialSeq [x]
573 ((linear-approximator exp exp) x))
574
575 (defmethod sin DifferentialSeq
576 [x]
577 ((linear-approximator sin cos) x))
578
579 (defmethod cos DifferentialSeq
580 [x]
581 ((linear-approximator cos #(- (sin %))) x))
582
583 (defmethod log DifferentialSeq
584 [x]
585 ((linear-approximator log (fn [x] (/ x)) ) x))
586
587 (defmethod / [DifferentialSeq DifferentialSeq]
588 [x y]
589 ((linear-approximator /
590 (fn [x y] (/ 1 y))
591 (fn [x y] (- (/ x (* y y)))))
592 x y))
593
594 #+end_src
595
596
597
598 * Derivatives Revisited
599 #+begin_src clojure
600 (in-ns 'sicm.utils)
601 (use 'clojure.contrib.seq
602 'clojure.contrib.generic.arithmetic
603 'clojure.contrib.generic.collection
604 'clojure.contrib.generic.math-functions)
605
606 (defn replace-in
607 "Replaces the nth item in coll with the given item. If n is larger
608 than the size of coll, adds n to the end of the collection."
609 [coll n item]
610 (concat
611 (take n coll)
612 [item]
613 (drop (inc n) coll)))
614
615 (defn euclidean-structure [f partials]
616 (fn sd[g v]
617 (cond
618 (tuple? v)
619 (opposite-spin
620 v
621 (map
622 (fn [n]
623 (sd (fn [xn]
624 (g
625 (same-spin v
626 (replace-in v n xn))))
627 (nth v n)))
628 (range (count v)))))))
629
630
631
632
633 #+end_src
634
635
636 * Symbolic Quantities
637
638 #+srcname: symbolic
639 #+begin_src clojure
640 (in-ns 'sicm.utils)
641 (use 'clojure.contrib.generic.arithmetic
642 'clojure.contrib.generic.math-functions)
643
644 (deftype Symbolic
645 [type
646 expression])
647
648 (defn print-expression [s]
649 (print (.expression s)))
650
651 (defn symbolic-number
652 [symbol]
653 (Symbolic. java.lang.Number symbol))
654
655 (defn simplify-expression [s]
656 (let [e (.expression s)]
657 (cond
658 (not(list? e)) e
659 (= (first e) '+)
660 )
661
662
663
664 (defmethod + [Symbolic Symbolic]
665 [x y]
666 (Symbolic. (.type x) (list '+ (.expression x) (.expression y))))
667
668 (defmethod + [Symbolic java.lang.Number]
669 [s x]
670 (if (zero? x)
671 s
672 (Symbolic. (.type s) (list '+ (.expression s) x))))
673
674 (defmethod sin Symbolic
675 [s]
676 (Symbolic. (.type s) (list 'sin (.expression s))))
677
678 #+end_src
679
680 * COMMENT To-be-tangled Source
681 #+begin_src clojure :tangle utils.clj
682 (ns sicm.utils)
683
684 <<tuples>>
685 <<matrices>>
686 <<arith-tuple>>
687
688 <<differential>>
689 #+end_src
690