F# Recursion vs Iteration speed/overhead - recursion

I've been messing around a bit with Riccardo Terrell's Akka.NET Fractal demo (https://github.com/rikace/akkafractal) to try and understand it. (It's great, btw)
One thing I tried as a minor challenge for myself was to rewrite some bits of it in a more functional way. I've got it to work but it's much slower than the original.
Here's (more or less) the original Mandelbrot Set calculation adapted for testing:
let mandelbrotSet (xp : int) (yp : int) (w : int) (h :int) (width : int) (height : int)
(maxr : float) (minr : float) (maxi : float) (mini : float) : List<int> =
let mutable zx = 0.
let mutable zy = 0.
let mutable cx = 0.
let mutable cy = 0.
let mutable xjump = ((maxr - minr) / ( float width))
let yjump = ((maxi - mini) / (float height))
let mutable tempzx = 0.
let loopmax = 1000
let mutable loopgo = 0
let outputList: int list = List.empty
for x = xp to (xp + w) - 1 do
cx <- (xjump * float x) - abs(minr)
for y = yp to (yp + h) - 1 do
zx <- 0.
zy <- 0.
cy <- (yjump * float y) - abs(mini)
loopgo <- 0
while (zx * zx + zy * zy <= 4. && loopgo < loopmax) do
loopgo <- loopgo + 1
tempzx <- zx
zx <- (zx * zx) - (zy * zy) + cx
zy <- (2. * tempzx * zy) + cy
(List.append outputList [loopgo]) |> ignore
outputList
And here's my version with the recursive mbCalc function doing the work:
let mandelbrotSetRec (xp : int) (yp : int) (w : int) (h :int) (width : int) (height : int)
(maxr : float) (minr : float) (maxi : float) (mini : float) : List<int> =
let xjump = ((maxr - minr) / (float width))
let yjump = ((maxi - mini) / (float height))
let loopMax = 1000
let outputList: int list = List.empty
let rec mbCalc(zx:float, zy:float, cx:float, cy:float, loopCount:int) =
match (zx * zx + zy * zy), loopCount with //The square of the magnitude of z
| a,b when a > 4. || b = loopMax -> loopCount
| _ -> mbCalc((zx * zx) - (zy * zy) + cx, (2. * zx * zy) + cy, cx, cy, loopCount+1) //iteration is the next value of z^2+c
[|0..w-1|] //For each x...
|> Array.map (fun x -> let cx = (xjump * float (x+xp) - abs(minr))
[|0..h-1|] ///and for each y...
|> Array.map (fun y -> let cy = (yjump * float (y+yp) - abs(mini))
let mbVal = mbCalc(0., 0., cx, cy,0) //Calculate the number of iterations to convergence (recursively)
List.append outputList [mbVal]))|>ignore
outputList
Is this just to be expected, pointlessly loading up an Actor with a load of recursive calls, or am I just doing something very inefficiently? Any pointers gratefully received!
If you want to run them then here's a little test script:
let xp = 1500
let yp = 1500
let w = 200
let h = 200
let width = 4000
let height = 4000
let timer1 = new System.Diagnostics.Stopwatch()
timer1.Start()
let ref = mandelbrotSet xp yp w h width height 0.5 -2.5 1.5 -1.5
timer1.Stop()
let timer2 = new System.Diagnostics.Stopwatch()
timer2.Start()
let test = mandelbrotSetRec xp yp w h width height 0.5 -2.5 1.5 -1.5
timer2.Stop
timer1.ElapsedTicks;;
timer2.ElapsedTicks;;
ref = test;;
EDIT: As per Philip's answer below, I added the list output quickly (too quickly!) to make something that ran in a script without requiring any imports. Here's the code to return the image:
let mandelbrotSetRec (xp : int) (yp : int) (w : int) (h :int) (width : int) (height : int)
(maxr : float) (minr : float) (maxi : float) (mini : float) : Image<Rgba32> =
let img = new Image<Rgba32>(w, h)
let xjump = ((maxr - minr) / (float width))
let yjump = ((maxi - mini) / (float height))
let loopMax = 1000
//Precalculate the possible colour list
let palette = List.append ([0..loopMax - 1] |> List.map(fun c -> Rgba32(byte(c % 32 * 7), byte(c % 128 * 2), byte(c % 16 * 14)))) [Rgba32.Black]
let rec mbCalc(zx:float, zy:float, cx:float, cy:float, loopCount:int) =
match (zx * zx + zy * zy), loopCount with //The square of the magnitude of z
| a,b when a > 4. || b = loopMax -> loopCount
| _ -> mbCalc((zx * zx) - (zy * zy) + cx, (2. * zx * zy) + cy, cx, cy, loopCount+1) //iteration is the next value of z^2+c
[|0..w-1|] //For each x...
|> Array.map (fun x -> let cx = (xjump * float (x+xp) - abs(minr))
[|0..h-1|] ///and for each y...
|> Array.map (fun y -> let cy = (yjump * float (y+yp) - abs(mini))
let mbVal = mbCalc(0., 0., cx, cy,0) //Calculate the number of iterations to convergence (recursively)
img.[x,y] <- palette.[mbVal]))|>ignore
img

Firstly, both functions return [] so there is no mandlebrot set being returned even if it's correctly calculated. List.append returns a list, it doesn't mutate an existing one.
Using a quick BenchmarkDotNet program below, where each function is in its own module:
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Running
open ActorTest
[<MemoryDiagnoser>]
type Bench() =
let xp = 1500
let yp = 1500
let w = 200
let h = 200
let width = 4000
let height = 4000
[<Benchmark(Baseline=true)>]
member _.Mutable() =
Mutable.mandelbrotSet xp yp w h width height 0.5 -2.5 1.5 -1.5
[<Benchmark>]
member _.Recursive() =
Recursive.mandelbrotSet xp yp w h width height 0.5 -2.5 1.5 -1.5
[<EntryPoint>]
let main argv =
let summary = BenchmarkRunner.Run<Bench>()
printfn "%A" summary
0 // return an integer exit code
Your code gave these results:
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------- |---------:|----------:|----------:|------:|--------:|---------:|---------:|------:|----------:|
| Mutable | 1.356 ms | 0.0187 ms | 0.0166 ms | 1.00 | 0.00 | 406.2500 | - | - | 1.22 MB |
| Recursive | 2.558 ms | 0.0303 ms | 0.0283 ms | 1.89 | 0.03 | 613.2813 | 304.6875 | - | 2.13 MB |
I noticed that you're using Array.map but there's no results being captured anywhere, so changing that to Array.iter got your code to be nearly the same:
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
|---------- |---------:|----------:|----------:|------:|---------:|------:|------:|----------:|
| Mutable | 1.515 ms | 0.0107 ms | 0.0094 ms | 1.00 | 406.2500 | - | - | 1.22 MB |
| Recursive | 1.652 ms | 0.0114 ms | 0.0101 ms | 1.09 | 607.4219 | - | - | 1.82 MB |
This difference can probably be explained by the additional allocations done with the mapping calls. Allocations are expensive, especially when it's larger arrays, so it's best to avoid them if possible. Exact timings will differ from machine to machine but I'd expect a similar before/after ratio when using BenchmarkDotNet.
This could probably be further optimized by avoiding the list allocations and pre-allocating a list or array that you fill in. The same is true for the iterative call. Also looping through a Span<'T> will be faster than an array since it elides a bounds check, but you'd probably have to change the shape of your code a lot to do that.
Lastly, always use a statistical benchmarking tool like BenchmarkDotNet to measure performance in microbenchmarks like this. Quick scripts are fine as a starting point, but they're no substitute for a tool that accounts for execution time variability on a machine.

Related

F# recursive function alternating between plus and minus

I'm asked to remove one clause in below F# function, while maintaining it's functionality, that's alternating between plus and minus.
let rec altsum =
function
| [] -> 0
| [ x ] -> x
| x0 :: x1 :: xr -> x0 - x1 + altsum xr
I stumbled upon below solution, but I don't quite understand how it evaluates to the correct result since it only contain a minus symbol and not a plus symbol. Can someone please explain how below function altsum1 evaluates?
let rec altsum1 =
function
| [] -> 0
| x0 :: xr -> x0 - altsum1 xr
It works because -1 * -1 = 1. We can then use the distributive property to show that:
a - (b - c) = a - b + c
So:
altsum([1; 2; 3; 4]) =
1 - altsum([2; 3; 4]) =
1 - (2 - altsum([3; 4])) =
1 - (2 - (3 - altsum[4])) =
1 - (2 - (3 - 4)) =
1 - 2 + 3 - 4 =
-2

How to recursively get scalar value from 2 lists?

The task is to get scalar value from 2 lists recursively. I wrote the code that I think should work, but I am having some type related problem
let rec scalar2 (a, b) = function
| ([], []) -> 0
| ([x : int], [y : int]) -> x * y
| (h1::t1, h2::t2) ->
let sc : int = scalar2 (t1,t2)
sc + (h1 * h2)
The error is that scalar2 (t1,t2) reqested to be int, but it is int list * int list -> int
How this problem could be solved?
When defining a function using the function keyword, you don't need to name your parameters (a and b here). Note that your function body doesn't refer to a or b at all. You want scalar2 to be a function, and the function expression on the right hand side results in a function, so just assign this function to scalar2 directly.
let rec scalar2 = function
| ([], []) -> 0
| ([x : int], [y : int]) -> x * y
| (h1::t1, h2::t2) ->
let sc : int = scalar2 (t1,t2)
sc + (h1 * h2)
Your mistake is likely caused by a confusion with the usual way of defining a function, which doesn't use the function keyword:
let rec scalar2 (a,b) =
match (a,b) with
| ([], []) -> 0
| ([x : int], [y : int]) -> x * y
| (h1::t1, h2::t2) ->
let sc : int = scalar2 (t1,t2)
sc + (h1 * h2)
This way you need a match expression which does use the parameters a and b.
Note that both of these definitions are incomplete, since you haven't said what should happen when only one of the lists is non-empty.
To explain the type error in the original code, consider how F# evaluates let sc : int = scalar2 (t1,t2). Your original definition says that scalar2 (a,b) = function ..., and the left-hand side of this equality has the same form as the expression scalar2 (t1,t2).
So the scalar2 (t1,t2) gets replaced with the function ... expression, after substituting t1 for a and t2 for b. This leaves let sc : int = function ... which of course doesn't type-check.

F# floor function doesn't round properly?

I have a super strange issue. Here's my simple piece of recursive code:
let float2cfrac x =
let rec tofloat (lst : int list) (xi : float) =
let qi = floor xi
let ri = xi-qi
printfn "%A %A %A %A" xi qi ri (1.0/ri)
if ri > (floor 0.0) then
tofloat (lst # [int qi]) (1.0/ri)
else
lst
tofloat [] x
I'm not going to explain my code much, as the issue i'm having seems quite basic.
The printfn will print xi and qi, where qi is simply the floor of xi.
When looking at the output, it looks like once the software reaches a round number for xi, the floor function removes 1, instead of doing nothing.
Here's my output for the number 3.245, which should complete computing after just a few calculations:
float2cfrac 3.245;;
3.245 3.0 0.245 4.081632653
4.081632653 4.0 0.08163265306 12.25
12.25 12.0 0.25 4.0
4.0 3.0 1.0 1.0 - Here it gets messed up. Floor of 4.0 should be 4, right?
1.0 1.0 4.035882739e-12 2.477772682e+11
2.477772682e+11 2.477772682e+11 0.2112731934 4.733208147
4.733208147 4.0 0.7332081468 1.363869188
If anybody has an explanation for this or some sugguestions, it would be greatly appreciated!
A super well known issue: floating-point numbers have finite precision, so you can't generally count on the same calculation done via different methods to produce the same result. There will always be a margin of error.
The corollary is that you can't compare floating-point numbers for strict equality. You have to take their difference and compare it to some very small number.
You can avoid the numerical issues of floats by not using floats. Here one solution is to represent the input as a rational number, i.e integer numerator and integer denominator, and then adjust the formulas accordingly.
open System.Numerics
let number2cfrac (xNumerator : int) (xDenominator : int) =
let rec loop acc (xin : BigInteger) (xid : BigInteger) =
let qi = xin / xid
let rin = xin - (qi * xid)
printfn "%A %A %A %A" (float xin / float xid) qi (float rin / float xid) (float xid / float rin)
if rin <> BigInteger.Zero then
loop (int qi :: acc) xid rin
else
List.rev acc
loop [] (BigInteger(xNumerator)) (BigInteger(xDenominator))
> number2cfrac 3245 1000;;
3.245 3 0.245 4.081632653
4.081632653 4 0.08163265306 12.25
12.25 12 0.25 4.0
4.0 4 0.0 infinity
val it : int list = [3; 4; 12]

F#: multiplication of polynomials

I found this exercise in "Functional Programming Using F#" (4.22.3):
Declare infix F# operators for addition and multiplication of
polynomials in the chosen representation.
f(x) = a0 + a1 * x + a2 * x^2 + ... + an * x^n
The polynomial is reprsented as a list of integer. For example the polynomial f(x) = x^3 + 2 is represented by the list [2; 0; 0; 1]. Now I need a function that takes two lists of integers and returns a list of integers:
// polymul: int list -> int list -> int list
let polymul p q =
???
The authors gave this hint along with the exercise:
The following recursion formula is useful when defining the
multiplication:
0 * Q(x) = 0
(a0+a1*x+...+an*x^n) * Q(x) =
a0 * Q(x) + x * [(a1+a2*x+...+an*x^(n-1)) * Q(x)]
I could not come up with a solution for this exercise. Can anyone help me?
I have a solution. I took the hint and converted it one-to-one into F#. And it worked magically:
// multiplicate a polynomial with a constant
// polymulconst: float -> float list -> float list
let rec polymulconst c = function
| [] -> []
| a::rest -> c*a::polymulconst c rest
// multiplying a polynomial by x
// polymulx: float list -> float list
let polymulx = function
| [] -> []
| lst -> 0.0::lst
// add two polynomials
// polyadd: float int -> float int -> float int
let rec polyadd ps qs =
match (ps, qs) with
| ([], ys) -> ys
| (xs, []) -> xs
| (x::xs, y::ys) -> (x+y)::polyadd xs ys
// polymul: float int -> float int -> float int
let rec polymul qs = function
| [] -> []
| p::ps -> polyadd (polymulconst p qs)
(polymulx (polymul qs ps))
let ( .++. ) p q = polyadd p q
let ( .**. ) p q = polymul p q
I tested the function in the F# REPL:
> let polyA = [1.0; -2.0; 1.0];;
val polyA : float list = [1.0; -2.0; 1.0]
> let polyB = [-4.0; 3.0; 2.0];;
val polyB : float list = [-4.0; 3.0; 2.0]
> polymul polyA polyB;;
val it : float list = [-4.0; 11.0; -8.0; -1.0; 2.0]
> polyA .**. polyB;;
val it : float list = [-4.0; 11.0; -8.0; -1.0; 2.0]
>

Reversing an int in OCaml

I'm teaching myself OCaml, and the main resources I'm using for practice are some problem sets Cornell has made available from their 3110 class. One of the problems is to write a function to reverse an int (i.e: 1234 -> 4321, -1234 -> -4321, 2 -> 2, -10 -> -1 etc).
I have a working solution, but I'm concerned that it isn't exactly idiomatic OCaml:
let rev_int (i : int) : int =
let rec power cnt value =
if value / 10 = 0 then cnt
else power (10 * cnt) (value/10) in
let rec aux pow temp value =
if value <> 0 then aux (pow/10) (temp + (value mod 10 * pow)) (value / 10)
else temp in
aux (power 1 i) 0 i
It works properly in all cases as far as I can tell, but it just seems seriously "un-OCaml" to me, particularly because I'm running through the length of the int twice with two inner-functions. So I'm just wondering whether there's a more "OCaml" way to do this.
I would say, that the following is idiomatic enough.
(* [rev x] returns such value [y] that its decimal representation
is a reverse of decimal representation of [x], e.g.,
[rev 12345 = 54321] *)
let rev n =
let rec loop acc n =
if n = 0 then acc
else loop (acc * 10 + n mod 10) (n / 10) in
loop 0 n
But as Jeffrey said in a comment, your solution is quite idiomatic, although not the nicest one.
Btw, my own style, would be to write like this:
let rev n =
let rec loop acc = function
| 0 -> acc
| n -> loop (acc * 10 + n mod 10) (n / 10) in
loop 0 n
As I prefer pattern matching to if/then/else. But this is a matter of mine personal taste.
I can propose you some way of doing it:
let decompose_int i =
let r = i / 10 in
i - (r * 10) , r
This function allows me to decompose the integer as if I had a list.
For instance 1234 is decomposed into 4 and 123.
Then we reverse it.
let rec rev_int i = match decompose_int i with
| x , 0 -> 10 , x
| h , t ->
let (m,r) = rev_int t in
(10 * m, h * m + r)
The idea here is to return 10, 100, 1000... and so on to know where to place the last digit.
What I wanted to do here is to treat them as I would treat lists, decompose_int being a List.hd and List.tl equivalent.

Resources