; This benchmark was obtained from Andrew Wright. ; 970215 / wdc Changed {box, unbox, set-box!} to {list, car, set-car!}, ; flushed #% prefixes, defined void, ; and added nbody-benchmark. ; 981116 / wdc Replaced nbody-benchmark by main, added apply:+. (library (rnrs-benchmarks nbody) (export main) (import (rnrs) (rnrs arithmetic flonums) (rnrs r5rs) (rnrs mutable-pairs) (rnrs-benchmarks)) (define void (let ((invisible (string->symbol ""))) (lambda () invisible))) (define (apply:+ xs) (do ((result 0.0 (fl+ result (car xs))) (xs xs (cdr xs))) ((null? xs) result))) ;; code is slightly broken... (define vect (vector)) ; minimal standard random number generator ; 32 bit integer version ; cacm 31 10, oct 88 ; (define *seed* (list 1)) (define (srand seed) (set-car! *seed* seed)) (define (rand) (let* ((hi (quotient (car *seed*) 127773)) (lo (modulo (car *seed*) 127773)) (test (- (* 16807 lo) (* 2836 hi)))) (if (> test 0) (set-car! *seed* test) (set-car! *seed* (+ test 2147483647))) (car *seed*))) ;; return a random number in the interval [0,n) (define random (lambda (n) (modulo (abs (rand)) n))) (define (array-ref a . indices) (let loop ((a a) (indices indices)) (if (null? indices) a (loop (vector-ref a (car indices)) (cdr indices))))) (define (atan0 y x) (if (and (= x y) (= x 0)) 0 (atan y x))) ;change this to desired precision ;measured in order of expansions calculated (define precision 10) ;;; ========================================================= ;;; Algorithm ;;; ========================================================= (define (cartesian-algorithm tree) (up! tree cartesian-make-multipole-expansion cartesian-multipole-shift cartesian-expansion-sum) (down! tree cartesian-multipole-to-local-convert cartesian-local-shift cartesian-eval-local-expansion cartesian-expansion-sum cartesian-zero-expansion)) (define (spherical-algorithm tree) (up! tree spherical-make-multipole-expansion spherical-multipole-shift spherical-expansion-sum) (down! tree spherical-multipole-to-local-convert spherical-local-shift spherical-eval-local-expansion spherical-expansion-sum spherical-zero-expansion)) ;;; The upward path in the algorithm calculates ;;; multipole expansions at every node. (define (up! tree make-multipole-expansion multipole-shift expansion-sum) (let loop ((node (tree-body tree))) (define center (node-center node)) (if (leaf-node? node) (let ((multipole-expansion (expansion-sum (map (lambda (particle) (make-multipole-expansion (pt- (particle-position particle) center) (particle-strength particle))) (node-particles node))))) (set-node-multipole-expansion! node multipole-expansion) (cons center multipole-expansion)) (let ((multipole-expansion (expansion-sum (map (lambda (child) (define center-and-expansion (loop child)) (multipole-shift (pt- (car center-and-expansion) center) (cdr center-and-expansion))) (node-children node))))) (set-node-multipole-expansion! node multipole-expansion) (cons center multipole-expansion))))) ;;; Downward path of the algorithm which calculates local expansionss ;;; at every node and accelerations and potentials at each particle. (define (down! tree multipole-to-local-convert local-shift eval-local-expansion expansion-sum zero-expansion) (let loop ((node (tree-body tree)) (parent-local-expansion (zero-expansion)) (parent-center (node-center (tree-body tree)))) (let* ((center (node-center node)) (interactive-sum (expansion-sum (map (lambda (interactive) (multipole-to-local-convert (pt- (node-center interactive) center) (node-multipole-expansion interactive))) (node-interactive-field node)))) (local-expansion (expansion-sum (list (local-shift (pt- center parent-center) parent-local-expansion) interactive-sum)))) (if (leaf-node? node) (eval-potentials-and-accelerations node local-expansion eval-local-expansion) (for-each (lambda (child) (loop child local-expansion center)) (node-children node)))))) (define (eval-potentials-and-accelerations node local-expansion eval-local-expansion) (let ((center (node-center node)) (near-field (apply append (map node-particles (node-near-field node))))) (for-each (lambda (particle) (let* ((pos (particle-position particle)) (far-field-accel-and-poten (eval-local-expansion (pt- pos center) local-expansion))) (set-particle-acceleration! particle (pt+ (car far-field-accel-and-poten) (sum-vectors (map (lambda (near) (direct-accel (pt- (particle-position near) pos) (particle-strength near))) (nfilter near-field (lambda (near) (not (eq? near particle)))))))) (set-particle-potential! particle (+ (cdr far-field-accel-and-poten) (apply:+ (map (lambda (near) (direct-poten (pt- (particle-position near) pos) (particle-strength near))) (nfilter near-field (lambda (near) (not (eq? near particle)))))))))) (node-particles node)))) ;;; ================================================================ ;;; Expansion Theorems ;;; ================================================================ (define (cartesian-make-multipole-expansion pt strength) (let ((min-x (- (pt-x pt))) (min-y (- (pt-y pt))) (min-z (- (pt-z pt)))) (make-cartesian-expansion (lambda (i j k) (* strength (expt min-x i) (expt min-y j) (expt min-z k) (n1/prod-fac i j k)))))) (define (spherical-make-multipole-expansion pt strength) (let ((r (pt-r pt)) (theta (pt-theta pt)) (phi (pt-phi pt))) (make-spherical-expansion (lambda (l m) (* strength (expt r l) (eval-spherical-harmonic l (- m) theta phi)))))) ;;; ================================================================== ;;; Shifting lemmas ;;; ================================================================== ;;; Shift multipole expansion (define (cartesian-multipole-shift pt multipole-expansion) (let ((pt-expansion (cartesian-make-multipole-expansion pt 1))) (make-cartesian-expansion (lambda (i j k) (sum-3d i j k (lambda (l m n) (* (array-ref multipole-expansion l m n) (array-ref pt-expansion (- i l) (- j m) (- k n))))))))) (define (spherical-multipole-shift pt multipole-expansion) (let ((r (pt-r pt)) (theta (pt-theta pt)) (phi (pt-phi pt))) (letrec ((foo (lambda (a b) (if (< (* a b) 0) (expt -1 (min (abs a) (abs b))) 1))) (bar (lambda (a b) (/ (expt -1 a) (sqrt (* (fac (- a b)) (fac (+ a b)))))))) (let ((pt-expansion (make-spherical-expansion (lambda (l m) (* (eval-spherical-harmonic l m theta phi) (bar l m) (expt r l)))))) (make-spherical-expansion (lambda (j k) (sum-2d j (lambda (l m) (if (> (abs (- k m)) (- j l)) 0 (* (spherical-ref multipole-expansion (- j l) (- k m)) (foo m (- k m)) (bar (- j l) (- k m)) (spherical-ref pt-expansion l (- m)) (/ (bar j k)))))))))))) ;;; Convert multipole to local (define (cartesian-multipole-to-local-convert pt multipole-expansion) (define pt-expansion (let* ((n1/radius (/ (pt-r vect))) (n2cosines (pt-scalar* (* 2 n1/radius) vect)) (x (pt-x n2cosines)) (y (pt-y n2cosines)) (z (pt-z n2cosines))) (make-cartesian-expansion (lambda (i j k) (define ijk (+ i j k)) (* (expt -1 ijk) (expt n1/radius (+ 1 ijk)) (prod-fac i j k) (sum-3d (/ i 2) (/ j 2) (/ k 2) (lambda (l m n) (* (fac-1 (- ijk l m n)) (n1/prod-fac l m n) (n1/prod-fac (- i (* 2 l)) (- j (* 2 m)) (- k (* 2 n))) (expt x (- i (* 2 l))) (expt y (- j (* 2 m))) (expt z (- k (* 2 n))))))))))) (make-cartesian-expansion (lambda (i j k) (sum2-3d (- precision i j k) (lambda (l m n) (* (array-ref multipole-expansion l m n) (array-ref pt-expansion (+ i l) (+ j m) (+ k n)))))))) (define (spherical-multipole-to-local-convert pt multipole-expansion) (let* ((r (pt-r pt)) (theta (pt-theta pt)) (phi (pt-phi pt)) (foo (lambda (a b c) (* (expt -1 b) (if (> (* a c) 0) (expt -1 (min (abs a) (abs c))) 1)))) (bar (lambda (a b) (/ (expt -1 a) (sqrt (* (fac (- a b)) (fac (+ a b))))))) (pt-expansion (make-spherical-expansion (lambda (l m) (/ (eval-spherical-harmonic l m theta phi) (bar l m) (expt r (+ 1 l))))))) (make-spherical-expansion (lambda (j k) (* (bar j k) (sum-2d (- precision j 1) (lambda (l m) (* (spherical-ref multipole-expansion l m) (foo k l m) (bar l m) (spherical-ref pt-expansion (+ j l) (- m k)))))))))) ;;; Shift local expansion (define (cartesian-local-shift pt local-expansion) (let* ((x (pt-x pt)) (y (pt-y pt)) (z (pt-z pt)) (expts (make-cartesian-expansion (lambda (l m n) (* (expt x l) (expt y m) (expt z n)))))) (make-cartesian-expansion (lambda (i j k) (sum2-3d (- precision i j k) (lambda (l m n) (* (array-ref local-expansion (+ i l) (+ j m) (+ k n)) (array-ref expts l m n) (n1/prod-fac l m n)))))))) (define (spherical-local-shift pt local-expansion) (let* ((pt (pt- (make-pt 0 0 0) pt)) (r (pt-r pt)) (theta (pt-theta pt)) (phi (pt-phi pt)) (foo (lambda (a b c) (* (expt -1 a) (expt -1 (/ (+ (abs (- b c)) (abs b) (- (abs c))) 2))))) (bar (lambda (a b) (/ (expt -1 a) (sqrt (* (fac (- a b)) (fac (+ a b))))))) (stuff (make-spherical-expansion (lambda (l m) (* (eval-spherical-harmonic l m theta phi) (expt r l)))))) (make-spherical-expansion (lambda (j k) (sum2-2d j (lambda (l m) (if (> (abs (- m k)) (- l j)) 0 (* (spherical-ref local-expansion l m) (bar (- l j) (- m k)) (bar j k) (spherical-ref stuff (- l j) (- m k)) (/ (foo (- l j) (- m k) m) (bar l m)))))))))) ;;; Evaluate the resulting local expansion at point pt. (define (cartesian-eval-local-expansion pt local-expansion) (let* ((x (pt-x pt)) (y (pt-y pt)) (z (pt-z pt)) (local-expansion (make-cartesian-expansion (lambda (i j k) (* (array-ref local-expansion i j k) (n1/prod-fac i j k))))) (expts (make-cartesian-expansion (lambda (i j k) (* (expt x i) (expt y j) (expt z k)))))) (cons (make-pt (sum2-3d (- precision 1) (lambda (l m n) (* (array-ref expts l m n) (+ 1 l) (array-ref local-expansion (+ 1 l) m n)))) (sum2-3d (- precision 1) (lambda (l m n) (* (array-ref expts l m n) (+ 1 m) (array-ref local-expansion l (+ 1 m) n)))) (sum2-3d (- precision 1) (lambda (l m n) (* (array-ref expts l m n) (+ 1 n) (array-ref local-expansion l m (+ 1 n)))))) (sum2-3d precision (lambda (l m n) (* (array-ref expts l m n) (array-ref local-expansion l m n))))))) (define (spherical-eval-local-expansion pt local-expansion) (let* ((r (pt-r pt)) (x (pt-x pt)) (y (pt-y pt)) (z (pt-z pt)) (rho-sq (+ (* x x) (* y y))) (theta (pt-theta pt)) (phi (pt-phi pt)) (r-deriv (real-part (sum-2d (- precision 1) (lambda (l m) (* (spherical-ref local-expansion l m) (expt r (- l 1)) l (eval-spherical-harmonic l m theta phi)))))) (theta-deriv (real-part (sum-2d (- precision 1) (lambda (l m) (* (spherical-ref local-expansion l m) (expt r l) (eval-spher-harm-theta-deriv l m theta phi)))))) (phi-deriv (real-part (sum-2d (- precision 1) (lambda (l m) (* (spherical-ref local-expansion l m) (expt r l) (eval-spher-harm-phi-deriv l m theta phi))))))) (cons (make-pt (+ (* r-deriv (/ x r)) (* theta-deriv (/ x (sqrt rho-sq) (+ z (/ rho-sq z)))) (* phi-deriv -1 (/ y rho-sq))) (+ (* r-deriv (/ y r)) (* theta-deriv (/ y (sqrt rho-sq) (+ z (/ rho-sq z)))) (/ phi-deriv x (+ 1 (/ (* y y) x x)))) (+ (* r-deriv (/ z r)) (* theta-deriv (/ -1 r r) (sqrt rho-sq)))) (real-part (sum-2d (- precision 1) (lambda (l m) (* (spherical-ref local-expansion l m) (eval-spherical-harmonic l m theta phi) (expt r l)))))))) ;;; Direct calculation of acceleration and potential (define (direct-accel pt strength) (pt-scalar* (/ strength (expt (pt-r pt) 3)) pt)) (define (direct-poten pt strength) (/ strength (pt-r pt))) ;;; ================================================================= ;;; TREES NODES PARTICLES and POINTS ;;; ================================================================= (begin (begin (begin (define make-raw-tree (lambda (tree-1 tree-2 tree-3) (vector ' tree-1 tree-2 tree-3))) (define tree? (lambda (obj) (if (vector? obj) (if (= (vector-length obj) 4) (eq? (vector-ref obj 0) ') #f) #f))) (define tree-1 (lambda (obj) (if (tree? obj) (void) (error 'tree-1 "~s is not a ~s" obj ')) (vector-ref obj 1))) (define tree-2 (lambda (obj) (if (tree? obj) (void) (error 'tree-2 "~s is not a ~s" obj ')) (vector-ref obj 2))) (define tree-3 (lambda (obj) (if (tree? obj) (void) (error 'tree-3 "~s is not a ~s" obj ')) (vector-ref obj 3))) (define set-tree-1! (lambda (obj newval) (if (tree? obj) (void) (error 'set-tree-1! "~s is not a ~s" obj ')) (vector-set! obj 1 newval))) (define set-tree-2! (lambda (obj newval) (if (tree? obj) (void) (error 'set-tree-2! "~s is not a ~s" obj ')) (vector-set! obj 2 newval))) (define set-tree-3! (lambda (obj newval) (if (tree? obj) (void) (error 'set-tree-3! "~s is not a ~s" obj ')) (vector-set! obj 3 newval)))) (define make-tree (lambda (body low-left-front-vertex up-right-back-vertex) ((lambda () (make-raw-tree body low-left-front-vertex up-right-back-vertex))))) (define tree-body tree-1) (define tree-low-left-front-vertex tree-2) (define tree-up-right-back-vertex tree-3) (define set-tree-body! set-tree-1!) (define set-tree-low-left-front-vertex! set-tree-2!) (define set-tree-up-right-back-vertex! set-tree-3!)) (begin (begin (define make-raw-node (lambda (node-1 node-2 node-3 node-4 node-5 node-6 node-7 node-8) (vector ' node-1 node-2 node-3 node-4 node-5 node-6 node-7 node-8))) (define node? (lambda (obj) (if (vector? obj) (if (= (vector-length obj) 9) (eq? (vector-ref obj 0) ') #f) #f))) (define node-1 (lambda (obj) (if (node? obj) (void) (error 'node-1 "~s is not a ~s" obj ')) (vector-ref obj 1))) (define node-2 (lambda (obj) (if (node? obj) (void) (error 'node-2 "~s is not a ~s" obj ')) (vector-ref obj 2))) (define node-3 (lambda (obj) (if (node? obj) (void) (error 'node-3 "~s is not a ~s" obj ')) (vector-ref obj 3))) (define node-4 (lambda (obj) (if (node? obj) (void) (error 'node-4 "~s is not a ~s" obj ')) (vector-ref obj 4))) (define node-5 (lambda (obj) (if (node? obj) (void) (error 'node-5 "~s is not a ~s" obj ')) (vector-ref obj 5))) (define node-6 (lambda (obj) (if (node? obj) (void) (error 'node-6 "~s is not a ~s" obj ')) (vector-ref obj 6))) (define node-7 (lambda (obj) (if (node? obj) (void) (error 'node-7 "~s is not a ~s" obj ')) (vector-ref obj 7))) (define node-8 (lambda (obj) (if (node? obj) (void) (error 'node-8 "~s is not a ~s" obj ')) (vector-ref obj 8))) (define set-node-1! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-1! "~s is not a ~s" obj ')) (vector-set! obj 1 newval))) (define set-node-2! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-2! "~s is not a ~s" obj ')) (vector-set! obj 2 newval))) (define set-node-3! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-3! "~s is not a ~s" obj ')) (vector-set! obj 3 newval))) (define set-node-4! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-4! "~s is not a ~s" obj ')) (vector-set! obj 4 newval))) (define set-node-5! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-5! "~s is not a ~s" obj ')) (vector-set! obj 5 newval))) (define set-node-6! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-6! "~s is not a ~s" obj ')) (vector-set! obj 6 newval))) (define set-node-7! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-7! "~s is not a ~s" obj ')) (vector-set! obj 7 newval))) (define set-node-8! (lambda (obj newval) (if (node? obj) (void) (error 'set-node-8! "~s is not a ~s" obj ')) (vector-set! obj 8 newval)))) (define make-node (lambda (center low-left-front-vertex up-right-back-vertex children particles multipole-expansion near-field interactive-field) ((lambda () (make-raw-node center low-left-front-vertex up-right-back-vertex children particles multipole-expansion near-field interactive-field))))) (define node-center node-1) (define node-low-left-front-vertex node-2) (define node-up-right-back-vertex node-3) (define node-children node-4) (define node-particles node-5) (define node-multipole-expansion node-6) (define node-near-field node-7) (define node-interactive-field node-8) (define set-node-center! set-node-1!) (define set-node-low-left-front-vertex! set-node-2!) (define set-node-up-right-back-vertex! set-node-3!) (define set-node-children! set-node-4!) (define set-node-particles! set-node-5!) (define set-node-multipole-expansion! set-node-6!) (define set-node-near-field! set-node-7!) (define set-node-interactive-field! set-node-8!)) (define leaf-node? (lambda (node) (null? (node-children node)))) (begin (begin (define make-raw-particle (lambda (particle-1 particle-2 particle-3 particle-4 particle-5 particle-6) (vector ' particle-1 particle-2 particle-3 particle-4 particle-5 particle-6))) (define particle? (lambda (obj) (if (vector? obj) (if (= (vector-length obj) 7) (eq? (vector-ref obj 0) ') #f) #f))) (define particle-1 (lambda (obj) (if (particle? obj) (void) (error 'particle-1 "~s is not a ~s" obj ')) (vector-ref obj 1))) (define particle-2 (lambda (obj) (if (particle? obj) (void) (error 'particle-2 "~s is not a ~s" obj ')) (vector-ref obj 2))) (define particle-3 (lambda (obj) (if (particle? obj) (void) (error 'particle-3 "~s is not a ~s" obj ')) (vector-ref obj 3))) (define particle-4 (lambda (obj) (if (particle? obj) (void) (error 'particle-4 "~s is not a ~s" obj ')) (vector-ref obj 4))) (define particle-5 (lambda (obj) (if (particle? obj) (void) (error 'particle-5 "~s is not a ~s" obj ')) (vector-ref obj 5))) (define particle-6 (lambda (obj) (if (particle? obj) (void) (error 'particle-6 "~s is not a ~s" obj ')) (vector-ref obj 6))) (define set-particle-1! (lambda (obj newval) (if (particle? obj) (void) (error 'set-particle-1! "~s is not a ~s" obj ')) (vector-set! obj 1 newval))) (define set-particle-2! (lambda (obj newval) (if (particle? obj) (void) (error 'set-particle-2! "~s is not a ~s" obj ')) (vector-set! obj 2 newval))) (define set-particle-3! (lambda (obj newval) (if (particle? obj) (void) (error 'set-particle-3! "~s is not a ~s" obj ')) (vector-set! obj 3 newval))) (define set-particle-4! (lambda (obj newval) (if (particle? obj) (void) (error 'set-particle-4! "~s is not a ~s" obj ')) (vector-set! obj 4 newval))) (define set-particle-5! (lambda (obj newval) (if (particle? obj) (void) (error 'set-particle-5! "~s is not a ~s" obj ')) (vector-set! obj 5 newval))) (define set-particle-6! (lambda (obj newval) (if (particle? obj) (void) (error 'set-particle-6! "~s is not a ~s" obj ')) (vector-set! obj 6 newval)))) (define make-particle (lambda (position acceleration d-acceleration potential d-potential strength) ((lambda () (make-raw-particle position acceleration d-acceleration potential d-potential strength))))) (define particle-position particle-1) (define particle-acceleration particle-2) (define particle-d-acceleration particle-3) (define particle-potential particle-4) (define particle-d-potential particle-5) (define particle-strength particle-6) (define set-particle-position! set-particle-1!) (define set-particle-acceleration! set-particle-2!) (define set-particle-d-acceleration! set-particle-3!) (define set-particle-potential! set-particle-4!) (define set-particle-d-potential! set-particle-5!) (define set-particle-strength! set-particle-6!)) (begin (begin (define make-raw-pt (lambda (pt-1 pt-2 pt-3) (vector ' pt-1 pt-2 pt-3))) (define pt? (lambda (obj) (if (vector? obj) (if (= (vector-length obj) 4) (eq? (vector-ref obj 0) ') #f) #f))) (define pt-1 (lambda (obj) (if (pt? obj) (void) (error 'pt-1 "~s is not a ~s" obj ')) (vector-ref obj 1))) (define pt-2 (lambda (obj) (if (pt? obj) (void) (error 'pt-2 "~s is not a ~s" obj ')) (vector-ref obj 2))) (define pt-3 (lambda (obj) (if (pt? obj) (void) (error 'pt-3 "~s is not a ~s" obj ')) (vector-ref obj 3))) (define set-pt-1! (lambda (obj newval) (if (pt? obj) (void) (error 'set-pt-1! "~s is not a ~s" obj ')) (vector-set! obj 1 newval))) (define set-pt-2! (lambda (obj newval) (if (pt? obj) (void) (error 'set-pt-2! "~s is not a ~s" obj ')) (vector-set! obj 2 newval))) (define set-pt-3! (lambda (obj newval) (if (pt? obj) (void) (error 'set-pt-3! "~s is not a ~s" obj ')) (vector-set! obj 3 newval)))) (define make-pt (lambda (x y z) ((lambda () (make-raw-pt x y z))))) (define pt-x pt-1) (define pt-y pt-2) (define pt-z pt-3) (define set-pt-x! set-pt-1!) (define set-pt-y! set-pt-2!) (define set-pt-z! set-pt-3!))) ;(define-structure (tree ; body ; low-left-front-vertex ; up-right-back-vertex)) ; ;(define-structure (node ; center ; low-left-front-vertex ; up-right-back-vertex ; children ; particles ; multipole-expansion ; near-field ; interactive-field)) ; ;(define (leaf-node? node) ; (null? (node-children node))) ; ;(define-structure (particle ; position ; acceleration ; d-acceleration ; potential ; d-potential ; strength)) ; ;(define-structure (pt x y z)) ; (define (pt-r pt) (sqrt (+ (* (pt-x pt) (pt-x pt)) (* (pt-y pt) (pt-y pt)) (* (pt-z pt) (pt-z pt))))) (define (pt-theta pt) (let ((x (pt-x pt)) (y (pt-y pt)) (z (pt-z pt))) (atan0 (sqrt (+ (* x x) (* y y))) z))) (define (pt-phi pt) (let ((x (pt-x pt)) (y (pt-y pt))) (atan0 y x))) (define (pt+ pt1 pt2) (make-pt (+ (pt-x pt1) (pt-x pt2)) (+ (pt-y pt1) (pt-y pt2)) (+ (pt-z pt1) (pt-z pt2)))) (define (sum-vectors vectors) (make-pt (apply:+ (map pt-x vectors)) (apply:+ (map pt-y vectors)) (apply:+ (map pt-z vectors)))) (define (pt- pt1 pt2) (make-pt (- (pt-x pt1) (pt-x pt2)) (- (pt-y pt1) (pt-y pt2)) (- (pt-z pt1) (pt-z pt2)))) (define (pt-average pt1 pt2) (pt-scalar* .5 (pt+ pt1 pt2))) (define (pt-scalar* scalar pt) (make-pt (* scalar (pt-x pt)) (* scalar (pt-y pt)) (* scalar (pt-z pt)))) (define (within-box? pt pt1 pt2) (and (<= (pt-x pt) (pt-x pt2)) (> (pt-x pt) (pt-x pt1)) (<= (pt-y pt) (pt-y pt2)) (> (pt-y pt) (pt-y pt1)) (<= (pt-z pt) (pt-z pt2)) (> (pt-z pt) (pt-z pt1)))) ;;; ========================================================== ;;; Useful Things ;;; ========================================================== (define (nfilter list predicate) (let loop ((list list)) (cond ((null? list) '()) ((predicate (car list)) (cons (car list) (loop (cdr list)))) (else (loop (cdr list)))))) ;;; array in the shape of a pyramid with each ;;; element a function of the indices (define (make-cartesian-expansion func) (let ((expansion (make-vector precision 0))) (let loop1 ((i 0)) (if (= i precision) expansion (let ((foo (make-vector (- precision i) 0))) (vector-set! expansion i foo) (let loop2 ((j 0)) (if (= j (- precision i)) (loop1 (+ 1 i)) (let ((bar (make-vector (- precision i j) 0))) (vector-set! foo j bar) (let loop3 ((k 0)) (if (= k (- precision i j)) (loop2 (+ 1 j)) (begin (vector-set! bar k (func i j k)) (loop3 (+ 1 k))))))))))))) ;;; array in the shape of a triangle with each ;;; element a function of the indices (define (make-spherical-expansion func) (let ((expansion (make-vector precision 0))) (let loop1 ((l 0)) (if (= l precision) expansion (let ((foo (make-vector (+ 1 l) 0))) (vector-set! expansion l foo) (let loop2 ((m 0)) (if (= m (+ 1 l)) (loop1 (+ 1 l)) (begin (vector-set! foo m (func l m)) (loop2 (+ 1 m)))))))))) (define (spherical-ref expansion l m) (let ((conj (lambda (z) (make-rectangular (real-part z) (- (imag-part z)))))) (if (negative? m) (conj (array-ref expansion l (- m))) (array-ref expansion l m)))) (define (cartesian-expansion-sum expansions) (make-cartesian-expansion (lambda (i j k) (apply:+ (map (lambda (expansion) (array-ref expansion i j k)) expansions))))) (define (spherical-expansion-sum expansions) (make-spherical-expansion (lambda (l m) (apply:+ (map (lambda (expansion) (spherical-ref expansion l m)) expansions))))) (define (cartesian-zero-expansion) (make-cartesian-expansion (lambda (i j k) 0))) (define (spherical-zero-expansion) (make-spherical-expansion (lambda (l m) 0))) (define (sum-3d end1 end2 end3 func) (let loop1 ((l 0) (sum 0)) (if (> l end1) sum (loop1 (+ 1 l) (+ sum (let loop2 ((m 0) (sum 0)) (if (> m end2) sum (loop2 (+ 1 m) (+ sum (let loop3 ((n 0) (sum 0)) (if (> n end3) sum (loop3 (+ 1 n) (+ sum (func l m n)))))))))))))) (define (sum2-3d end func) (let loop1 ((l 0) (sum 0)) (if (= l end) sum (loop1 (+ 1 l) (+ sum (let loop2 ((m 0) (sum 0)) (if (= (+ l m) end) sum (loop2 (+ 1 m) (+ sum (let loop3 ((n 0) (sum 0)) (if (= (+ l m n) end) sum (loop3 (+ 1 n) (+ sum (func l m n)))))))))))))) (define (sum-2d end func) (let loop1 ((l 0) (sum 0)) (if (> l end) sum (loop1 (+ 1 l) (+ sum (let loop2 ((m (- l)) (sum 0)) (if (> m l) sum (loop2 (+ 1 m) (+ sum (func l m)))))))))) (define (sum2-2d init func) (let loop1 ((l init) (sum 0)) (if (= l precision) sum (loop1 (+ 1 l) (+ sum (let loop2 ((m (- l)) (sum 0)) (if (> m l) sum (loop2 (+ 1 m) (+ sum (func l m)))))))))) ;;; Storing factorials in a table (define fac (let ((table (make-vector (* 4 precision) 0))) (vector-set! table 0 1) (let loop ((n 1)) (if (= n (* 4 precision)) (lambda (x) (vector-ref table x)) (begin (vector-set! table n (* n (vector-ref table (- n 1)))) (loop (+ 1 n))))))) ;;; The table for (* (-0.5) (-1.5) (-2.5) ... (+ -0.5 -i 1)) (define fac-1 (let ((table (make-vector precision 0))) (vector-set! table 0 1) (let loop ((n 1)) (if (= n precision) (lambda (x) (vector-ref table x)) (begin (vector-set! table n (* (- .5 n) (vector-ref table (- n 1)))) (loop (+ 1 n))))))) (define fac-2 (let ((table (make-vector (* 4 precision) 0))) (vector-set! table 0 1) (let loop ((n 1)) (if (= n (* 4 precision)) (lambda (n) (if (< n 0) 1 (vector-ref table n))) (begin (vector-set! table n (* (if (even? n) 1 n) (vector-ref table (- n 1)))) (loop (+ 1 n))))))) ;;; Storing the products of factorials in a table. (define prod-fac (let ((table (make-cartesian-expansion (lambda (i j k) (* (fac i) (fac j) (fac k)))))) (lambda (i j k) (array-ref table i j k)))) (define n1/prod-fac (let ((table (make-cartesian-expansion (lambda (i j k) (/ (prod-fac i j k)))))) (lambda (i j k) (array-ref table i j k)))) (define (assoc-legendre l m x) (cond ((= l m) (* (expt -1 m) (fac-2 (- (* 2 m) 1)) (expt (- 1 (* x x)) (/ m 2)))) ((= l (+ 1 m)) (* x (+ 1 (* 2 m)) (assoc-legendre m m x))) (else (/ (- (* x (- (* 2 l) 1) (assoc-legendre (- l 1) m x)) (* (+ l m -1) (assoc-legendre (- l 2) m x))) (- l m))))) (define (eval-spherical-harmonic l m theta phi) (let ((mm (abs m))) (* (sqrt (/ (fac (- l mm)) (fac (+ l mm)))) (assoc-legendre l mm (cos theta)) (make-polar 1 (* m phi))))) (define (eval-spher-harm-phi-deriv l m theta phi) (* (eval-spherical-harmonic l m theta phi) m (make-rectangular 0 1))) (define (eval-spher-harm-theta-deriv l m theta phi) (let ((mm (abs m))) (* (sqrt (/ (fac (- l mm)) (fac (+ l mm)))) (make-polar 1 (* m phi)) (- (sin theta)) (assoc-legendre-deriv l mm (cos theta))))) (define (assoc-legendre-deriv l m x) (cond ((= l m) (* (expt -1 (+ 1 m)) (fac-2 (- (* 2 m) 1)) m (expt (- 1 (* x x)) (- (/ m 2) 1)) x)) ((= l (+ 1 m)) (* (+ 1 (* 2 m)) (+ (assoc-legendre m m x) (* x (assoc-legendre-deriv m m x))))) (else (/ (- (* (- (* 2 l) 1) (+ (assoc-legendre (- l 1) m x) (* x (assoc-legendre-deriv (- l 1) m x)))) (* (+ l m -1) (assoc-legendre-deriv (- l 2) m x))) (- l m))))) ;;; ================================================================ ;;; TREE CODE ;;; ================================================================ (define (build-tree height near-size) (let* ((vertex1 (make-pt -10 -10 -10)) (vertex2 (make-pt 10 10 10)) (tree (make-tree '() vertex1 vertex2))) (let loop ((level 0) (pt1 vertex1) (pt2 vertex2)) (let* ((half-diagonal (pt-scalar* .5 (pt- pt2 pt1))) (diag-length/2 (pt-x half-diagonal))) (insert-node tree level pt1 pt2) (if (< level height) (let ((child-pt1s (map (lambda (offset) (pt+ pt1 (pt-scalar* diag-length/2 offset))) (list (make-pt 0 0 0) (make-pt 0 0 1) (make-pt 0 1 0) (make-pt 1 0 0) (make-pt 0 1 1) (make-pt 1 0 1) (make-pt 1 1 0) (make-pt 1 1 1))))) (for-each (lambda (child-pt1) (loop (+ 1 level) child-pt1 (pt+ child-pt1 half-diagonal))) child-pt1s))))) (calc-near-and-interaction tree near-size) tree)) (define (insert-node tree level pt1 pt2) (let* ((center (pt-average pt1 pt2)) (new-node (make-node center pt1 pt2 '() '() '() '() '()))) (letrec ((insert-internal (lambda (node depth) (if (= level depth) (set-node-children! node (cons new-node (node-children node))) (insert-internal (find-child node center) (+ 1 depth)))))) (if (= level 0) (set-tree-body! tree new-node) (insert-internal (tree-body tree) 1))))) (define (find-child node pos) (let loop ((children (node-children node))) (let ((child (car children))) (if (within-box? pos (node-low-left-front-vertex child) (node-up-right-back-vertex child)) child (loop (cdr children)))))) (define (insert-particle tree particle) (let* ((pos (particle-position particle))) (letrec ((insert-internal (lambda (node) (if (leaf-node? node) (set-node-particles! node (cons particle (node-particles node))) (insert-internal (find-child node pos)))))) (if (within-box? pos (tree-low-left-front-vertex tree) (tree-up-right-back-vertex tree)) (insert-internal (tree-body tree)) (error 'insert-particle "particle not within boundaries of tree" particle))))) ;;; This function finds the near and ;;; interaction fields for every node in the tree. (define (calc-near-and-interaction tree near-size) (set-node-near-field! (tree-body tree) (list (tree-body tree))) (let loop ((node (tree-body tree)) (parent #f)) (if parent (let* ((center (node-center node)) (dist (* near-size (abs (- (pt-x center) (pt-x (node-center parent))))))) (for-each (lambda (parent-near) (let ((interactives (list '()))) (for-each (lambda (child) (if (> (pt-r (pt- center (node-center child))) dist) (set-car! interactives (cons child (car interactives))) (set-node-near-field! node (cons child (node-near-field node))))) (node-children parent-near)) (set-node-interactive-field! node (append (car interactives) (node-interactive-field node))))) (node-near-field parent)))) (for-each (lambda (child) (loop child node)) (node-children node)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; GO (define (initial-particle x y z m) (make-particle (make-pt x y z) (make-pt 0 0 0) (make-pt 0 0 0) 0 0 m)) (define (random-float bot top) (+ (* (- top bot) (/ (* (random 1000000) 1.0) 1000000.0)) bot)) (define (random-particle) (make-particle (make-pt (random-float -10.0 10.0) (random-float -10.0 10.0) (random-float -10.0 10.0)) (make-pt 0 0 0) (make-pt 0 0 0) 0 0 1.0)) (define *particles* (list '())) (define (go depth precision n-particles) (let ((tree (build-tree depth 27)) (particles (let next ((i 0) (ps '())) (if (<= i n-particles) (next (+ i 1) (cons (random-particle) ps)) ps)))) (for-each (lambda (p) (insert-particle tree p)) particles) (cartesian-algorithm tree) (set-car! *particles* particles))) ;;; virtual time for cartesian-algorithm step ;;; (go 1 3 10) 0.31 seconds ;;; (go 3 5 128) 1397.31 ;;; (go 3 5 256) 1625.29 ;;; (go 3 5 512) 2380.35 ;;; (go 2 5 128) 27.44 seconds (define (main . args) (run-benchmark "nbody" nbody-iters (lambda () #t) (lambda (i j k) (go i j k)) 2 5 128)))