I'm trying to expand on James Hugard's post How do I plot a data series in F#? and I'm running into a glitch when using a variable number of function arguments. If I specifically name out the functions for Hugard's LineChartForm like in this example, the code works as expected when loaded into the F# Interactive Interpreter:
(* This code is based off of a Stack Overflow post by
James Hugard #
https://stackoverflow.com/questions/3276357/how-do-i-plot-a-data-series-in-f
*)
module HuggardPlot
#r "System.Windows.Forms.DataVisualization"
open System.Windows.Forms
open System.Windows.Forms.DataVisualization.Charting
type LineChartForm( title, minX, maxX, func1, func2) =
inherit Form( Text=title )
let chart = new Chart(Dock=DockStyle.Fill)
let area = new ChartArea(Name="Area1")
(* Add the first plot Hugard style *)
let series = new Series()
do series.ChartType <- SeriesChartType.Line
do for i in minX .. maxX do
series.Points.AddXY(i, func1(i)) |> ignore
do series.ChartArea <- "Area1"
do chart.Series.Add( series )
(* Add the second plot Hugard style *)
let series2 = new Series()
do series2.ChartType <- SeriesChartType.Line
do for i in minX .. maxX do
series2.Points.AddXY(i, func2(i)) |> ignore
do series2.ChartArea <- "Area1"
do chart.Series.Add( series2 )
(* Add area1 to the plot *)
do chart.ChartAreas.Add(area)
do base.Controls.Add( chart )
(* Convenience method to make it easier to plot functions *)
let plotTwoFunctions minX maxX f1 f2 =
let LCF = new LineChartForm("lines", minX, maxX, f1, f2);
LCF.Show();
On the other hand, if I try to use the technique in the Parameter Arrays section of http://msdn.microsoft.com/en-us/library/dd233213.aspx to pass a variable number of functions via a ParamArray with the following code:
(* This code is based off of a Stack Overflow post by
James Hugard #
https://stackoverflow.com/questions/3276357/how-do-i-plot-a-data-series-in-f
*)
module HuggardPlot
#r "System.Windows.Forms.DataVisualization"
open System
open System.Windows.Forms
open System.Windows.Forms.DataVisualization.Charting
type LineChartForm(
title,
minX,
maxX,
[<ParamArray>] funcs: Object[]) =
inherit Form( Text=title )
let chart = new Chart(Dock=DockStyle.Fill)
let area = new ChartArea(Name="Area1")
do for func in funcs do
(* Add the first plot Hugard style *)
let series = new Series()
do series.ChartType <- SeriesChartType.Line
do for i in minX .. maxX do
series.Points.AddXY(i, func(i)) |> ignore
do series.ChartArea <- "Area1"
do chart.Series.Add( series )
(* Add area1 to the plot *)
do chart.ChartAreas.Add(area)
do base.Controls.Add( chart )
(* Convenience method to make it easier to plot functions *)
let plotTwoFunctions minX maxX f1 f2 =
let LCF = new LineChartForm("lines", minX, maxX, f1, f2);
LCF.Show();
I get an error on line 27 series.Points.AddXY(i, func(i)) |> ignore stating "This value is not a function and cannot be applied" with a red squiggly under "func".
I suspect this has something to do with the fact that I am using [<ParamArray>] funcs: Object[] as a parameter definition to Mr. Hugard's LineChartForm Type.
What should I change [<ParamArray>] funcs: Object[] to so line 27 of the second code example will recognize "func" as a function?
If you want to create a method that takes params array of functions, then you need to define the parameter as [<ParamArray>] funcs: (float -> float)[]. In your original version, the type was Object[] meaning that individual elements of the array were objects - if you change the type of elements to functions, then F# will recognize that you can call them.
However, if you're simply interested in charting F# data, then there is already a good wrapper for the WinForms DataVisualization library called F# Chart and available on GitHub. There is also a comprehensive documentation available on MSDN (if you use the latest version of the library, then FSharpChart has been renamed to just Chart).
To create a plot comparing two functions over a specified range, you can simply write:
let plotTwoFunctions minX maxX f1 f2 =
Chart.Combine
[ Chart.Line [ for x in minX .. maxX -> x, f1 x ]
Chart.Line [ for x in minX .. maxX -> x, f2 x ] ]
This creates two individual line charts (with data generated by the two functions) and then combines them into a single chart using Chart.Combine.
Related
In a program that revolves around maths, I find myself using Plotly.NET (F#) to display user-defined functions. This works quite well, but there are cases where a function has discontinuities or even chunks defined over certain regions. For example, for the function f(x) defined by 0 if x <= 0 and 10 elsewhere, the expected graph (I used Wolfram Alpha here) is:
With Plotly and the code below,
let fn x = if x <= 0.0 then 0.0 else 10.0
let xs = [ -10.0 .. 0.1 .. 10.0 ]
let ys = Seq.map fn xs
Chart.Line(xs, ys, UseDefaults = false)
|> Chart.withTitle #"$f(x)$"
|> Chart.savePNG("example")
I get this graph:
As you can see, Plotly connects two points that shouldn't be connected (and I don't blame it, that's how the lib works). I wonder then how to avoid this kind of behaviour, which often happens with piecewise defined functions.
If possible, I would like a solution that is general enough to be applied to all functions / graphs, as my program does not encode functions in advance, the user enters them. The research I've done doesn't lead me anywhere, unfortunately, and the documentation doesn't show an example for what I want.
PS: also, you may have noticed, Plotly doesn't display the LaTex in the exported image, according to my research this is a known issue with Python, but if you know how to solve this with the .NET version of the lib, I'm also interested!
I don't think there's any way for Plotly to know that the function is discontinuous. Note that the vertical portion of your chart isn't truly vertical, because x jumps from 0.0 to 0.1.
However, you can still achieve the effect you're looking for by creating a separate chart for each piece of the function, and then combining them:
let color = Color.fromString "Blue"
let xsA = [ -10.0 .. 0.0 ]
let ysA = xsA |> Seq.map (fun _ -> 0.0)
let chartA = Chart.Line(xsA, ysA, LineColor = color)
let xsB = [ 0.0 .. 10.0 ]
let ysB = xsB |> Seq.map (fun _ -> 10.0)
let chartB = Chart.Line(xsB, ysB, LineColor = color)
[ chartA; chartB ]
|> Chart.combine
|> Chart.withLegend false
|> Chart.show
Note that there are actually two distinct points for x = 0 in the combined chart, so it's technically not a function. (Perhaps there's some way to show that the top piece is open, while the bottom piece is closed in Plotly, but I don't know how.) Result is:
I have an assignment, where I have made to classes, "Drone" and "Airspace".
I need to move the x,y coordinates of all the drones that exists, which I have stored in x.drones in the Airspace class, as a list. However I am having trouble accessing and manipulating the list in x.drones. How can I in a class change the values of a list from another member?
I have given all my code so far down under, it's the method x.flyDrones I need help with.
I have tried to make a for loop, but that doesn't seem to work.
module Drones
type Drone(x1:int, y1:int, x2:int, y2:int, spd:int) =
static let mutable count = 0
static let mutable hinsta = []
let mutable position = (x1,y1)
let mutable destination = (x2,y2)
let mutable speed = spd
do count <-count + 1
let mutable insta = [x1,y1,x2,y2,spd]
do hinsta <- hinsta # insta
static member Count = count
static member Collection = hinsta
member x.fly =
if fst destination - fst position > 0 then position <- (x1+1,y1)
else if snd destination - snd position > 0 then position <- (x1,y1+1)
member x.isFinished =
fst destination - fst position = 0 && snd destination - snd position = 0
type Airspace =
member x.drones = Drone.Collection
member x.droneDist(drone1, drone2) =
let dist = ((fst drone1 - fst drone2),(snd drone1 - snd drone2))
dist
member x.flyDrones =
for drone in x.drones do
member x.addDrone(x1, y1, x2, y2, spd) = new Drone(x1, y1, x2, y2, spd)
//member x.willCollide(time) =
Sorry to disappoint you, OliverVS, but this ain't gonna fly. I believe I can see your intentions, but this needs a complete rewrite rather than a fix. Let me try to give you some advice.
First of all, why do you use classes at all? I wouldn't, but I'll assume the assignment says you should, so let's continue doing that.
The Drone class should be responsible for keeping the information for a single drone, but you have x.drones in Airspace that is set to Drone.Collection, which is not a collection or a list of Drone, but rather a list of int*int*int*int*int that is kept as a static list in the Drone class. That's really messy. Why do the Drone class have statics? The Airspace class should be responsible for keeping a list of Drone, so get rid of all the statics in Drone, and instead find a way to maintain the list of drones in the Airspace class.
The Airspace type has instance members - not static members - yet there is no constructor. You will likely want a default constructor, so it should be Airspace() =, and then you need to instantiate a single Airspace.
You don't need to maintain a count when you can simply find the count of drones by getting the length of the list of drones.
You can solve the entire assignment without using a single static anywhere.
Start member names with capital letter.
Members like x.fly and x.flyDrones are not methods, because they don't have arguments. You need at least the () argument for them to be methods.
x.addDrone correctly creates a new Drone, but it should add it to the list of drones that the Airspace class should have.
The Drone class uses tuples for position and destination, which leads to considerably less readable code with use of fst and snd throughout. Don't do that. In general, use records instead of tuples if the tuples cause confusion. Don't use mysterious names like x1, y1, x2, y2, spd, insta, hinsta, fly.
A few changes will make the Drone class a little better, though I haven't followed all of my own advice here. The point of shadowing the class arguments with mutables of the same name, is 1) indeed that they are mutable which means they can be changed, and 2) block further use of the original arguments, which avoids confusion of any sort, whether it's while writing code or reading code.
type Drone(x1:int, y1:int, x2:int, y2:int, speed:int) =
let mutable x1 = x1
let mutable y1 = y1
let mutable x2 = x2
let mutable y2 = y2
let mutable speed = speed
member _.X1 = x1
member _.Y1 = y1
member _.X2 = x2
member _.Y2 = y2
member _.Speed = speed
member _.Fly() =
if x2 - x1 > 0 then x1 <- x1 + 1
elif y2 - y1 > 0 then y1 <- y1 + 1
member _.IsFinished = x2 - x1 = 0 && y2 - y1 = 0
I am using the F# skeleton tracking template provided by KinectContrib. The template in C# that does the same thing works so I know the hardware is OK.
I am using Windows Kinect SDK v1.8.
The program will track once in a rare while but with no consistent pattern. I have been playing with the code since last night so I am looking for someone to confirm the same behavior on another system or for any pointers on how to change the code.
Thanks in advance.
This is the template code:
#light
open System
open System.Windows
open System.Windows.Media.Imaging
open Microsoft.Kinect
open System.Diagnostics
let sensor = KinectSensor.KinectSensors.[0]
//The main canvas that is handling the ellipses
let canvas = new System.Windows.Controls.Canvas()
canvas.Background <- System.Windows.Media.Brushes.Transparent
let ds : byte = Convert.ToByte(1)
let dummySkeleton : Skeleton = new Skeleton(TrackingState = SkeletonTrackingState.Tracked)
// Thanks to Richard Minerich (#rickasaurus) for helping me figure out
// some array concepts in F#.
let mutable pixelData : byte array = [| |]
let mutable skeletons : Skeleton array = [| |]
//Right hand ellipse
let rhEllipse = new System.Windows.Shapes.Ellipse()
rhEllipse.Height <- 20.0
rhEllipse.Width <- 20.0
rhEllipse.Fill <- System.Windows.Media.Brushes.Red
rhEllipse.Stroke <- System.Windows.Media.Brushes.White
//Left hand ellipse
let lhEllipse = new System.Windows.Shapes.Ellipse()
lhEllipse.Height <- 20.0
lhEllipse.Width <- 20.0
lhEllipse.Fill <- System.Windows.Media.Brushes.Red
lhEllipse.Stroke <- System.Windows.Media.Brushes.White
//Head ellipse
let hEllipse = new System.Windows.Shapes.Ellipse()
hEllipse.Height <- 20.0
hEllipse.Width <- 20.0
hEllipse.Fill <- System.Windows.Media.Brushes.Red
hEllipse.Stroke <- System.Windows.Media.Brushes.White
canvas.Children.Add(rhEllipse) |> ignore
canvas.Children.Add(lhEllipse) |> ignore
canvas.Children.Add(hEllipse) |> ignore
let grid = new System.Windows.Controls.Grid()
let winImage = new System.Windows.Controls.Image()
winImage.Height <- 600.0
winImage.Width <- 800.0
grid.Children.Add(winImage) |> ignore
grid.Children.Add(canvas) |> ignore
//Video frame is ready to be processed.
let VideoFrameReady (sender : obj) (args: ColorImageFrameReadyEventArgs) =
let receivedData = ref false
using (args.OpenColorImageFrame()) (fun r ->
if (r <> null) then
(
pixelData <- Array.create r.PixelDataLength ds
//Array.Resize(ref pixelData, r.PixelDataLength)
r.CopyPixelDataTo(pixelData)
receivedData := true
)
if (receivedData <> ref false) then
(
winImage.Source <- BitmapSource.Create(640, 480, 96.0, 96.0, Media.PixelFormats.Bgr32, null, pixelData, 640 * 4)
)
)
//Required to correlate the skeleton data to the PC screen
//IMPORTANT NOTE: Code for vector scaling was imported from the Coding4Fun Kinect Toolkit
//available here: http://c4fkinect.codeplex.com/
//I only used this part to avoid adding an extra reference.
let ScaleVector (length : float32, position : float32) =
let value = (((length / 1.0f) / 2.0f) * position) + (length / 2.0f)
if value > length then
length
elif value < 0.0f then
0.0f
else
value
//This will set the ellipse positions depending on the passed instance and joint
let SetEllipsePosition (ellipse : System.Windows.Shapes.Ellipse, joint : Joint) =
let vector = new Microsoft.Kinect.SkeletonPoint(X = ScaleVector(640.0f, joint.Position.X), Y=ScaleVector(480.0f, -joint.Position.Y),Z=joint.Position.Z)
let mutable uJoint = joint
uJoint.TrackingState <- JointTrackingState.Tracked
uJoint.Position <- vector
System.Windows.Controls.Canvas.SetLeft(ellipse,(float uJoint.Position.X))
System.Windows.Controls.Canvas.SetTop(ellipse,(float uJoint.Position.Y))
//Triggered when a new skeleton frame is ready for processing
let SkeletonFrameReady (sender : obj) (args: SkeletonFrameReadyEventArgs) =
let receivedData = ref false
using (args.OpenSkeletonFrame()) (fun r ->
if (r <> null) then
(
skeletons <- Array.create r.SkeletonArrayLength dummySkeleton
r.CopySkeletonDataTo(skeletons)
for i in skeletons do
Debug.WriteLine(i.TrackingState.ToString())
receivedData := true
)
if (receivedData <> ref false) then
(
for i in skeletons do
if i.TrackingState <> SkeletonTrackingState.NotTracked then
(
let currentSkeleton = i
SetEllipsePosition(hEllipse, currentSkeleton.Joints.[JointType.Head])
SetEllipsePosition(lhEllipse, currentSkeleton.Joints.[JointType.HandLeft])
SetEllipsePosition(rhEllipse, currentSkeleton.Joints.[JointType.HandRight])
)
)
)
let WindowLoaded (sender : obj) (args: RoutedEventArgs) =
sensor.Start()
sensor.ColorStream.Enable()
sensor.SkeletonStream.Enable()
sensor.ColorFrameReady.AddHandler(new EventHandler<ColorImageFrameReadyEventArgs>(VideoFrameReady))
sensor.SkeletonFrameReady.AddHandler(new EventHandler<SkeletonFrameReadyEventArgs>(SkeletonFrameReady))
let WindowUnloaded (sender : obj) (args: RoutedEventArgs) =
sensor.Stop()
//Defining the structure of the test window
let window = new Window()
window.Width <- 800.0
window.Height <- 600.0
window.Title <- "Kinect Skeleton Application"
window.Loaded.AddHandler(new RoutedEventHandler(WindowLoaded))
window.Unloaded.AddHandler(new RoutedEventHandler(WindowUnloaded))
window.Content <- grid
window.Show()
[<STAThread()>]
do
let app = new Application() in
app.Run(window) |> ignore
I ended up rewriting it based off of this post http://channel9.msdn.com/coding4fun/kinect/Kinecting-with-F and the skeleton tracking is now working. Still interested in why the original code doesn't work as well.
// Learn more about F# at http://fsharp.net
#light
open System
open System.Windows
open System.Windows.Media.Imaging
open System.Windows.Threading
open Microsoft.Kinect
open System.Diagnostics
[<STAThread>]
do
let sensor = KinectSensor.KinectSensors.[0]
sensor.SkeletonStream.Enable()
sensor.Start()
// Set-up the WPF window and its contents
let width = 1024.
let height = 768.
let w = Window(Width=width, Height=height)
let g = Controls.Grid()
let c = Controls.Canvas()
let hd = Shapes.Rectangle(Fill=Media.Brushes.Red, Width=15., Height=15.)
let rh = Shapes.Rectangle(Fill=Media.Brushes.Blue, Width=15., Height=15.)
let lh = Shapes.Rectangle(Fill=Media.Brushes.Green, Width=15., Height=15.)
ignore <| c.Children.Add hd
ignore <| c.Children.Add rh
ignore <| c.Children.Add lh
ignore <| g.Children.Add c
w.Content <- g
w.Unloaded.Add(fun args -> sensor.Stop())
let getDisplayPosition w h (joint : Joint) =
let x = ((w * (float)joint.Position.X + 2.0) / 4.0) + (w/2.0)
let y = ((h * -(float)joint.Position.Y + 2.0) / 4.0) + (h/2.0)
System.Console.WriteLine("X:" + x.ToString() + " Y:" + y.ToString())
new Point(x,y)
let draw (joint : Joint) (sh : System.Windows.Shapes.Shape) =
let p = getDisplayPosition width height joint
sh.Dispatcher.Invoke(DispatcherPriority.Render, Action(fun () -> System.Windows.Controls.Canvas.SetLeft(sh, p.X))) |> ignore
sh.Dispatcher.Invoke(DispatcherPriority.Render, Action(fun () -> System.Windows.Controls.Canvas.SetTop(sh, p.Y))) |> ignore
let drawJoints (sk : Skeleton) =
draw (sk.Joints.Item(JointType.Head)) hd
draw (sk.Joints.Item(JointType.WristRight)) rh
draw (sk.Joints.Item(JointType.WristLeft)) lh
let skeleton (sensor : KinectSensor) =
let rec loop () =
async {
let! args = Async.AwaitEvent sensor.SkeletonFrameReady
use frame = args.OpenSkeletonFrame()
let skeletons : Skeleton[] = Array.zeroCreate(frame.SkeletonArrayLength)
frame.CopySkeletonDataTo(skeletons)
skeletons
|> Seq.filter (fun s -> s.TrackingState <> SkeletonTrackingState.NotTracked)
|> Seq.iter (fun s -> drawJoints s)
return! loop ()
}
loop ()
skeleton sensor |> Async.Start
let a = Application()
ignore <| a.Run(w)
In F#, any value bindings (e.g., let or do) you declare within a module itself will be executed the first time the module is opened or accessed from another module. If you're familiar with C#, you can think of these value bindings as executing within a type constructor (i.e., a static constructor).
I suspect the reason the second version of your code works, but not the first, is that in the second version, you're creating the Window and drawing the shapes into it from within the STA thread running the application's message loop. In the first version, I'd guess that code is executing on some other thread, and that's why it isn't working as expected.
There's nothing wrong with the second version of your code, but a more-canonical F# approach would be to lift your functions (getDisplayPosition, draw, etc.) out of the top-level do binding. That makes the code a bit easier to read by making it obvious that those functions aren't capturing any of the local values created within the do.
I have to create a path between two given points in a grid in Prolog. The code I have so far is:
createPath(GridSize, BeginPosition, EndPosition, VisitedPoints, Path):-
nextStep(BeginPosition, NextStep, GridSize),
(
NextStep \== EndPosition,
->
nonmember(NextStep, VisitedPoints),
add(NextStep, VisitedPoints, NewVisitedPoints),
add(NextStep, Path, NewPath),
createPath(GridSize, NextStep, EndPosition, NewVisitedPoints, NewPath)
;
???
).
A little bit of explanation of my code:
GridSize is just an integer. If it is 2, the grid is a 2x2 grid. So all the grids are square.
The BeginPosition and EndPosition are shown like this: pos(X,Y).
The function nextStep looks for a valid neigbor of a given position. The values of X and Y have to be between 1 and the grid size. I've declared 4 different predicates of nextStep: X + 1, X - 1, Y + 1 and Y - 1.
This is the code:
nextStep(pos(X,Y),pos(X1,Y),GridSize):-
X1 is X + 1,
X1 =< GridSize.
nextStep(pos(X,Y),pos(X1,Y),_):-
X1 is X - 1,
X1 >= 1.
nextStep(pos(X,Y),pos(X,Y1),GridSize):-
Y1 is Y + 1,
Y1 =< GridSize.
nextStep(pos(X,Y),pos(X,Y1),_):-
Y1 is Y - 1,
Y1 >= 1.
nonmember returns true if a given element doesn't occur in a given list.
add adds an element to a given list, and returns the list with that element in it.
Another thing to know about VisitedPoints: Initially the BeginPosition and EndPosition are stored in that list. For example, if I want to find a path in a 2x2 grid, and I have to avoid point pos(2,1), then I will call the function like this:
createPath(2, pos(1,1), pos(2,2), [pos(1,1),pos(2,2),pos(2,1)], X).
The result I should get of it, should be:
X = [pos(1,2)]
Because that is the point needed to connect pos(1,1) and pos(2,2).
My question is, how can I stop the code from running when NextStep == EndPosition. In other words, what do I have to type at the location of the '???' ? Or am I handling this problem the wrong way?
I'm pretty new to Prolog, and making the step from object oriented languages to this is pretty hard.
I hope somebody can answer my question.
Kind regards,
Walle
I think you just placed the 'assignment' to path at the wrong place
createPath(GridSize, BeginPosition, EndPosition, VisitedPoints, Path):-
nextStep(BeginPosition, NextStep, GridSize),
(
NextStep \== EndPosition,
->
nonmember(NextStep, VisitedPoints),
add(NextStep, VisitedPoints, NewVisitedPoints),
% add(NextStep, Path, NewPath),
% createPath(GridSize, NextStep, EndPosition, NewVisitedPoints, NewPath)
createPath(GridSize, NextStep, EndPosition, NewVisitedPoints, Path)
;
% ???
% bind on success the output variable, maybe add EndPosition
Path = VisitedPoints
).
Maybe this is not entirely worth an answer, but a comment would be a bit 'blurry'
Hello everyone
I have converted a project in C# to F# that paints the Mandelbrot set.
Unfortunately does it take around one minute to render a full screen so I am try to find some ways to speed it up.
It is one call that take almost all of the time:
Array.map (fun x -> this.colorArray.[CalcZ x]) xyArray
xyArray (double * double) [] => (array of tuple of double)
colorArray is an array of int32 length = 255
CalcZ is defined as:
let CalcZ (coord:double * double) =
let maxIterations = 255
let rec CalcZHelper (xCoord:double) (yCoord:double) // line break inserted
(x:double) (y:double) iters =
let newx = x * x + xCoord - y * y
let newy = 2.0 * x * y + yCoord
match newx, newy, iters with
| _ when Math.Abs newx > 2.0 -> iters
| _ when Math.Abs newy > 2.0 -> iters
| _ when iters = maxIterations -> iters
| _ -> CalcZHelper xCoord yCoord newx newy (iters + 1)
CalcZHelper (fst coord) (snd coord) (fst coord) (snd coord) 0
As I only use around half of the processor capacity is an idea to use more threads and specifically Array.Parallel.map, translates to system.threading.tasks.parallel
Now my question
A naive solution, would be:
Array.Parallel.map (fun x -> this.colorArray.[CalcZ x]) xyArray
but that took twice the time, how can I rewrite this to take less time, or can I take some other way to utilize the processor better?
Thanks in advance
Gorgen
---edit---
the function that is calling CalcZ looks like this:
let GetMatrix =
let halfX = double bitmap.PixelWidth * scale / 2.0
let halfY = double bitmap.PixelHeight * scale / 2.0
let rect:Mandelbrot.Rectangle =
{xMax = centerX + halfX; xMin = centerX - halfX;
yMax = centerY + halfY; yMin = centerY - halfY;}
let size:Mandelbrot.Size =
{x = bitmap.PixelWidth; y = bitmap.PixelHeight}
let xyList = GenerateXYTuple rect size
let xyArray = Array.ofList xyList
Array.map (fun x -> this.colorArray.[CalcZ x]) xyArray
let region:Int32Rect = new Int32Rect(0,0,bitmap.PixelWidth,bitmap.PixelHeight)
bitmap.WritePixels(region, GetMatrix, bitmap.PixelWidth * 4, region.X, region.Y);
GenerateXYTuple:
let GenerateXYTuple (rect:Rectangle) (pixels:Size) =
let xStep = (rect.xMax - rect.xMin)/double pixels.x
let yStep = (rect.yMax - rect.yMin)/double pixels.y
[for column in 0..pixels.y - 1 do
for row in 0..pixels.x - 1 do
yield (rect.xMin + xStep * double row,
rect.yMax - yStep * double column)]
---edit---
Following a suggestion from kvb (thanks a lot!) in a comment to my question, I built the program in Release mode. Building in the Relase mode generally speeded up things.
Just building in Release took me from 50s to around 30s, moving in all transforms on the array so it all happens in one pass made it around 10 seconds faster. At last using the Array.Parallel.init brought me to just over 11 seconds.
What I learnt from this is.... Use the release mode when timing things and using parallel constructs...
One more time, thanks for the help I have recieved.
--edit--
by using SSE assember from a native dll I have been able to slash the time from around 12 seconds to 1.2 seconds for a full screen of the most computational intensive points. Unfortunately I don't have a graphics processor...
Gorgen
Per the comment on the original post, here is the code I wrote to test the function. The fast version only takes a few seconds on my average workstation. It is fully sequential, and has no parallel code.
It's moderately long, so I posted it on another site: http://pastebin.com/Rjj8EzCA
I'm suspecting that the slowdown you are seeing is in the rendering code.
I don't think that the Array.Parallel.map function (which uses Parallel.For from .NET 4.0 under the cover) should have trouble parallelizing the operation if it runs a simple function ~1 million times. However, I encountered some weird performance behavior in a similar case when F# didn't optimize the call to the lambda function (in some way).
I'd try taking a copy of the Parallel.map function from the F# sources and adding inline. Try adding the following map function to your code and use it instead of the one from F# libraries:
let inline map (f: 'T -> 'U) (array : 'T[]) : 'U[]=
let inputLength = array.Length
let result = Array.zeroCreate inputLength
Parallel.For(0, inputLength, fun i ->
result.[i] <- f array.[i]) |> ignore
result
As an aside, it looks like you're generating an array of coordinates and then mapping it to an array of results. You don't need to create the coordinate array if you use the init function instead of map: Array.Parallel.init 1000 (fun y -> Array.init 1000 (fun x -> this.colorArray.[CalcZ (x, y)]))
EDIT: The following may be inaccurate:
Your problem could be that you call a tiny function a million times, causing the scheduling overhead to overwhelm that actual work you're doing. You should partition the array into much larger chunks so that each individual task takes a millisecond or so. You can use an array of arrays so that you would call Array.Parallel.map on the outer arrays and Array.map on the inner arrays. That way each parallel operation will operate on a whole row of pixels instead of just a single pixel.