-- # Basic use of literate Futhark
--
-- The ``futhark literate`` command translates Futhark programs to
-- Markdown files. Specially formatted comments, called *directives*,
-- are replaced with executing parts of the program. The purpose of
-- literate Futhark is to write small example
-- programs with quick visualisations and such.
--
-- For example, let us define a function for generating a linearly
-- spaced vector:
def linspace (n: i64) (start: f64) (end: f64) : [n]f64 =
tabulate n (\i -> start + f64.i64 i * ((end-start)/f64.i64 n))
-- With an evaluation directive, we can show what it evaluates to:
-- > linspace 10i64 0f64 10f64
-- > linspace 10i64 5f64 10f64
-- If you look at the [source file](https://futhark-lang.org/examples/literate-basics.fut), you'll see
-- that that the results are not part of what I wrote. Those are
-- automatically inserted by ``futhark literate``.
--
-- The expressions in the directives are not full Futhark expressions.
-- Rather, they are written in a tiny subset called FutharkScript,
-- which supports little besides calling top-level functions in the
-- literate file. An important restriction is that all numeric literals
-- must have a type suffix, as above. When we write literate files, we
-- must put all nontrivial code in ordinary Futhark definitions, which
-- support the full language. The Futhark part of a a literate Futhark program is
-- compiled like an ordinary Futhark program.
--
-- Some values are boring when viewed as arrays of numbers.
def linspace_2d n start end : [n][n](f64,f64) =
map (\x -> map (\y -> (x,y)) (linspace n start end))
(linspace n start end)
def spirals n v : [n][n]f64 =
let f (x, y) =
f64.sgn (f64.cos (f64.sqrt (x**2+y**2)))
in map (map f) (linspace_2d n (-v) v)
-- For expressions with an appropriate type, ``futhark literate`` can
-- convert them to an image:
-- > :img spirals 200i64 30f64
-- Two-dimensional arrays of floats are interpreted as greyscale. A
-- two-dimensional array of 32-bit integers is interpreted in RGB
-- format.
def colours n v : [n][n]u32 =
let f (x, y) =
(u32.f64 (x*y) & 0xFF) << 16 |
(u32.f64 (x+y) & 0xFF) << 8 |
(u32.f64 (f64.cos x-f64.sin y) & 0xFF)
in map (map f) (linspace_2d n (-v) v)
-- This is more colourful.
-- > :img colours 200i64 30f64
-- Using the `:img` directive, we can draw whatever we want.
-- However, if we just want to plot the value of some function, it
-- would be pretty awkward to implement graphing every time.
def xys f n start end : ([n]f64, [n]f64) =
unzip (map (\x -> (x, f x)) (linspace n start end))
def plot_sqrt = xys f64.sqrt
def plot_sin = xys f64.sin
def plot_cos = xys f64.cos
def plot_inv = xys (1/)
-- The `:plot2d` directive lets us to a quick plot of pairs of *x* and
-- *y* value arrays.
-- > :plot2d plot_sqrt 1000i64 0f64 100f64
-- And if we want multiple plots in the same graph, then we pass a
-- record of such pairs.
-- > :plot2d {sqrt=plot_sqrt 1000i64 0f64 25f64,
-- sin=plot_sin 1000i64 0f64 25f64,
-- cos=plot_cos 1000i64 0f64 25f64,
-- inv=plot_inv 1000i64 1f64 25f64}
-- The `:plot2d` directive shells out to
-- [Gnuplot](http://www.gnuplot.info/). For advanced needs, such as
-- 3D plotting, we can write the Gnuplot commands ourselves.
def plot3d n start end : ([]f64, []f64, []f64) =
let f (x, y) =
let z = f64.sin(5*x) *f64.cos(5*y)/5
in (x, y, z)
in unzip3 (flatten (map (map f) (linspace_2d n start end)))
-- We pass in the data as a a record, and each field becomes a Gnuplot
-- variable that contains the name of a data file.
-- > :gnuplot {ourdata=plot3d 100i64 0f64 2f64};
-- set palette rgbformulae 33,13,10
-- set dgrid3d 100,100
-- splot ourdata u 1:2:3 with pm3d notitle