**hp2pretty** is a program to graph heap profiles output by
Haskell programs compiled by GHC. It makes images like this by default:

Today I hacked on it some more and added some new features, like `--reverse`

to switch the order of the bands:

and `--sort=stddev`

to sort by band standard deviation:

and `--sort=name`

to sort by cost center name:

and `--trace=50`

to combine the last percentage of trace elements into one band:

and `--bands=5`

to show only a certain number of bands:

and the icing on the cake, `--pattern`

to use pattern fills for low ink printing:

Here's the `--help`

output, using the optparse-applicative package
for command line arguments:

hp2pretty - generate pretty graphs from heap profiles Usage: hp2pretty [--uniform-scale AXES] [--sort FIELD] [--reverse] [--trace PERCENT] [--bands COUNT] [--pattern] FILES... Convert heap profile FILES.hp to pretty graphs FILES.svg Available options: --uniform-scale AXES Whether to use a uniform scale for all outputs. One of: none (default), time, memory, both. --sort FIELD How to sort the bands. One of: size (default), stddev, name. --reverse Reverse the order of bands. --trace PERCENT Percentage of trace elements to combine. (default: 1.0) --bands COUNT Maximum number of bands to draw (0 for unlimited). (default: 15) --pattern Use patterns instead of solid colours to fill bands. FILES... Heap profiles (FILE.hp will be converted to FILE.svg). -h,--help Show this help text

Version 0.7 was also released this week, featuring a contributed bugfix in parsing. You can get the latest from git here:

git clone https://code.mathr.co.uk/hp2pretty.git

or install from Hackage.

]]>Another year has arrived! 2017 is prime, so it occurs a lot in the Online Encyclopedia of Integer Sequences. I visualized one that isn't dependent on its primality: A134169. Haskell code below:

