rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: aurellem ☉
rlm@2: rlm@2:Building a Classical Mechanics Library in Clojure
rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2:
rlm@2:
rlm@2:
rlm@2: Table of Contents
rlm@2:
rlm@2:
rlm@2: -
rlm@2:
- 1 Useful data types
rlm@2:
-
rlm@2:
- 1.1 Complex numbers rlm@2:
- 1.2 Power series rlm@2:
- 1.3 Tuples and tensors
rlm@2:
-
rlm@2:
- 1.3.1 Tuples are “sequences with spin” rlm@2:
- 1.3.2 Matrices rlm@2:
rlm@2: - 1.4 Generic Operations rlm@2:
rlm@2: - 2 Operators and Differentiation
rlm@2:
-
rlm@2:
- 2.1 Operators rlm@2:
- 2.2 Differential Terms and Sequences rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 1 Useful data types
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 1.1 Complex numbers
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 1.2 Power series
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 1.3 Tuples and tensors
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 1.3.1 Tuples are “sequences with spin”
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: (in-ns 'sicm.utils) rlm@2: rlm@2: ;; Let some objects have spin rlm@2: rlm@2: (defprotocol Spinning rlm@2: (up? [this]) rlm@2: (down? [this])) rlm@2: rlm@2: (defn spin rlm@2: "Returns the spin of the Spinning s, either :up or :down" rlm@2: [#^Spinning s] rlm@2: (cond (up? s) :up (down? s) :down)) rlm@2: rlm@2: rlm@2: ;; DEFINITION: A tuple is a sequence with spin rlm@2: rlm@2: (deftype Tuple rlm@2: [spin coll] rlm@2: rlm@2: clojure.lang.Seqable rlm@2: (seq [this] (seq (.coll this))) rlm@2: rlm@2: clojure.lang.Counted rlm@2: (count [this] (count (.coll this))) rlm@2: rlm@2: Spinning rlm@2: (up? [this] (= ::up (.spin this))) rlm@2: (down? [this] (= ::down (.spin this)))) rlm@2: rlm@2: (defmethod print-method Tuple rlm@2: [o w] rlm@2: (print-simple rlm@2: (if (up? o) rlm@2: (str "u" (.coll o)) rlm@2: (str "d" (vec(.coll o)))) rlm@2: w)) rlm@2: rlm@2: rlm@2: ;; CONSTRUCTORS rlm@2: rlm@2: (defn up rlm@2: "Create a new up-tuple containing the contents of coll." rlm@2: [coll] rlm@2: (Tuple. ::up coll)) rlm@2: rlm@2: (defn down rlm@2: "Create a new down-tuple containing the contents of coll." rlm@2: [coll] rlm@2: (Tuple. ::down coll)) rlm@2:rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2:
rlm@2:
rlm@2:
rlm@2: 1.3.2 Matrices
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: (in-ns 'sicm.utils) rlm@2: (require 'incanter.core) ;; use incanter's fast matrices rlm@2: rlm@2: rlm@2: (defn all-equal? [coll] rlm@2: (if (empty? (rest coll)) true rlm@2: (and (= (first coll) (second coll)) rlm@2: (recur (rest coll))))) rlm@2: rlm@2: rlm@2: (defprotocol Matrix rlm@2: (rows [matrix]) rlm@2: (cols [matrix]) rlm@2: (diagonal [matrix]) rlm@2: (trace [matrix]) rlm@2: (determinant [matrix]) rlm@2: (transpose [matrix]) rlm@2: (conjugate [matrix]) rlm@2: ) rlm@2: rlm@2: (extend-protocol Matrix incanter.Matrix rlm@2: (rows [rs] (map down (apply map vector (apply map vector rs)))) rlm@2: (cols [rs] (map up (apply map vector rs))) rlm@2: (diagonal [matrix] (incanter.core/diag matrix) ) rlm@2: (determinant [matrix] (incanter.core/det matrix)) rlm@2: (trace [matrix] (incanter.core/trace matrix)) rlm@2: (transpose [matrix] (incanter.core/trans matrix))) rlm@2: rlm@2: (defn count-rows [matrix] rlm@2: ((comp count rows) matrix)) rlm@2: rlm@2: (defn count-cols [matrix] rlm@2: ((comp count cols) matrix)) rlm@2: rlm@2: (defn square? [matrix] rlm@2: (= (count-rows matrix) (count-cols matrix))) rlm@2: rlm@2: (defn identity-matrix rlm@2: "Define a square matrix of size n-by-n with 1s along the diagonal and rlm@2: 0s everywhere else." rlm@2: [n] rlm@2: (incanter.core/identity-matrix n)) rlm@2: rlm@2: rlm@2: (defn matrix-by-rows rlm@2: "Define a matrix by giving its rows." rlm@2: [& rows] rlm@2: (if rlm@2: (not (all-equal? (map count rows))) rlm@2: (throw (Exception. "All rows in a matrix must have the same number of elements.")) rlm@2: (incanter.core/matrix (vec rows)))) rlm@2: rlm@2: (defn matrix-by-cols rlm@2: "Define a matrix by giving its columns" rlm@2: [& cols] rlm@2: (if (not (all-equal? (map count cols))) rlm@2: (throw (Exception. "All columns in a matrix must have the same number of elements.")) rlm@2: (incanter.core/matrix (vec (apply map vector cols))))) rlm@2: rlm@2: (defn identity-matrix rlm@2: "Define a square matrix of size n-by-n with 1s along the diagonal and rlm@2: 0s everywhere else." rlm@2: [n] rlm@2: (incanter.core/identity-matrix n)) rlm@2: rlm@2:rlm@2: rlm@2: rlm@2: rlm@2: rlm@2:
rlm@2:
rlm@2:
rlm@2: 1.4 Generic Operations
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: (in-ns 'sicm.utils) rlm@2: (use 'clojure.contrib.generic.arithmetic rlm@2: 'clojure.contrib.generic.collection rlm@2: 'clojure.contrib.generic.functor rlm@2: 'clojure.contrib.generic.math-functions) rlm@2: rlm@2: (defn numbers? rlm@2: "Returns true if all arguments are numbers, else false." rlm@2: [& xs] rlm@2: (every? number? xs)) rlm@2: rlm@2: (defn tuple-surgery rlm@2: "Applies the function f to the items of tuple and the additional rlm@2: arguments, if any. Returns a Tuple of the same type as tuple." rlm@2: [tuple f & xs] rlm@2: ((if (up? tuple) up down) rlm@2: (apply f (seq tuple) xs))) rlm@2: rlm@2: rlm@2: rlm@2: ;;; CONTRACTION collapses two compatible tuples into a number. rlm@2: rlm@2: (defn contractible? rlm@2: "Returns true if the tuples a and b are compatible for contraction, rlm@2: else false. Tuples are compatible if they have the same number of rlm@2: components, they have opposite spins, and their elements are rlm@2: pairwise-compatible." rlm@2: [a b] rlm@2: (and rlm@2: (isa? (type a) Tuple) rlm@2: (isa? (type b) Tuple) rlm@2: (= (count a) (count b)) rlm@2: (not= (spin a) (spin b)) rlm@2: rlm@2: (not-any? false? rlm@2: (map #(or rlm@2: (numbers? %1 %2) rlm@2: (contractible? %1 %2)) rlm@2: a b)))) rlm@2: rlm@2: (defn contract rlm@2: "Contracts two tuples, returning the sum of the rlm@2: products of the corresponding items. Contraction is recursive on rlm@2: nested tuples." rlm@2: [a b] rlm@2: (if (not (contractible? a b)) rlm@2: (throw rlm@2: (Exception. "Not compatible for contraction.")) rlm@2: (reduce + rlm@2: (map rlm@2: (fn [x y] rlm@2: (if (numbers? x y) rlm@2: (* x y) rlm@2: (contract x y))) rlm@2: a b)))) rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: (defmethod conj Tuple rlm@2: [tuple & xs] rlm@2: (tuple-surgery tuple #(apply conj % xs))) rlm@2: rlm@2: (defmethod fmap Tuple rlm@2: [f tuple] rlm@2: (tuple-surgery tuple (partial map f))) rlm@2: rlm@2: rlm@2: rlm@2: ;; TODO: define Scalar, and add it to the hierarchy above Number and Complex rlm@2: rlm@2: rlm@2: (defmethod * [Tuple Tuple] ; tuple*tuple rlm@2: [a b] rlm@2: (if (contractible? a b) rlm@2: (contract a b) rlm@2: (map (partial * a) b))) rlm@2: rlm@2: rlm@2: (defmethod * [java.lang.Number Tuple] ;; scalar * tuple rlm@2: [a x] (fmap (partial * a) x)) rlm@2: rlm@2: (defmethod * [Tuple java.lang.Number] rlm@2: [x a] (* a x)) rlm@2: rlm@2: (defmethod * [java.lang.Number incanter.Matrix] ;; scalar * matrix rlm@2: [x M] (incanter.core/mult x M)) rlm@2: rlm@2: (defmethod * [incanter.Matrix java.lang.Number] rlm@2: [M x] (* x M)) rlm@2: rlm@2: (defmethod * [incanter.Matrix incanter.Matrix] ;; matrix * matrix rlm@2: [M1 M2] rlm@2: (incanter.core/mmult M1 M2)) rlm@2: rlm@2: (defmethod * [incanter.Matrix Tuple] ;; matrix * tuple rlm@2: [M v] rlm@2: (if (and (apply numbers? v) (up? v)) rlm@2: (* M (matrix-by-cols v)) rlm@2: (throw (Exception. "Currently, you can only multiply a matrix by a tuple of *numbers*")) rlm@2: )) rlm@2: rlm@2: (defmethod * [Tuple incanter.Matrix] ;; tuple * Matrix rlm@2: [v M] rlm@2: (if (and (apply numbers? v) (down? v)) rlm@2: (* (matrix-by-rows v) M) rlm@2: (throw (Exception. "Currently, you can only multiply a matrix by a tuple of *numbers*")) rlm@2: )) rlm@2: rlm@2: rlm@2: (defmethod exp incanter.Matrix rlm@2: [M] rlm@2: (incanter.core/exp M)) rlm@2: rlm@2:rlm@2: rlm@2: rlm@2: rlm@2: rlm@2:
rlm@2:
rlm@2: 2 Operators and Differentiation
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 2.1 Operators
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: 2.2 Differential Terms and Sequences
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2:
rlm@2: (in-ns 'sicm.utils) rlm@2: (use 'clojure.contrib.seq rlm@2: 'clojure.contrib.generic.arithmetic rlm@2: 'clojure.contrib.generic.collection rlm@2: 'clojure.contrib.generic.math-functions) rlm@2: rlm@2: ;;∂ rlm@2: rlm@2: ;; DEFINITION : Differential Term rlm@2: rlm@2: ;; A quantity with infinitesimal components, e.g. x, dxdy, 4dydz. The rlm@2: ;; coefficient of the quantity is returned by the 'coefficient' method, rlm@2: ;; while the sequence of differential parameters is returned by the rlm@2: ;; method 'partials'. rlm@2: rlm@2: ;; Instead of using (potentially ambiguous) letters to denote rlm@2: ;; differential parameters (dx,dy,dz), we use integers. So, dxdz becomes [0 2]. rlm@2: rlm@2: ;; The coefficient can be any arithmetic object; the rlm@2: ;; partials must be a nonrepeating sorted sequence of nonnegative rlm@2: ;; integers. rlm@2: rlm@2: (deftype DifferentialTerm [coefficient partials]) rlm@2: rlm@2: (defn differential-term rlm@2: "Make a differential term given pairs of coefficients and partials." rlm@2: [coefficient partials] rlm@2: (if (and (coll? partials) (every? #(and (integer? %) (not(neg? %))) partials)) rlm@2: (DifferentialTerm. coefficient (set partials)) rlm@2: (throw (java.lang.IllegalArgumentException. "Partials must be a collection of integers.")))) rlm@2: rlm@2: rlm@2: ;; DEFINITION : Differential Sequence rlm@2: ;; A differential sequence is a sequence of differential terms, all with different partials. rlm@2: ;; Internally, it is a map from the partials of each term to their coefficients. rlm@2: rlm@2: (deftype DifferentialSeq rlm@2: [terms] rlm@2: ;;clojure.lang.IPersistentMap rlm@2: clojure.lang.Associative rlm@2: (assoc [this key val] rlm@2: (DifferentialSeq. rlm@2: (cons (differential-term val key) terms))) rlm@2: (cons [this x] rlm@2: (DifferentialSeq. (cons x terms))) rlm@2: (containsKey [this key] rlm@2: (not(nil? (find-first #(= (.partials %) key) terms)))) rlm@2: (count [this] (count (.terms this))) rlm@2: (empty [this] (DifferentialSeq. [])) rlm@2: (entryAt [this key] rlm@2: ((juxt #(.partials %) #(.coefficient %)) rlm@2: (find-first #(= (.partials %) key) terms))) rlm@2: (seq [this] (seq (.terms this)))) rlm@2: rlm@2: rlm@2: (defmethod fmap DifferentialSeq rlm@2: [f dseq] rlm@2: (DifferentialSeq. (fmap f (.terms dseq)))) rlm@2: rlm@2: rlm@2: ;; BUILDING DIFFERENTIAL OBJECTS rlm@2: rlm@2: (defn differential-seq rlm@2: "Define a differential sequence by specifying coefficient/partials rlm@2: pairs, which are used to create differential terms to populate the rlm@2: sequence." rlm@2: ([coefficient partials] rlm@2: (DifferentialSeq. {(set partials) coefficient})) rlm@2: ([coefficient partials & cps] rlm@2: (if (odd? (count cps)) rlm@2: (throw (Exception. "differential-seq requires an even number of terms.")) rlm@2: (DifferentialSeq. rlm@2: (reduce rlm@2: #(assoc %1 (set (second %2)) (first %2)) rlm@2: {(set partials) coefficient} rlm@2: (partition 2 cps)))))) rlm@2: rlm@2: rlm@2: rlm@2: (defn big-part rlm@2: "Returns the part of the differential sequence that is finite, rlm@2: i.e. not infinitely small, as a differential sequence. If given a rlm@2: non-differential object, returns that object." rlm@2: [dseq] rlm@2: (if (not= (type dseq) DifferentialSeq) dseq rlm@2: (let [m (.terms dseq) rlm@2: keys (sort-by count (keys m)) rlm@2: smallest-var (last (last keys))] rlm@2: (DifferentialSeq. rlm@2: (reduce rlm@2: #(assoc %1 (first %2) (second %2)) rlm@2: {} rlm@2: (remove #((first %) smallest-var) m)))))) rlm@2: rlm@2: rlm@2: (defn small-part rlm@2: "Returns the part of the differential sequence that infinitely rlm@2: small, as a differential sequence. If given a non-differential rlm@2: object, returns that object." rlm@2: [dseq] rlm@2: (if (not= (type dseq) DifferentialSeq) 0 rlm@2: (let [m (.terms dseq) rlm@2: keys (sort-by count (keys m)) rlm@2: smallest-var (last (last keys))] rlm@2: (DifferentialSeq. rlm@2: (reduce rlm@2: #(assoc %1 (first %2) (second %2)) {} rlm@2: (filter #((first %) smallest-var) m)))))) rlm@2: rlm@2: rlm@2: rlm@2: (defn cartesian-product [set1 set2] rlm@2: (reduce concat rlm@2: (for [x set1] rlm@2: (for [y set2] rlm@2: [x y])))) rlm@2: rlm@2: (defn differential-multiply rlm@2: "Multiply two differential sequences. The square of any differential rlm@2: variable is zero since differential variables are infinitesimally rlm@2: small." rlm@2: [dseq1 dseq2] rlm@2: (DifferentialSeq. rlm@2: (reduce rlm@2: (fn [m [[vars1 coeff1] [vars2 coeff2]]] rlm@2: (if (not (empty? (clojure.set/intersection vars1 vars2))) rlm@2: m rlm@2: (assoc m (clojure.set/union vars1 vars2) (* coeff1 coeff2)))) rlm@2: {} rlm@2: (cartesian-product (.terms dseq1) (.terms dseq2))))) rlm@2: rlm@2: rlm@2: rlm@2: (defmethod * [DifferentialSeq DifferentialSeq] rlm@2: [dseq1 dseq2] rlm@2: (differential-multiply dseq1 dseq2)) rlm@2: rlm@2: (defmethod + [DifferentialSeq DifferentialSeq] rlm@2: [dseq1 dseq2] rlm@2: (DifferentialSeq. rlm@2: (merge-with + (.terms dseq1) (.terms dseq2)))) rlm@2: rlm@2: (defmethod * [java.lang.Number DifferentialSeq] rlm@2: [x dseq] rlm@2: (fmap (partial * x) dseq)) rlm@2: rlm@2: (defmethod * [DifferentialSeq java.lang.Number] rlm@2: [dseq x] rlm@2: (fmap (partial * x) dseq)) rlm@2: ;; DERIVATIVES rlm@2: rlm@2: rlm@2: rlm@2: (defn linear-approximator rlm@2: "Returns an operator that linearly approximates the given function." rlm@2: ([f df|dx] rlm@2: (fn [x] rlm@2: (let [big-part (big-part x) rlm@2: small-part (small-part x)] rlm@2: (+ (fmap f big-part) rlm@2: (* (fmap df|dx big-part) small-part))))) rlm@2: ([f df|dx df|dy] rlm@2: (fn [x y] rlm@2: (let [X (big-part x) rlm@2: Y (big-part y) rlm@2: DX (small-part x) rlm@2: DY (small-part y)] rlm@2: (+ (f X Y) rlm@2: (+ (* DX (df|dx X Y)) rlm@2: (* DY (df|dy X Y))))))) rlm@2: ) rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: (defn D[f] rlm@2: (fn[x] rlm@2: (f (differential-seq x [] 1 [0])))) rlm@2: rlm@2: (defn d[f partials] rlm@2: (fn [x] rlm@2: (get rlm@2: (.terms ((D f)x)) rlm@2: (set partials) rlm@2: 0 rlm@2: ))) rlm@2: rlm@2: (defmethod exp DifferentialSeq [x] rlm@2: ((linear-approximator exp exp) x)) rlm@2: rlm@2: (defmethod sin DifferentialSeq rlm@2: [x] rlm@2: ((linear-approximator sin cos) x)) rlm@2: rlm@2: (defmethod cos DifferentialSeq rlm@2: [x] rlm@2: ((linear-approximator cos #(- (sin %))) x)) rlm@2: rlm@2: rlm@2: rlm@2:rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2: rlm@2:
rlm@2:
rlm@2: Date: 2011-08-11 04:10:36 EDT
rlm@2: rlm@2:Org version 7.6 with Emacs version 23
rlm@2: Validate XHTML 1.0 rlm@2: