Today I presented GULCII, my Graphical Untyped Lambda Calculus Interactive Interpreter, at the University of Edinburgh Informatics department. It went well I think. The first go around in the morning I missed some slides about de Bruijn indexes but the afternoon audience seemed to be familiar with it. First I performed with GULCII, the same set as the FARM conference music evening, testing an equivalence between Church encoding and Scott encoding of natural numbers. Then I presented some slides about those encoding schemes of data in untyped lambda calculus, and a bit about how GULCII works, some shortcomings, and ideas for future developments.

A week before the talk I discovered a bad bug in the evaluator ("exp two two" with Church numerals was not equal to "four"), but I managed to rewrite it in time, using the "scope extrusion" rules from the Lambdascope paper. However, sharing is still broken, so the next version will probably switch to using Lambdascope as a library, so that full lazy graph reduction will work properly. The bug had been there since 2011, and is visible in the video of my FARM performance.

You can download the slides from my presentation,
or view the Pandoc Markdown for LaTex Beamer
source code.
I might upload some video of the second talk soon. GULCII itself is
on Hackage so you can install
it with `cabal install gulcii`

.

Incidentally the brickwork opposite the Informatics Forum reminded me of Donky Kong, with its ladders:

]]>I last played live with GULCII in 2011 at LiWoLi and NOHUP Bow Arts Open around the same time. With further tweaks and enhancements, I've been rehearsing a short code recital for the FARM conference's Evening Of Algorithmic Arts at the Old Fire Station in Oxford, UK, this coming Saturday 9th September. Book your free ticket online and come along, it should be a good one with many performances.

GULCII (Graphical Untyped Lambda Calculus Interactive Interpreter) is an untyped lambda calculus interpreter supporting interactive modification of a running program with graphical display of graph reduction. Lambda calculus is a minimal prototypical functional programming language developed by Alonzo Church in the 1930s. Church encoding uses folds to represent data as higher-order functions. Dana Scott's encoding composes algebraic data types as functions. Each has strengths and weaknesses.

The performance is a code recital, with the internal state of the interpreter visualized and sonified. Part 1 introduces Church encoding. Part 2 develops Scott encoding. An interlude takes in two non-terminating loops, each with their own intrinsic computational rhythm. Finally, Part 3 tests an equivalence between Church and Scott numerals.

The performance involves functional programming in two ways. Firstly, the live recital of lambda calculus code into the interpreter for evaluation. Secondly, the interpreter itself is written in the functional programming language Haskell.

Regarding related work, Henning Thielemann's
"live-sequencer"
has a similar approach to code evaluation with names rebindable to terms during
runtime. Sonification of data has a long history, including a mention by Douglas
Adams in "*Dirk Gently’s Holistic Detective Agency*" (1987), while
sonification of machines is perhaps more niche. I was inspired by Valentina
Vuksic's
"Sei Personaggi Part 2",
in which "*the magnetic fields of the memory modules (RAM) are being
picked up acoustically while the experimental computer drama is moving on*";
the Puredyne GNU/Linux startup sound, "`cat /dev/mem > /dev/dsp`

";
and also Dave Griffith's "Betablocker"
live-coding acid techno machine.

I'll be at the daytime FARM conference too, if you're at ICFP and want to say hi. Long beard, glasses, probably drinking coffee.

**EDIT:** it went really well, I'm pleased. Had some nice
feedback too, particularly a suggestion that I could have finished up by
defining:

churchPred = \n . toChurch (scottPred (fromChurch n))

I didn't catch the name of the suggester, but I think I'll use this in future, it's a very nice punchline. Thanks, mystery functional programmer! I'll try to improve the sonfication too, it is rather minimal.

Since getting back home I worked a bit on the code, cleaning it up for a 0.3 release on Hackage (not quite ready yet) - I also fixed an infelicity with the evaluator, now it reduces the right-hand branch of an apply node when the left-hand branch is irreducible (and the apply node itself is not a lambda applied to something, in which case it would beta reduce). This means it can make progress in more cases, for example (with Scott-encoded lists and numerals):