{-# LANGUAGE FlexibleContexts #-} import Prelude hiding (null) import Data.Bits (bit, finiteBitSize, testBit, (.&.)) import Data.Set (Set) import qualified Data.Set as S import Diagrams.Prelude hiding (intersection) import Diagrams.Backend.PGF.CmdLine (B, defaultMain) type Z = Int type S = Int type P = Set S intersection :: S -> S -> S intersection = (.&.) isSubsetOf :: S -> S -> Bool x `isSubsetOf` y = (x `intersection` y) == x isProperSubsetOf :: S -> S -> Bool x `isProperSubsetOf` y = (x `isSubsetOf` y) && x /= y null :: S -> Bool null x = x == 0 member :: Z -> S -> Bool member i x = testBit x i toList :: S -> [Z] toList x = [ i | i <- [0 .. finiteBitSize x - 1], i `member` x ] nset :: Z -> S nset n = bit n - 1 npower :: Z -> P npower n = S.fromList [0 .. bit n - 1] data T = A | B | C | D t :: S -> S -> Maybe T t x y | y > x = Nothing | null (x `intersection` y) && not (x `isSubsetOf` y) && not (y `isSubsetOf` x) = Just A | not (null (x `intersection` y)) && not (x `isSubsetOf` y) && not (y `isSubsetOf` x) = Just B | not (null (x `intersection` y)) && ((x `isProperSubsetOf` y) || (y `isProperSubsetOf` x)) = Just C | x == y = Just D | otherwise = Nothing label is x = [ square 2 # strokeP # lc black # fc (if i `member` x then black else white) # pad 2 | i <- is ] xlabel s x = vcat $ label (reverse $ toList s) x ylabel s y = hcat $ label ( toList s) y withEnvelope' :: Diagram B -> Diagram B -> Diagram B withEnvelope' = withEnvelope cell :: Maybe T -> Diagram B cell Nothing = withEnvelope' (square 2) mempty cell (Just A) = circle 1 # strokeP # lc red cell (Just B) = triangle 2 # centerXY # strokeP # lc green cell (Just C) = square 2 # strokeP # lc magenta cell (Just D) = (p2 (-1, -1) ~~ p2 (1, 1) `atop` p2 (1, -1) ~~ p2 (-1, 1)) # lc blue diagram n = lwL 0.25 . vcat $ ( hcat $ (++[withEnvelope' (ylabel s 0) mempty]) [ xlabel s x | x <- S.toList p ] ) : [ hcat $ (++[ylabel s y]) [ cell (t x y) # pad 2 | x <- S.toList p ] | y <- S.toList p ] where p = npower n s = nset n key a b c d = vcat [ cell (Just D) # pad 2 ||| d , cell (Just A) # pad 2 ||| a , cell (Just B) # pad 2 ||| b , cell (Just C) # pad 2 ||| c ] # scale 8 txt = alignedText 0 0.5 main1 :: Z -> IO () main1 n = defaultMain $ let a = txt "$ x \\cap y = \\emptyset \\wedge x \\not\\subseteq y \\wedge x \\not\\supseteq y $" b = txt "$ x \\cap y \\neq \\emptyset \\wedge x \\not\\subseteq y \\wedge x \\not\\supseteq y $" c = txt "$ x \\cap y \\neq \\emptyset \\wedge \\left( x \\subset y \\vee x \\supset y \\right) $" d = txt "$ x = y $" m = 2^(n - 1) * (2^n - 1) + 1 count = txt $ "$ " ++ show m ++ " $" oeis = alignedText 0 0 "\\phantom{.} OEIS / A134169" in bg white . pad 1.1 . centerXY $ alignBR (alignBL (diagram n # centerXY) `atop` alignBL (key a b c d # centerXY) === alignTL ((strutY 1.1 ||| count) # bold # scale 96)) `atop` alignBR (rotate (90 @@ deg) (oeis # bold # scale 8)) main :: IO () main = main1 6

So, plans for the year ahead? I guess "continuity" sums it up - working on my projects, improving them, maybe finding cross-links between them. Playing around researching new things too. And peforming and exhibiting and presenting when opportunities arise. Concretely, here are a few project ideas:

- cca
- Blog about coupled cellular automata experiments, explore potential links with RDEX and BitBreeder projects, possible sonification.
- clive
- Look into the possibilities of cross-compilation and upload to Bela or other similar low-latency audio platform.
- cmcms
- Make year-rollover automatic instead of manual.
- graphgrow
- Blog about the updated Haskell GUI (GTK, OpenGL) with its Pd sonification. Think about how to present it as an installation, perhaps with touch screen. Also think about live performance possibilities.
- hgmp
- Battle-test my low-level Haskell bindings to GMP by writing FFI wrappers for my Mandelbrot-related C code. Will also require writing similar low-level bindings to MPFR, but which Haskell MPFR library to choose?
- incidents
- Try to make videos for another track or two at least. Blog about recent updates to the visualisation of the inflatable torus physics demo.
- mandelbrot-book
- Continue work on the reboot. Blog about it.
- mandelbrot-*
- Add new algorithms as I uncover them, document the existing code (with references to papers where possible), more examples.
- mightymandel
- Figure out how to merge with mandelbrot-perturbator.
- monotone
- Continue hacking at the code trying to speed it up enough to run in realtime on my hardware. Figure out how to profile the bottlenecks.
- pool-party
- Document and announce this water simulation demo.
- tilda
- Find a project or two for the EMFCamp 2016 badge.
- unarchive
- Blog about the recent bug-fixes to my Internet Archive downloader.

Interlocking space filling curves - in the limit the perimeter between adjacent blocks becomes infinite, like a perfect lung for transferring oxygen from air to blood and carbon dioxide in the other direction. Made with the Haskell Diagrams library:

]]>{-# LANGUAGE TypeFamilies, FlexibleContexts #-} import Diagrams.Prelude hiding (E) import Diagrams.Backend.SVG.CmdLine (defaultMain) import Control.Monad.State (state, evalState) import Data.Colour.SRGB (sRGB24) data H = N | E | S | W data T = L | R step L = [L,R,L] step R = [L,R,R,R,L] seed = [R,R,R,R] levels = iterate (concatMap step) seed heading N L = W heading E L = N heading S L = E heading W L = S heading N R = E heading E R = S heading S R = W heading W R = N angle N = 90 @@ deg angle E = 0 @@ deg angle S = -90 @@ deg angle W = 180 @@ deg draw d h = (arcD d a a', h') where a = angleDir $ angle h a' = case d of L -> 90 @@ deg R -> -90 @@ deg h' = heading h d arcD L a b = arc a b arcD R a b = scale (-1) $ arcCW a (rotate b a) draws = mconcat . (`evalState` E) . mapM (state . draw) base = draws (levels !! level) # closeTrail # pathFromTrail # translate (r2 (-1, 0)) # stroke left = base middle = base # scale (sqrt 0.5) # rotate (45 @@ deg) # translateX (2 ^ (level + 1)) right = base # scale 0.5 # translateX (2 ^ level * 3) # translateY (2 ^ level) four = base # scale (sqrt 0.125) # rotate (45 @@ deg) # translateX (3 * 2 ^ level) # translateY (2 ^ (level + 1)) lung = (left # fc r <> middle # fc y <> right # fc g <> four # fc b) # rotate ((atan 5 :: Double) @@ rad) # centerXY # pad 1.01 # lc black # lw 0.1 # bg white # rotate (-90 @@ deg) y = sRGB24 0xff 0xdf 0x80 r = sRGB24 0xff 0x88 0xaa b = sRGB24 0xaa 0xaa 0xff g = sRGB24 0x88 0xff 0xaa level = 4 main = defaultMain lung

A000129 Pell numbers

(0, 1, 2, 5, 12, 29,70, 169, 408, 985, ...)

Number of lattice paths from (0,0) to the line x=n-1 consisting of U=(1,1), D=(1,-1) and H=(2,0) steps; for example, a(3)=5, counting the paths H, UD, UU, DU and DD.-- Emeric Deutsch

{-# LANGUAGE FlexibleContexts #-} import Diagrams.Prelude import Diagrams.Backend.SVG.CmdLine (B, defaultMain) import Control.Monad (replicateM) import Data.List (sort, transpose) import Data.List.Split (chunksOf) u, d, h, z :: (Int, Int) u = (1, 1) d = (1, -1) h = (2, 0) z = (0, 0) add (a, b) (c, d) = (a + c, b + d) v :: (Int, Int) -> V2 Double v (a, b) = V2 (fromIntegral a) (fromIntegral b) vs = map v . scanl add z l = fst . foldr add z paths n = [ q | m <- [0..n] , q <- replicateM m [u,d,h] , l q == n ] draw n q = frame 0.5 . (`atop` centerXY (strutY (fromIntegral n))) . centerXY $ mconcat [ circle 0.25 # fc white # translate pq # lw thin | pq <- vs q ] `atop` strokeT (trailFromOffsets (map v q)) grid = vcat . map hcat diagram n m = bg white . centerXY . grid . transpose . chunksOf m . map (draw n) . sort $ paths n main = defaultMain (diagram 5 10)

A000332 Binomial coefficient (n,4)

(0, 0, 0, 0, 1, 5, 15, 35,70, 126, 210, 330, 495, 715, ...)

Number of equilateral triangles with vertices in an equilateral triangular array of points with n rows (offset 1), with any orientation.-- Ignacio Larrosa CaĆ±estro

{-# LANGUAGE FlexibleContexts #-} import Diagrams.Prelude import Diagrams.Backend.SVG.CmdLine (B, defaultMain) import Data.List (sort, sortOn, nub, transpose) import Data.List.Split (chunksOf) third :: (Int, Int) -> (Int, Int) -> (Int, Int) third (p, q) (p', q') = let (s, t) = (p' - p, q' - q) in (p - t, q + s + t) inTriangle :: Int -> (Int, Int) -> Bool inTriangle n (p, q) = 0 <= p && 0 <= q && p + q < n sizeSquared :: [(Int, Int)] -> Int sizeSquared [(p, q), (p', q'), _] = let (s, t) = (p' - p, q' - q) in s * s + s * t + t * t triangles :: Int -> [[(Int, Int)]] triangles n = sortOn sizeSquared $ nub [ sort [(a, b), (c, d), (e, f)] | a <- [0..n] , b <- [0..n] , inTriangle n (a, b) , c <- [0..n] , d <- [0..n] , inTriangle n (c, d) , (a, b) /= (c, d) , (e, f) <- [ third (a, b) (c, d) , third (c, d) (a, b) ] , inTriangle n (e, f) ] t2 :: (Int, Int) -> V2 Double t2 (p, q) = V2 (fromIntegral p + fromIntegral q / 2) (sqrt 3 * fromIntegral q / 2) t2' = P . t2 draw n t@[ab,cd,ef] = frame 0.75 . scale 1.25 . rotate (15 @@ deg) $ mconcat [ circle 0.25 # fc (if (p, q) `elem` t then grey else white) # translate (t2 (p, q)) # lw thin | p <- [0..n] , q <- [0..n] , inTriangle n (p, q) ] `atop` mconcat [ t2' ab ~~ t2' cd , t2' cd ~~ t2' ef , t2' ef ~~ t2' ab ] grid = vcat . map hcat diagram n m = bg white . grid . chunksOf m . map (draw n) $ triangles n main = defaultMain (diagram 6 7)

A000984 Central binomial coefficient (2n,n)

(1, 2, 6, 20,70, 252, 924, ...)

The number of direct routes from my home to Granny's when Granny lives n blocks south and n blocks east of my home in Grid City. For example, a(2)=6 because there are 6 direct routes: SSEE, SESE, SEES, EESS, ESES and ESSE.-- Dennis P. Walsh

{-# LANGUAGE FlexibleContexts #-} import Diagrams.Prelude import Diagrams.Backend.SVG.CmdLine (B, defaultMain) import Control.Monad (replicateM) import Data.List.Split (chunksOf) u, d, z :: (Int, Int) u = (1, 0) d = (0, 1) z = (0, 0) add (a, b) (c, d) = (a + c, b + d) v :: (Int, Int) -> V2 Double v (a, b) = V2 (fromIntegral a) (fromIntegral b) vs = map v . scanl add z l = foldr add z paths n = [ q | q <- replicateM (2 * n) [u,d] , l q == (n, n) ] draw n q = frame 0.5 . (`atop` centerXY (strutY (fromIntegral n))) . centerXY $ mconcat [ circle 0.25 # fc white # translate pq # lw thin | pq <- vs q ] `atop` strokeT (trailFromOffsets (map v q)) grid = vcat . map hcat diagram n m = bg white . centerXY . grid . chunksOf m . map (draw n) $ paths n main = defaultMain (diagram 4 7)

A001405 Binomial (n,floor(n/2))

(1, 1, 2, 3, 6, 10, 20, 35,70, 126, 252, 462, 924, ...)

Number of distinct strings of length n, each of which is a prefix of a string of balanced parentheses; For n = 4, the a(4) = 6 distinct strings of length 4 are ((((, (((), (()(, ()((, ()(), and (()).-- Lee A. Newberg

{-# LANGUAGE FlexibleContexts #-} import Diagrams.Prelude import Diagrams.Backend.SVG.CmdLine (B, defaultMain) import Control.Monad (replicateM) import Data.List (sort, transpose) import Data.List.Split (chunksOf) u, d, z :: (Int, Int) u = (1, 1) d = (1, -1) z = (0, 0) add (a, b) (c, d) = (a + c, b + d) boundedBelow = not . any ((< 0) . snd) . scanl add z paths n = [ q | q <- replicateM n [u,d] , boundedBelow q ] v :: (Int, Int) -> V2 Double v (a, b) = V2 (fromIntegral a) (fromIntegral b) vs = map v . scanl add z draw n q = frame 0.5 . (`atop` centerXY (strutY (fromIntegral n))) . centerXY $ mconcat [ circle 0.25 # fc white # translate pq # lw thin | pq <- vs q ] `atop` strokeT (trailFromOffsets (map v q)) grid = vcat . map hcat diagram n m = bg white . centerXY . grid . transpose . chunksOf m . map (draw n) . sort $ paths n main = defaultMain (diagram 8 10)

A002623 Generating function of 1/((1+x)(1-x)^4)

(1, 3, 7, 13, 22, 34, 50,70, 95, 125, 161, 203, 252, 308, 372, 444, 525, 615, 715, 825, 946, ...)

Number of nondegenerate triangles that can be made from rods of length 1,2,3,4,...,n.-- Alfred Bruckstein

{-# LANGUAGE FlexibleContexts #-} import Diagrams.Prelude import Diagrams.Backend.SVG.CmdLine (B, defaultMain) import Data.List (sort, sortOn, nub, transpose) import Data.List.Split (chunksOf) nondegenerate :: [Int] -> Bool nondegenerate [a,b,c] = a + b > c corners :: [Int] -> [V2 Double] corners [a',b',c'] = [V2 0 0, V2 c 0, V2 x y] where a = fromIntegral a' b = fromIntegral b' c = fromIntegral c' x = (c^2 - a^2 + b^2) / (2 * c) y = sqrt $ b^2 - x^2 sizeSquared :: [Int] -> Double sizeSquared [a',b',c'] = s * (s - a) * (s - b) * (s - c) where a = fromIntegral a' b = fromIntegral b' c = fromIntegral c' s = (a + b + c) / 2 triangles :: Int -> [([Int], [V2 Double])] triangles n = map (\t -> (t, corners t)) . sortOn sizeSquared $ [ abc | a <- [1..n] , b <- [a..n] , c <- [b..n] , let abc = [a,b,c] , nondegenerate abc ] edge k a b = mconcat [ circle 0.25 # fc white # translate p # lw thin | p <- [ lerp t a b | i <- [0..k] , let t = fromIntegral i / fromIntegral k ] ] `atop` (P a ~~ P b) draw n ([a,b,c], t@[ab,cd,ef]) = frame 0.5 . (`atop` centerXY (strut (fromIntegral n))) . centerXY . rotate (15 @@ deg) $ mconcat [ ] `atop` mconcat [ edge c ab cd , edge a cd ef , edge b ef ab ] grid = vcat . map hcat diagram n m = bg white . grid . chunksOf m . map (draw n) $ triangles n main = defaultMain (diagram 8 7)

**hgmp** is a Haskell interface to GMP
(for GHC with the default integer-gmp implementation of Integer).
Contains type definitions and marshalling functions, to be able to write FFI
bindings using Haskell's Integer and Rational types. Function bindings may
come in a future version.

A simple example illustrating binding to GMP's next probable-prime function:

{-# LANGUAGE ForeignFunctionInterface #-} import Foreign.Ptr (Ptr(..)) import Numeric.GMP.Types (MPZ) import Numeric.GMP.Utils (withInInteger, withOutInteger_) import System.IO.Unsafe (unsafePerformIO) foreign import ccall safe "__gmpz_nextprime" mpz_nextprime :: Ptr MPZ -> Ptr MPZ -> IO () nextPrime :: Integer -> Integer nextPrime n = unsafePerformIO $ withOutInteger_ $ \rop -> withInInteger n $ \op -> mpz_nextprime rop op

You can cabal install (or otherwise get it) from hgmp on Hackage, or get (or browse code.mathr.co.uk/hgmp) the freshest sources from git:

git clone https://code.mathr.co.uk/hgmp.git

Any and all feedback welcome! I'm sure there are some things that could be improved, and ideas for future versions will be appreciated too.

]]>Last year I drew some things, and today I recreated one of them using Haskell with the Diagrams library. It features a circular Gray code with five bits, giving 32 possible combinations. A Gray code has the property that neighbouring combinations change only in one position, which is useful for detecting errors in rotary encoders.

Here is the source code:

{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} import Diagrams.Prelude hiding (size) import Diagrams.Backend.SVG.CmdLine (B, defaultMain) main :: IO () main = defaultMain diagram diagram :: Diagram B diagram = bg white . frame 0.125 . centerXY . mconcat $ [ ring 6 2 1 , ring 5 4 2 , ring 4 8 4 , ring 3 16 8 , ring 2 16 0 , txt ] ring :: Double -> Int -> Int -> Diagram B ring inr count offset = mconcat . zipWith (cell inr) [0..31] . drop offset . cycle $ ( replicate count False ++ replicate count True ) cell :: Double -> Double -> Bool -> Diagram B cell inr angle flag = rotate (angle / 32 @@ turn) . fc (if flag then dark else light) . lc white . translate (r2 (i, 0)) . strokeT . closeTrail $ mconcat [ p2 (i, 0) ~~ p2 (o, 0) , arc' o h t , rotate t $ p2 (o, 0) ~~ p2 (i, 0) , arc' i (rotate t h) t' ] where t = 1 / 32 @@ turn t' = -1 / 32 @@ turn i = size inr o = size (inr + 1) h = direction (r2 (1, 0)) light, dark :: Colour Double light = sRGB 0.7 0.7 0.7 dark = sRGB 0.3 0.3 0.3 size :: Double -> Double size r = exp ((r - 7) / 5) txt :: Diagram B txt = scale 0.125 . centerXY . vcat $ centerXY . atop (translateY 0.4 $ strutY 0.8) . font "LMSans10" . italic . fontSizeL 1 . text <$> words "32 Shades of Gray"

I had some frustrations until I realized that **fontSizeL** was
what I needed to make the text change size when rendering at different sizes.

Back in August last year I was experimenting with vector renditions of the Buddhabrot, reasoning that tracing the boundary of hyperbolic components and plotting those iterates would give a reasonable idea of what the limit of the Buddhabrot at infinite iterations would look like. It's not perfect (some straight lines instead of curves, due to problems with highly awkward behaviour near root points) and the level of detail could be a bit higher, but I think it looks quite ok.

The program expects a list of minibrot islands as input on stdin with lines like "period size cre cim", suitable values might be derived by regexp voodoo from my Mandelbrot set feature database. It then finds all child components recursively, down to a minimum size limit, and traces their boundaries and the iterates thereof (the image of a closed curve under the quadratic polynomial map is another loop). The colouring weight is adjusted by the amount of stretching involved in the transformations, as well as the period and size of the component it belongs to.

You can download the Haskell source: vector-buddhabrot.hs whose heaviest dependency is cairo, with other dependencies including strict, deepseq, parallel, and my mandelbrot-numerics library.

]]>I implemented in Haskell some symbolic algorithms related to the Mandelbrot set, but setting up a Haskell tool-chain to try it out is quite a barrier to entry. Some time ago I wrote a server-side web interface, but I didn't run it live on the web because of security concerns (mainly denial of service from heavy computations). Last week I finally got around to installing GHCJS (pro-tip: trying to install in a sandbox is more trouble than its worth), a Haskell to JavaScript compiler, and ported the web interface to run client-side in the browser, neatly side-stepping any security issues and also allowing offline usage.

You can try it out at /mandelbrot/web/, there's no documentation to speak of but hopefully the examples should be enough to get you started. Some calculations can take a long time, but GHCJS's scheduler avoids the dreaded browser "script taking too long" popup, provided the calculations are not in a synchronous callback (thanks to luite in the #ghcjs IRC channel on freenode for pointing this out).

The source code for the web interface is at mandelbrot-web which also requires my mandelbrot-symbolics and mandelbrot-text libraries. You can also download the compiled web interface as a tarball: web.tgz. It has been postprocessed with closurecompiler, as documented on the GHCJS deployment page, which reduced the size quite a bit. Pro-tip #2, Debian calls the nodejs binary "/usr/bin/nodejs", but npm packages expect just "node" - I put a little script in ~/opt/bin/node (which is in my $PATH) to make it work properly:

#!/bin/sh exec nodejs "$@"

I plan to adapt the web interface further to include support for my mandelbrot-numerics library and eventually image rendering with annotations - though there won't be deep zoom support until I find an efficient JavaScript BigFloat library that I can FFI to, or an efficient pure Haskell BigFloat implementation (my variable-precision library is probably too slow and wasteful to be useful).

]]>Generating deep zoom images of the Mandelbrot set by iterations of \( z_{n+1} = F(z_n, c) \) require high precision for \(z_n\) and \(c\). Perturbation techniques use a high precision reference with low precision deltas which I write \( \left<\left< . \right>\right> \):

\[ z_{n+1} + \left<\left< z_{n+1} \right>\right> \gets F\left( z_n + \left<\left< z_n \right>\right> , c + \left<\left< c \right>\right> \right) \]

Boring algebraic manipulation gives the perturbed iteration equation:

\[ \left<\left< z_{n+1} \right>\right> \gets \left<\left< F \right>\right> \left(z_n, \left<\left< z_n \right>\right>, \left<\left< c \right>\right> \right) \]

Series approximation techniques assume \( \left<\left< z_n \right>\right> \) can be expressed in terms of \( \left<\left< c \right>\right> \):

\[ \left<\left< z_n \right>\right> = \sum_{k=1}^\infty{a_{k,n} {\left<\left< c \right>\right>}^k} \]

Substituting the series expression for \( \left<\left< z_n \right>\right> \) into the right hand side of the perturbed iteration equation and collecting terms by powers of \( \left<\left< c \right>\right> \) gives a collection of iteration equations for the coefficients \( a_k \):

\[ a_{k, n + 1} = A_k \left( z_n , \left\{ a_{j,n} : j \le k \right\} \right) \]

These recurrence relations are in complex variables, but arbitrary precision arithmetic libraries like MPFR generally work with real variables, so more boring algebraic manipulation expands each recurrence into a pair of recurrences involving the real and imaginary parts.

The functions in libraries like MPFR are quite basic - typically they take two input variables and an output variable, and compute a single arithmetic operation. Modern CPUs (and GPUs) have many cores, and systems like OpenMP allow to parallelize code to utilize more than one core. Parallel loops perform the same operation multiple times (compare with SIMD), but the recurrence expressions are mixed up mash of operations. So I canonicalize the recurrences into a representation that allows to extract as much uniform-operation parallelism as possible.

The outer-most operation is a sum, with each term being a product of a small integer with another sum. Each inner sum term is an optional negation of an optional scaling by a power of two of a product of powers. Negation and scaling by a power of two are very cheap operations. Squaring a high precision real number is cheaper than multiplying two different numbers, so powers are factored to use as much squaring as possible.

The computations are split into a sequence of phases, with each phase performing a parallel operation. The first phases compute all the squarings that are necessary for the powers. The next phases perform all the non-squaring products. The product (or sum) of a number of terms can be performed partially in parallel by careful bracketing: a * b * c * d becomes (a * b) * (c * d) with the two inner products performed in parallel before the outer product. Here's a diagram for an order 4 series:

The code generator is implemented in Haskell, and outputs C code using MPFR and OpenMP. You can find it as part of my (still-experimental) mandelbrot-perturbator project. The Haskell is really very ugly though (lots of dirty String concatenation to emit C code), not to mention inefficient - expect high order series to take a vast amount of time. The generator theoretically supports arbitrary integer powers in \(z \to z^p+c\) but I've only tested with \(p = 2\) because the rest of the perturbator code (mostly C-style C++ with a few templates and overloaded numerics) hasn't got some needed functionality for other powers. Eventually I plan to extend the code generator to emit the missing pieces, but first I want to experiment with native precision operations for some coefficients in the hope that it isn't so deathly slow for high order series at very deep zooms. Currently rendering a (boring) test zoom, only 400 frames away from 1e-10000. It's taking a while though, might be done by May.

Note: most of this post was written in May last year, the generator probably changed a bit since then.

]]>Some time ago I implemented a small library to extend floating
point types with a larger exponent, so that you can represent
really huge or really tiny numbers without overflow to infinity
or underflow to zero. Now that **compensated** is
updated on Hackage, I released it this morning.

> unExpExtended . log . exp . expExtended' $ 1000 1000.0 > log . exp $ 1000 Infinity > unExpExtended . log . exp . negate . expExtended' $ 1000 -1000.0 > log . exp . negate $ 1000 -Infinity

Or replace uses of the **Double** type with the
**EDouble** type. If using with **compensated**,
use **Compensated EDouble** rather than trying to
ExpExtend Compensated Double, otherwise you'll be limited to
1000 or so bits as the least-significant limbs will underflow
to zero.

A grid of squares getting smaller at the edges. I already blogged about this calendar image for February (yes, this post is rather late): Lozenge. The only other thing to mention is the Hermann grid illusion.

]]>The concept for Wedged is "playing Tetris optimally badly". Badly in that no row is complete, and optimally in that each row has at most one empty cell, and the rectangle is filled. The implementation is in Haskell using the Diagrams library for image output. The program takes around 10 minutes to run, generating 189 PNG images totalling around 38 MB. The arrangement of pieces is the same on each run, but the uneven wobbly drawn appearance is randomized and different each time. Here is a small selection of the output:

The full source code is 555 lines long, all in one file, with no comments or documentation. The architectural overview is this:

Starting from an empty rectangle, block off one cell in each row, subject to the constraint that blocked cells in nearby rows shouldn't be too close to each other, and the blocked cells should be roughly evenly distributed between columns. Some of these blockings might be duplicates (taking into account mirroring and rotation), so pick only one from each equivalence class.

Starting from the top left empty cell in each of these boards, fill it with pieces that fit. Fitting means that the piece is entirely within the board, not overlapping any blocked cells or other pieces. There are some additional constraints to improve aesthetic appearance and reduce the amount of output images: there should not be too many pieces of the same colour in the board, all adjacent pieces should be a different colour, and no piece should be able to slide into the space left when blocked cells are removed (this applies only to the long thin blue pieces, the other pieces can't move due to the earlier constraint on nearby blocked cells). The implementation uses MonadPlus [] for tree search with pruning, giving a list of possible fillings.

The filling process has some other aesthetic constraints: the board must be diverse (there must be a wide range of distinct colours in each row and column), the complete board must have a roughly even number of pieces of each colour, and there shouldn't be any long straight line boundaries between multiple pieces. The complete boards might have duplicates under symmetries (in the case that the original blocking arrangement was symmetrical), so pick only one from each equivalence class.

Finally each board is visualized using the Diagrams library. The pieces are extracted from the board using a horrific function 75 lines long, which performs brute force case analysis to determine the orientation of each piece and how to draw it (whether a sequence of lines for most pieces, or a closed loop for the red squares). The actual drawing uses recursive randomized midpoint perturbation to make each stroke wobbly, then uses the painter's algorithm to outline it: first a thick black stroke (actually formed of lots of circles) is drawn, followed by a narrower coloured stroke (again, lots of circles). The coloured pieces are drawn on a white background rectangle, with some margin space, and the result is saved to a numbered PNG file.

You can download the Wedged source code tarball or install from Hackage.

]]>