Code Feedback Wanted (Generating more garbage)
From: David Steuber (david.steuber_at_verizon.net)
Date: 03/24/04
- Next message: Tim Bradshaw: "Re: Ye Old Time Sharing System"
- Previous message: Christophe Turle: "Re: PHP"
- Next in thread: David Sletten: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: David Sletten: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: Brian Downing: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: Kenny Tilton: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: Kenny Tilton: "Re: Code Feedback Wanted (Generating more garbage)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 24 Mar 2004 10:57:22 GMT
I've done a bit more hacking on my Lisp code for generating the
Mandelbrot Set. I've not been working on it constantly, that's why
I'm not further along than might be expected from the last time I
posted. I'm not using rationals anymore. I'm going with ints as a
way to do fixed radix math. I've not implemented certain features
yet like adjusting the number of bits used based on zoom factor. I'm
also doing a fairly brute-force approach to calculating the pixels.
I am considering using an array to hold the escape value data so that
I can do edge detection on the set so that I don't have to calculate
pixels in the interior. Anyway, here is the Lisp that I have:
;;;; fractal.lisp -- an exploration of the Mandelbrot Set using
;;;; arbitrary sized integer math.
;;;;
;;;; By David Steuber with feedback from comp.lang.lisp
;;;;
;;;; This software is put in the public domain by David Steuber
;;;;
;;;; Just for the record, I hate global variables because they
;;;; break modularity by being non-local.
;;; global variables related to the fractional bits in our fixed radix numbers
(defvar *fractional-bits* 16
"Number of fractional bits in our number")
(defvar *escape-value* (ash 4 *fractional-bits*)
"Numbers larger than this have escaped and are not part of the Mandelbrot Set")
(defvar *my-two* (ash 2 *fractional-bits*))
(defun set-fractional-bits (fb)
"A nice way to set both *fractional-bits* and *escape-value* at the same time"
(setf *escape-value* (ash 4 fb)
*my-two* (ash 2 fb)
*fractional-bits* fb))
(defun shifted-multiply (&rest args)
"Multiplies the args as integers left-shifted by *fractional-bits* and right-shifts accordingly"
(let ((n (apply #'* args)))
(ash n (- (* (max 0 (- (list-length args) 1)) *fractional-bits*)))))
(defun mandelbrot-escape-fn (c-real c-imag &key (max-iterations 10000))
"Return iterations for c-real + c-imag i as integers left shifted by *fractional-bits*"
(loop for iterations below max-iterations
for z-imag = 0 then (+ (shifted-multiply z-real z-imag *my-two*) c-imag)
for z-real = 0 then (+ (- z-real-squared z-imag-squared) c-real)
for z-real-squared = 0 then (shifted-multiply z-real z-real)
for z-imag-squared = 0 then (shifted-multiply z-imag z-imag)
unless (< (+ z-real-squared z-imag-squared) *escape-value*) return iterations
finally (return iterations)))
(defun mandelbrot-escape-fn-pc (c-real c-imag &key (max-iterations 10000))
"Return iterations for c-real + c-imag i as integers left shifted by *fractional-bits* doing period checking"
(loop for iterations below max-iterations
for period from 1
for z-imag = 0 then (+ (shifted-multiply z-real z-imag *my-two*) c-imag)
for z-real = 0 then (+ (- z-real-squared z-imag-squared) c-real)
for z-real-squared = 0 then (shifted-multiply z-real z-real)
for z-imag-squared = 0 then (shifted-multiply z-imag z-imag)
with a-imag = 0
with a-real = 0
with check = 3
unless (< (+ z-real-squared z-imag-squared) *escape-value*) return iterations
when (and (= a-imag z-imag) (= a-real z-real) (> iterations 1)) return max-iterations
when (> period check) do (setf period 1 check (* 2 check) a-imag z-imag a-real z-real)
finally (return iterations)))
(defun numbers-to-fixed-radix (&rest nums)
"Return list of numbers converted to fixed-radix integers with *fractional-bits*"
(mapcar #'(lambda (n)
(round (* n (ash 1 *fractional-bits*))))
nums))
(defun fixed-radix-to-numbers (&rest nums)
"Almost the inverse of numbers-to-fixed-radix"
(mapcar #'(lambda (n)
(/ n (ash 1 *fractional-bits*)))
nums))
(defun mandelbrot-set (&key (x 0) (y 0) (zoom 1) (aspect 1)
(bitmap-width 100) (bitmap-height 100)
(data-file-name "mandelbrot-data.txt")
(max-iterations 1000))
"Calculate bitmap data for the Mandelbrot Set"
(let ((real-array (make-array bitmap-width)) ; to contain the real values left to right
(imag-array (make-array bitmap-height)) ; to contain the imag values top to bottom
(escape-fn #'mandelbrot-escape-fn) ; escape function to use
(data-file (make-pathname :name data-file-name))
(iterations 0) ; just another local variable
(total-iterations 0)) ; useful to know?
;; initialize real-array
(loop for i below bitmap-width
with size = (/ (* 4 aspect) zoom)
with pixelsize = (/ size bitmap-width)
with start = (- x (* (/ bitmap-width 2) pixelsize))
do (setf (svref real-array i) (car (numbers-to-fixed-radix (+ start (* i pixelsize))))))
;; initialize imag-array
(loop for i below bitmap-height
with size = (/ 4 zoom)
with pixelsize = (/ size bitmap-height)
with start = (- y (* (/ bitmap-height 2) pixelsize))
do (setf (svref imag-array i) (car (numbers-to-fixed-radix (+ start (* i pixelsize)))))
finally (nreverse imag-array)) ; this is so the first element is on the top row
;; we will start by doing this the brute force way and calculate every damn pixel
(with-open-file (stream data-file :direction :output
:if-exists :supersede)
(format stream "/* FRACTAL MANDELBROT */~%/* left top right bottom */~%") ; old format for now
(format stream "~A ~A ~A ~A~%"
(svref real-array 0) (svref imag-array 0)
(svref real-array (1- bitmap-width)) (svref imag-array (1- bitmap-height)))
(format stream "/* center_x center_y aspect zoom */~%")
(format stream "~A ~A ~A ~A~%" x y aspect zoom)
(format stream "/* width height maxitt */~%~A ~A ~A~%/* data */~%" bitmap-width bitmap-height max-iterations)
(dotimes (j bitmap-height)
(dotimes (i bitmap-width)
(setf iterations (funcall escape-fn (svref real-array i) (svref imag-array j) :max-iterations max-iterations))
(if (= iterations max-iterations)
(setf escape-fn #'mandelbrot-escape-fn-pc)
(setf escape-fn #'mandelbrot-escape-fn))
(format stream "~A " iterations)
(incf total-iterations iterations))
(format stream "~%")))
total-iterations))
Here is a test run:
CL-USER> (time (mandelbrot-set :x -3/4 :zoom 2 :bitmap-width 640 :bitmap-height 640))
(MANDELBROT-SET :X -3/4 :ZOOM 2 :BITMAP-WIDTH 640 :BITMAP-HEIGHT 640) took 126,626 milliseconds (126.626 seconds) to run.
Of that, 87,830 milliseconds (87.830 seconds) were spent in user mode
32,520 milliseconds (32.520 seconds) were spent in system mode
6,276 milliseconds (6.276 seconds) were spent executing other OS processes.
24,051 milliseconds (24.051 seconds) was spent in GC.
2,252,959,120 bytes of memory allocated.
145962887
By running this Perl script on the data written out
http://www.david-steuber.com/~david/files/fractal2image.txt
I generate an XPM file that I converted to this GIF file with Graphic
Converter:
http://www.david-steuber.com/~david/files/mandelbrot3.gif
This was all done on a PowerBook G4 12" with the 866Mhz G4 and 640MB
RAM.
--
It would not be too unfair to any language to refer to Java as a
stripped down Lisp or Smalltalk with a C syntax.
--- Ken Anderson
http://openmap.bbn.com/~kanderso/performance/java/index.html
- Next message: Tim Bradshaw: "Re: Ye Old Time Sharing System"
- Previous message: Christophe Turle: "Re: PHP"
- Next in thread: David Sletten: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: David Sletten: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: Brian Downing: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: Kenny Tilton: "Re: Code Feedback Wanted (Generating more garbage)"
- Reply: Kenny Tilton: "Re: Code Feedback Wanted (Generating more garbage)"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|