iterate succ zero

The version I performed with would get stuck at
`cons zero `

.*stuff*

**tanh()** is a nice function for saturation in audio, amongst
other applications. Near 0 it is similar to the identity function \(x\), leaving
sound unchanged. Far from 0 it tends to constant \(\pm 1\), with a smooth roll-off
curve that gives a soft-clipping effect, where louder signals are more distorted,
and the output never exceeds 0dBFS.

Unfortunately tanh() is computationally expensive, so approximations are desirable. One common approximation is a rational function:

\[ \tanh(x) \approx x \frac{27 + x^2}{27 + 9 x^2} \]

which the apparent source describes as

based on the pade-approximation of the tanh function with tweaked coefficients.

I also found mention Padé-approximants on a page primarily focussed on approximating tanh() with a continued fraction. So I set out to discover what they were. Some dead ends (a book appendix with 6 pages of badly-OCR'd FORTRAN code, to name but one) and I eventually struck gold with this PDF preview:

Outlines of Padé Approximation by Claude Brezinski in

Computational Aspects of Complex Analysis pp 1-50part of theNATO Advanced Study Institutes Series book series (ASIC, volume 102)Abstract: In the first section Padé approximants are defined and some properties are given. The second section deals with their algebraic properties and, in particular, with their connection to orthogonal polynomials and quadrature methods. Recursive schemes for computing sequences of Padé approximants are derived. The third section is devoted to some results concerning the convergence problem. Some applications and extensions are given in the last section.

The preview contains a system of equations involving a rational function:

\[ [p/q]_f := \frac{\sum_{i=0}^p a_i x^i}{\sum_{i=0}^q b_i x^i} \]

where without loss of generality \(b_0 = 1\). The coefficients are derived from the coefficients \(c_i\) of the Maclaurin series of \(f\) (which is its Taylor series expanded about \(0\)):

\[ f := \sum_{i=0}^\infty c_i x^i \]

The system of equations is very regular, here's some Haskell code to solve for the \(a\)s and \(b\)s given the \(c\)s:

pade :: [Rational] -> Int -> Int -> ([Rational], [Rational]) pade c p q = (a, b) where m = take q . map (take q) . tails . drop (p - q + 1) $ c v = take q . drop (p + 1) . map negate $ c Just m1 = inverse m -- FIXME this will crash if it isn't invertible b = 1 : reverse (m1 `mulmv` v) a = take (p + 1) $ [ reverse cs `dot` bs | (bs, cs) <- map unzip . drop 1 . inits $ zip (b ++ repeat 0) c ]

This needs an `inverse :: [[Rational]] -> Maybe [[Rational]]`

, the
pure Gaussian elimination code from
an old mailing list post
works fine with minor modifications for current GHC versions (it needs an extra
Eq constraint now).

With some extra output beautification code (and of course the coefficients of the power series for tanh(), which involve the Bernoulli numbers) the first few Padé approximants for tanh() are:

[1/0] = x [1/2] = x*(3)/(3+x**2) [3/2] = x*(15+x**2)/(15+6*x**2) [3/4] = x*(105+10*x**2)/(105+45*x**2+x**4) [5/4] = x*(945+105*x**2+x**4)/(945+420*x**2+15*x**4) [5/6] = x*(10395+1260*x**2+21*x**4)/(10395+4725*x**2+210*x**4+x**6) [7/6] = x*(135135+17325*x**2+378*x**4+x**6)/(135135+62370*x**2+3150*x**4+28*x**6) [7/8] = x*(2027025+270270*x**2+6930*x**4+36*x**6)/(2027025+945945*x**2+51975*x**4+630*x**6+x**8)

Some of these are shown in the image at the top of this post, along with the music-dsp approximation with "tweaked coefficients". The [7/6] approximation agrees with the truncated Lambert's continued fraction from the linked page, I'll probably end up using the [5/6] in various experiments. You can download the source code here: Pade.hs.

]]>**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.

]]>