forked from tsai/mathnet-numerics
7 changed files with 399 additions and 134 deletions
@ -0,0 +1,252 @@ |
|||||
|
(*** hide ***) |
||||
|
#I "../../out/lib/net40" |
||||
|
#r "MathNet.Numerics.dll" |
||||
|
#r "MathNet.Numerics.FSharp.dll" |
||||
|
open System |
||||
|
open MathNet.Numerics |
||||
|
let a = [| 2.0; 4.0; 3.0; 6.0 |] |
||||
|
|
||||
|
(** |
||||
|
Generating Data |
||||
|
=============== |
||||
|
|
||||
|
Numerics is all about analyzing and manipulating numeric data. But unless you can read in data from an external |
||||
|
file, source or e.g. with the excellent [F# Type Providers](http://fsharp.github.io/FSharp.Data/), |
||||
|
you may need to generate synthetic or random data locally, or transform existing data into a new form. |
||||
|
The `Generate` class can help you in all these scenarios with a set of static functions generating either |
||||
|
an array or an IEnumerable sequence. |
||||
|
|
||||
|
There is some overlap with LINQ, in case of F# also with some integrated language features and its fundamental types. |
||||
|
This is intended for simplicity and consistency between array and sequence operations, as LINQ only supports sequences. |
||||
|
|
||||
|
|
||||
|
Linear Range |
||||
|
------------ |
||||
|
|
||||
|
Generates a linearly spaced array within the inclusive interval between start and stop, |
||||
|
and either a provided step or a step of 1.0. Linear range is equivalent to the |
||||
|
single colon `:` and double colon `::` operators in MATLAB. |
||||
|
|
||||
|
F# has built in linear range support in array comprehensions with the colon operator: |
||||
|
*) |
||||
|
|
||||
|
[ 10.0 .. 2.0 .. 15.0 ] |
||||
|
// [fsi:val it : float list = [10.0; 12.0; 14.0] ] |
||||
|
[ for x in 10.0 .. 2.0 .. 15.0 -> sin x ] |
||||
|
// [fsi:val it : float list = [-0.5440211109; -0.536572918; 0.9906073557] ] |
||||
|
|
||||
|
(** |
||||
|
In C# you can get the same result with `LinearRange`: |
||||
|
|
||||
|
[lang=csharp] |
||||
|
Generate.LinearRange(10, 2, 15); // returns array { 10.0, 12.0, 14.0 } |
||||
|
Generate.LinearRangeMap(10, 2, 15, Math.Sin); // applies sin(x) to each value |
||||
|
|
||||
|
Most of the routines in the `Generate` class have variants with a `Map` suffix. |
||||
|
Instead of returning an array with the generated numbers, these routines instead |
||||
|
apply the generated numbers to a custom function and return an array with the results. |
||||
|
Similarly, some routines have variants with a `Sequence` suffix that return |
||||
|
lazy enumerable sequences instead of arrays. |
||||
|
|
||||
|
|
||||
|
Linear-Spaced and Log-Spaced |
||||
|
---------------------------- |
||||
|
|
||||
|
Generates a linearly or log-spaced array within an interval, but other than linear range |
||||
|
where the step is provided, here we instead provide the number of values we want. |
||||
|
This is equivalent to the linspace and logspace operators in MATLAB. |
||||
|
|
||||
|
[lang=csharp] |
||||
|
Generate.LinearSpaced(11, 0.0, 1.0); // returns array { 0.0, 0.1, 0.2, .., 1.0 } |
||||
|
Generate.LinearSpacedMap(15, 0.0, Math.Pi, Math.Sin); // applies sin(x) to each value |
||||
|
|
||||
|
In F# you can also use: |
||||
|
*) |
||||
|
|
||||
|
Generate.linearSpacedMap 15 0.0 Math.PI sin |
||||
|
// [fsi:val it : float [] = ] |
||||
|
// [fsi: [|0.0; 0.222520934; 0.4338837391; 0.6234898019; 0.7818314825; 0.9009688679; ] |
||||
|
// [fsi: 0.9749279122; 1.0; 0.9749279122; 0.9009688679; 0.7818314825; 0.6234898019; ] |
||||
|
// [fsi: 0.4338837391; 0.222520934; 1.224606354e-16|] ] |
||||
|
|
||||
|
(** |
||||
|
`LogSpaced` works the same way but instead of the values $10^x$ it spaces the decade exponents $x$ linearly |
||||
|
between the provided two exponents. |
||||
|
|
||||
|
[lang=csharp] |
||||
|
Generate.LogSpaced(4,0,3); // returns array { 1, 10, 100, 1000 } |
||||
|
|
||||
|
|
||||
|
Kronecker Delta Impulse |
||||
|
----------------------- |
||||
|
|
||||
|
The Kronecker delta $\delta[n]$ is a fundamental signal in time-discrete signal processing, |
||||
|
often referred to as *unit impulse*. When applied to a system, the resulting output is the system's *impulse response*. |
||||
|
It is closely related to the Dirac delta impulse function $\delta(x)$ in continuous signal processing. |
||||
|
|
||||
|
$$$ |
||||
|
\delta[n] = \begin{cases} 0 &\mbox{if } n \ne 0 \\ 1 & \mbox{if } n = 0\end{cases} |
||||
|
|
||||
|
The `Impulse` routine generates a Kronecker delta impulse, but also accepts a sample delay |
||||
|
parameter $d$ and amplitude $A$ such that the resulting generated signal is |
||||
|
|
||||
|
$$$ |
||||
|
s[n] = A\cdot\delta[n-d] = \begin{cases} 0 &\mbox{if } n \ne d \\ A & \mbox{if } n = d\end{cases} |
||||
|
|
||||
|
There is also a periodic version in `PeriodicImpulse` which accepts an additional `period` parameter. |
||||
|
*) |
||||
|
|
||||
|
Generate.Impulse(8, 2.0, 3) |
||||
|
// [fsi:val it : float [] = [|0.0; 0.0; 0.0; 2.0; 0.0; 0.0; 0.0; 0.0|] ] |
||||
|
|
||||
|
Generate.PeriodicImpulse(8, 3, 10.0, 1) |
||||
|
// [fsi:val it : float [] = [|0.0; 10.0; 0.0; 0.0; 10.0; 0.0; 0.0; 10.0|] ] |
||||
|
|
||||
|
(** |
||||
|
Heaviside Step |
||||
|
-------------- |
||||
|
|
||||
|
Another fundamental signal in signal processing, the Heaviside step function $H[n]$ |
||||
|
is the integral of the Dirac delta impulse and represents a signal that switches on |
||||
|
at a specified time and then stays on indefinitely. In discrete time: |
||||
|
|
||||
|
$$$ |
||||
|
H[n] = \begin{cases} 0 &\mbox{if } n < 0 \\ 1 & \mbox{if } n \ge 0\end{cases} |
||||
|
|
||||
|
The `Step` routines generates a Heaviside step, but just like the Kronecker Delta impulse |
||||
|
also accepts a sample delay parameter $d$ and amplitude $A$ such that the resulting generated signal is |
||||
|
|
||||
|
$$$ |
||||
|
s[n] = A\cdot H[n-d] = \begin{cases} 0 &\mbox{if } n < d \\ A & \mbox{if } n \ge d\end{cases} |
||||
|
*) |
||||
|
|
||||
|
Generate.Step(8, 2.0, 3) |
||||
|
// [fsi:val it : float [] = [|0.0; 0.0; 0.0; 2.0; 2.0; 2.0; 2.0; 2.0|] ] |
||||
|
|
||||
|
|
||||
|
(** |
||||
|
Periodic Sawtooth |
||||
|
----------------- |
||||
|
|
||||
|
Generates an array of the given length with a periodic upper forward sawtooth signal, |
||||
|
i.e. a line starting at zero up to some amplitude $A$, then drop back to zero instantly and start afresh. |
||||
|
The sawtooth can be used to turn any arbitrary function defined over the interval $[0,A)$ into a |
||||
|
periodic function by repeating it continuously. |
||||
|
|
||||
|
Mathematically, the sawtooth can be described using the fractional part function |
||||
|
$\mathrm{frac}(x) \equiv x - \lfloor x \rfloor$, frequency $\nu$ and phase $\theta$ as |
||||
|
|
||||
|
$$$ |
||||
|
s(x) = A\cdot\mathrm{frac}\left(x\nu+\frac{\theta}{A}\right) |
||||
|
|
||||
|
In a trigonometric interpretation the signal represents the angular position $\alpha$ of a point moving endlessly |
||||
|
around a circle with radius $\frac{A}{2\pi}$ (and thus circumference $A$) in constant speed, |
||||
|
normalized to strictly $0\le\alpha < A$. |
||||
|
|
||||
|
`Generate.Periodic(length,samplingRate,frequency,amplitude,phase,delay)` |
||||
|
|
||||
|
* **Sampling Rate**: Number of samples $r$ per time unit. If the time unit is 1s, the sampling rate has unit Hz. |
||||
|
* **Frequency**: Frequency $\nu$ of the signal, in sawtooth-periods per time unit. If the time unit is 1s, the frequency has unit Hz. |
||||
|
For a desired number of samples $n$ per sawtooth-period and sampling rate $r$ choose $\nu=\frac{r}{n}$. |
||||
|
* **Amplitude**: The theoretical maximum value $A$, which is never reached and logically equivalent to zero. |
||||
|
The circumference of the circle. Typically $1$ or $2\pi$. |
||||
|
* **Phase**: Optional initial value or phase offset. Contributes to $\theta$. |
||||
|
* **Delay**: Optional initial delay, in samples. Contributes to $\theta$. |
||||
|
|
||||
|
The equivalent map function accepts a custom map lambda as second argument after the length: |
||||
|
*) |
||||
|
|
||||
|
Generate.periodicMap 15 ((+) 100.0) 1000.0 100.0 10.0 0.0 0 |
||||
|
// [fsi:val it : float [] = ] |
||||
|
// [fsi: [|100.0; 101.0; 102.0; 103.0; 104.0; 105.0; 106.0; 107.0; 108.0; 109.0; ] |
||||
|
// [fsi: 100.0; 101.0; 102.0; 103.0; 104.0|] ] |
||||
|
|
||||
|
(** |
||||
|
Sinusoidal |
||||
|
---------- |
||||
|
|
||||
|
Generates a Sine wave array of the given length. This is equivalent of applying a scaled trigonometric Sine function |
||||
|
to a periodic sawtooth of amplitude $2\pi$. |
||||
|
|
||||
|
$$$ |
||||
|
s(x) = A\cdot\sin(2\pi\nu x + \theta) |
||||
|
|
||||
|
`Generate.Sinusoidal(length,samplingRate,frequency,amplitude,mean,phase,delay)` |
||||
|
|
||||
|
[lang=csharp] |
||||
|
Generate.Sinusoidal(15, 1000.0, 100.0, 10.0); |
||||
|
// returns array { 0, 5.9, 9.5, 9.5, 5.9, 0, -5.9, ... } |
||||
|
|
||||
|
|
||||
|
Random |
||||
|
------ |
||||
|
|
||||
|
Generate random sequences by sampling from a random distribution. |
||||
|
|
||||
|
|
||||
|
#### Uniform Distribution |
||||
|
|
||||
|
Generate sample sequences distributed uniformly between 0 and 1. |
||||
|
|
||||
|
[lang=csharp] |
||||
|
Generate.Uniform(100); // e.g. 0.867421787170424, 0.236744313145403, ... |
||||
|
|
||||
|
Uniform supports mapping to functions with not only one but also two uniform variables |
||||
|
as arguments, with `UniformMap` and `UniformMap2`. As usual, lazy sequences can be |
||||
|
generated using the variants with the `Sequence` suffix, e.g. `UniformMap2Sequence`. |
||||
|
|
||||
|
|
||||
|
#### Non-Uniform Distributions |
||||
|
|
||||
|
Instead of uniform we can also sample from other distributions. |
||||
|
|
||||
|
* `Gaussian` - sample an array or sequence form a normal or standard distribution |
||||
|
* `Stable` - sample from a Levy alpha-stable distribution. |
||||
|
|
||||
|
In addition, the `Random` functions accept a custom distribution instance to sample |
||||
|
from. See the section about random numbers and probability distributions for details. |
||||
|
|
||||
|
|
||||
|
Map |
||||
|
--- |
||||
|
|
||||
|
Generates a new array or sequence where each new values is the result of applying the provided function |
||||
|
the the corresponding value in the input data. |
||||
|
|
||||
|
[lang=csharp] |
||||
|
var a = new double[] { 2.0, 4.0, 3.0, 6.0 }; |
||||
|
Generate.Map(a, x => x + 1.0); // returns array { 3.0, 5.0, 4.0, 7.0 } |
||||
|
|
||||
|
In F# you'd typically use the Array module to the same effect (and should continue to do so): |
||||
|
*) |
||||
|
|
||||
|
Array.map ((+) 1.0) a |
||||
|
// [fsi:val it : float [] = [|3.0; 5.0; 4.0; 7.0|] ] |
||||
|
|
||||
|
(** |
||||
|
...but no equivalent operation is available in the .NET base class libraries (BCL) for C#. |
||||
|
You can use LINQ, but that operates on sequences instead of arrays: |
||||
|
|
||||
|
[lang=csharp] |
||||
|
a.Select(x => x + 1.0).ToArray(); |
||||
|
|
||||
|
Similarly, with `Map2` you can also map a function accepting two inputs to two input arrays: |
||||
|
*) |
||||
|
|
||||
|
let b = [| 1.0; -1.0; 2.0; -2.0 |] |
||||
|
Generate.Map2(a, b, fun x y -> x + y) |
||||
|
// [fsi:val it : float [] = [|3.0; 3.0; 5.0; 4.0|] ] |
||||
|
|
||||
|
(** |
||||
|
Typical F# equivalent: |
||||
|
*) |
||||
|
|
||||
|
Array.map2 (+) a b |
||||
|
// [fsi:val it : float [] = [|3.0; 3.0; 5.0; 4.0|] ] |
||||
|
|
||||
|
(** |
||||
|
And in C# with LINQ: |
||||
|
|
||||
|
[lang=csharp] |
||||
|
a.Zip(b, (x, y) => x + y).ToArray(); |
||||
|
*) |
||||
Loading…
Reference in new issue