From dfa9483c3b7bb3e4e70a9c798794a5f660ea4b56 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 8 Feb 2014 22:29:22 +0100 Subject: [PATCH] Generate: clarifications and simplifications, F# module, docs --- MathNet.Numerics.sln | 1 + docs/content/Generate.fsx | 252 +++++++++++++++++++++++++++ docs/content/index.fsx | 18 +- docs/tools/templates/template.cshtml | 2 +- src/FSharp/Generate.fs | 8 +- src/Numerics/Generate.cs | 244 ++++++++++++++------------ src/UnitTests/GenerateTests.cs | 8 +- 7 files changed, 399 insertions(+), 134 deletions(-) create mode 100644 docs/content/Generate.fsx diff --git a/MathNet.Numerics.sln b/MathNet.Numerics.sln index 1f53dab4..184df47b 100644 --- a/MathNet.Numerics.sln +++ b/MathNet.Numerics.sln @@ -43,6 +43,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{039229DA-A docs\content\DescriptiveStatistics.fsx = docs\content\DescriptiveStatistics.fsx docs\content\Distance.fsx = docs\content\Distance.fsx docs\content\Functions.fsx = docs\content\Functions.fsx + docs\content\Generate.fsx = docs\content\Generate.fsx docs\content\index.fsx = docs\content\index.fsx docs\content\IntegralTransforms.fsx = docs\content\IntegralTransforms.fsx docs\content\Integration.fsx = docs\content\Integration.fsx diff --git a/docs/content/Generate.fsx b/docs/content/Generate.fsx new file mode 100644 index 00000000..f997176a --- /dev/null +++ b/docs/content/Generate.fsx @@ -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(); +*) diff --git a/docs/content/index.fsx b/docs/content/index.fsx index ad7fd025..aa32ceca 100644 --- a/docs/content/index.fsx +++ b/docs/content/index.fsx @@ -12,14 +12,14 @@ Installation Instructions The recommended way to get Math.NET Numerics is to use NuGet. The following packages are provided and maintained in the public [NuGet Gallery](https://nuget.org/profiles/mathnet/): -- `MathNet.Numerics` - core package, including .Net 4, .Net 3.5 and portable/PCL builds -- `MathNet.Numerics.FSharp` - optional extensions for a better F# experience -- `MathNet.Numerics.Data.Text` - optional extensions for text-based matrix input/output -- `MathNet.Numerics.Data.Matlab` - optional extensions for MATLAB matrix file input/output -- `MathNet.Numerics.MKL.Win-x86` - optional Linear Algebra MKL native provider -- `MathNet.Numerics.MKL.Win-x64` - optional Linear Algebra MKL native provider -- `MathNet.Numerics.Signed` - strong-named version of the core package *(not recommended)* -- `MathNet.Numerics.FSharp.Signed` - strong-named version of the F# package *(not recommended)* +- `MathNet.Numerics` - core package, including .Net 4, .Net 3.5 and portable/PCL builds. +- `MathNet.Numerics.FSharp` - optional extensions for a better F# experience. BigRational. +- `MathNet.Numerics.Data.Text` - optional extensions for text-based matrix input/output. +- `MathNet.Numerics.Data.Matlab` - optional extensions for MATLAB matrix file input/output. +- `MathNet.Numerics.MKL.Win-x86` - optional Linear Algebra MKL native provider. +- `MathNet.Numerics.MKL.Win-x64` - optional Linear Algebra MKL native provider. +- `MathNet.Numerics.Signed` - strong-named version of the core package *(not recommended)*. +- `MathNet.Numerics.FSharp.Signed` - strong-named version of the F# package *(not recommended)*. Alternatively you can also download the binaries in Zip packages, available on [CodePlex](http://mathnetnumerics.codeplex.com/releases): @@ -30,7 +30,7 @@ Supported Platforms: - .Net 4.0, .Net 3.5 and Mono: Windows, Linux and Mac. - PCL Portable Profiles 47 and 136: Silverlight 5, Windows Phone 8, .NET for Windows Store apps (Metro). -- PCL/Xamarin: Android, iOS +- PCL/Xamarin: Android, iOS *(not verified due to lack of license and devices)* Building Math.NET Numerics -------------------------- diff --git a/docs/tools/templates/template.cshtml b/docs/tools/templates/template.cshtml index a50a3db3..8de5c355 100644 --- a/docs/tools/templates/template.cshtml +++ b/docs/tools/templates/template.cshtml @@ -58,7 +58,7 @@
  • Complex Numbers
  • Euclid & Number Theory
  • Combinatorics
  • -
  • Generating Data
  • +
  • Generating Data
  • Random Numbers
  • Probability Distributions
  • Distance Metrics
  • diff --git a/src/FSharp/Generate.fs b/src/FSharp/Generate.fs index f10ea30c..11272074 100644 --- a/src/FSharp/Generate.fs +++ b/src/FSharp/Generate.fs @@ -58,7 +58,7 @@ module Generate = let inline randomMap2 length distribution map = Generate.RandomMap2(length, distribution, tobcl2 map) let inline randomMap2Seq distribution map = Generate.RandomMap2Sequence(distribution, tobcl2 map) - let inline randomUniformMap length map = Generate.RandomUniformMap(length, tobcl map) - let inline randomUniformMapSeq map = Generate.RandomUniformMapSequence(tobcl map) - let inline randomUniformMap2 length map = Generate.RandomUniformMap2(length, tobcl2 map) - let inline randomUniformMap2Seq map = Generate.RandomUniformMap2Sequence(tobcl2 map) + let inline uniformMap length map = Generate.UniformMap(length, tobcl map) + let inline uniformMapSeq map = Generate.UniformMapSequence(tobcl map) + let inline uniformMap2 length map = Generate.UniformMap2(length, tobcl2 map) + let inline uniformMap2Seq map = Generate.UniformMap2Sequence(tobcl2 map) diff --git a/src/Numerics/Generate.cs b/src/Numerics/Generate.cs index 903f73b5..5ff9cfa5 100644 --- a/src/Numerics/Generate.cs +++ b/src/Numerics/Generate.cs @@ -196,7 +196,7 @@ namespace MathNet.Numerics } /// - /// Generate a linearly spaced sample vector within the inclusive interval (start, stop) and the provide step. + /// Generate a linearly spaced sample vector within the inclusive interval (start, stop) and the provided step. /// The start value is aways included as first value, but stop is only included if it stop-start is a multiple of step. /// Equivalent to MATLAB double colon operator (::). /// @@ -457,44 +457,29 @@ namespace MathNet.Numerics } /// - /// Create a Dirac Delta Impulse sample vector. + /// Create a Kronecker Delta impulse sample vector. /// /// The number of samples to generate. - /// impulse sequence period. -1 for single impulse only. /// The maximal reached peak. /// Offset to the time axis. Zero or positive. - public static double[] Impulse(int length, int period, double amplitude, int delay) + public static double[] Impulse(int length, double amplitude, int delay) { var data = new double[length]; - if (period <= 0) - { - if (delay >= 0 && delay < length) - { - data[delay] = amplitude; - } - } - else + if (delay >= 0 && delay < length) { - delay = ((delay%period) + period)%period; - while (delay < length) - { - data[delay] = amplitude; - delay += period; - } + data[delay] = amplitude; } return data; } - /// - /// Create a Dirac Delta Impulse sample vector. + /// Create a Kronecker Delta impulse sample vector. /// - /// impulse sequence period. -1 for single impulse only. /// The maximal reached peak. - /// Offset to the time axis. Zero or positive. - public static IEnumerable ImpulseSequence(int period, double amplitude, int delay) + /// Offset to the time axis, hence the sample index of the impulse. + public static IEnumerable ImpulseSequence(double amplitude, int delay) { - if (period <= 0) + if (delay >= 0) { for (int i = 0; i < delay; i++) { @@ -502,87 +487,141 @@ namespace MathNet.Numerics } yield return amplitude; + } - while (true) - { - yield return 0d; - } + while (true) + { + yield return 0d; } - else + } + + /// + /// Create a periodic Kronecker Delta impulse sample vector. + /// + /// The number of samples to generate. + /// impulse sequence period. + /// The maximal reached peak. + /// Offset to the time axis. Zero or positive. + public static double[] PeriodicImpulse(int length, int period, double amplitude, int delay) + { + var data = new double[length]; + delay = Euclid.Modulus(delay, period); + while (delay < length) { - delay = ((delay%period) + period)%period; + data[delay] = amplitude; + delay += period; + } + return data; + } - for (int i = 0; i < delay; i++) - { - yield return 0d; - } + /// + /// Create a Kronecker Delta impulse sample vector. + /// + /// impulse sequence period. + /// The maximal reached peak. + /// Offset to the time axis. Zero or positive. + public static IEnumerable PeriodicImpulseSequence(int period, double amplitude, int delay) + { + delay = Euclid.Modulus(delay, period); - while (true) - { - yield return amplitude; + for (int i = 0; i < delay; i++) + { + yield return 0d; + } - for (int i = 1; i < period; i++) - { - yield return 0d; - } + while (true) + { + yield return amplitude; + + for (int i = 1; i < period; i++) + { + yield return 0d; } } } /// - /// Create random samples. + /// Create random samples, uniform between 0 and 1. + /// Faster than other methods but with reduced guarantees on randomness. /// - public static double[] Random(int length, IContinuousDistribution distribution) + public static double[] Uniform(int length) { - return distribution.Samples().Take(length).ToArray(); + return SystemRandomSource.Doubles(length); } /// - /// Create an infinite random sample sequence. + /// Create an infinite random sample sequence, uniform between 0 and 1. + /// Faster than other methods but with reduced guarantees on randomness. /// - public static IEnumerable Random(IContinuousDistribution distribution) + public static IEnumerable UniformSequence() { - return distribution.Samples(); + return SystemRandomSource.DoubleSequence(); } + /// - /// Create random samples, uniform between 0 and 1. + /// Generate samples by sampling a function at samples from a probability distribution, uniform between 0 and 1. /// Faster than other methods but with reduced guarantees on randomness. /// - public static double[] RandomUniform(int length) + public static T[] UniformMap(int length, Func map) { - return SystemRandomSource.Doubles(length); + var samples = SystemRandomSource.Doubles(length); + var data = new T[length]; + for (int i = 0; i < data.Length; i++) + { + data[i] = map(samples[i]); + } + return data; } /// - /// Create an infinite random sample sequence, uniform between 0 and 1. + /// Generate a sample sequence by sampling a function at samples from a probability distribution, uniform between 0 and 1. /// Faster than other methods but with reduced guarantees on randomness. /// - public static IEnumerable RandomUniform() + public static IEnumerable UniformMapSequence(Func map) { - return SystemRandomSource.DoubleSequence(); + return SystemRandomSource.DoubleSequence().Select(map); } /// - /// Create random samples. + /// Generate samples by sampling a function at sample pairs from a probability distribution, uniform between 0 and 1. + /// Faster than other methods but with reduced guarantees on randomness. /// - public static Complex[] RandomComplex(int length, IContinuousDistribution distribution) + public static T[] UniformMap2(int length, Func map) { - return RandomMap2(length, distribution, (r, i) => new Complex(r, i)); + var samples1 = SystemRandomSource.Doubles(length); + var samples2 = SystemRandomSource.Doubles(length); + var data = new T[length]; + for (int i = 0; i < data.Length; i++) + { + data[i] = map(samples1[i], samples2[i]); + } + return data; } /// - /// Create an infinite random sample sequence. + /// Generate a sample sequence by sampling a function at sample pairs from a probability distribution, uniform between 0 and 1. + /// Faster than other methods but with reduced guarantees on randomness. /// - public static IEnumerable RandomComplex(IContinuousDistribution distribution) + public static IEnumerable UniformMap2Sequence(Func map) { - return RandomMap2Sequence(distribution, (r, i) => new Complex(r, i)); + var rnd1 = SystemRandomSource.Default; + for (int i = 0; i < 128; i++) + { + yield return map(rnd1.NextDouble(), rnd1.NextDouble()); + } + + var rnd2 = new System.Random(RandomSeed.Robust()); + while (true) + { + yield return map(rnd2.NextDouble(), rnd2.NextDouble()); + } } /// /// Create samples with independent amplitudes of normal distribution and a flat spectral density. /// - public static double[] WhiteGaussianNoise(int length, double mean, double standardDeviation) + public static double[] Gaussian(int length, double mean, double standardDeviation) { return Normal.Samples(SystemRandomSource.Default, mean, standardDeviation).Take(length).ToArray(); } @@ -590,7 +629,7 @@ namespace MathNet.Numerics /// /// Create an infinite sample sequence with independent amplitudes of normal distribution and a flat spectral density. /// - public static IEnumerable WhiteGaussianNoiseSequence(double mean, double standardDeviation) + public static IEnumerable GaussianSequence(double mean, double standardDeviation) { return Normal.Samples(SystemRandomSource.Default, mean, standardDeviation); } @@ -603,9 +642,9 @@ namespace MathNet.Numerics /// Skewness beta-parameter of the stable distribution /// Scale c-parameter of the stable distribution /// Location mu-parameter of the stable distribution - public static double[] StableNoise(int length, double alpha, double beta, double scale, double location) + public static double[] Stable(int length, double alpha, double beta, double scale, double location) { - return Stable.Samples(SystemRandomSource.Default, alpha, beta, scale, location).Take(length).ToArray(); + return Distributions.Stable.Samples(SystemRandomSource.Default, alpha, beta, scale, location).Take(length).ToArray(); } /// @@ -615,110 +654,83 @@ namespace MathNet.Numerics /// Skewness beta-parameter of the stable distribution /// Scale c-parameter of the stable distribution /// Location mu-parameter of the stable distribution - public static IEnumerable StableNoiseSequence(double alpha, double beta, double scale, double location) + public static IEnumerable StableSequence(double alpha, double beta, double scale, double location) { - return Stable.Samples(SystemRandomSource.Default, alpha, beta, scale, location); + return Distributions.Stable.Samples(SystemRandomSource.Default, alpha, beta, scale, location); } /// - /// Generate samples by sampling a function at samples from a probability distribution. + /// Create random samples. /// - public static T[] RandomMap(int length, IContinuousDistribution distribution, Func map) + public static double[] Random(int length, IContinuousDistribution distribution) { - var data = new T[length]; - for (int i = 0; i < data.Length; i++) - { - data[i] = map(distribution.Sample()); - } - return data; + return distribution.Samples().Take(length).ToArray(); } /// - /// Generate a sample sequence by sampling a function at samples from a probability distribution. + /// Create an infinite random sample sequence. /// - public static IEnumerable RandomMapSequence(IContinuousDistribution distribution, Func map) + public static IEnumerable Random(IContinuousDistribution distribution) { - return distribution.Samples().Select(map); + return distribution.Samples(); } /// - /// Generate samples by sampling a function at sample pairs from a probability distribution. + /// Create random samples. /// - public static T[] RandomMap2(int length, IContinuousDistribution distribution, Func map) + public static Complex[] RandomComplex(int length, IContinuousDistribution distribution) { - var data = new T[length]; - for (int i = 0; i < data.Length; i++) - { - data[i] = map(distribution.Sample(), distribution.Sample()); - } - return data; + return RandomMap2(length, distribution, (r, i) => new Complex(r, i)); } /// - /// Generate a sample sequence by sampling a function at sample pairs from a probability distribution. + /// Create an infinite random sample sequence. /// - public static IEnumerable RandomMap2Sequence(IContinuousDistribution distribution, Func map) + public static IEnumerable RandomComplex(IContinuousDistribution distribution) { - return distribution.Samples().Zip(distribution.Samples(), map); + return RandomMap2Sequence(distribution, (r, i) => new Complex(r, i)); } /// - /// Generate samples by sampling a function at samples from a probability distribution, uniform between 0 and 1. - /// Faster than other methods but with reduced guarantees on randomness. + /// Generate samples by sampling a function at samples from a probability distribution. /// - public static T[] RandomUniformMap(int length, Func map) + public static T[] RandomMap(int length, IContinuousDistribution distribution, Func map) { - var samples = SystemRandomSource.Doubles(length); var data = new T[length]; for (int i = 0; i < data.Length; i++) { - data[i] = map(samples[i]); + data[i] = map(distribution.Sample()); } return data; } /// - /// Generate a sample sequence by sampling a function at samples from a probability distribution, uniform between 0 and 1. - /// Faster than other methods but with reduced guarantees on randomness. + /// Generate a sample sequence by sampling a function at samples from a probability distribution. /// - public static IEnumerable RandomUniformMapSequence(Func map) + public static IEnumerable RandomMapSequence(IContinuousDistribution distribution, Func map) { - return SystemRandomSource.DoubleSequence().Select(map); + return distribution.Samples().Select(map); } /// - /// Generate samples by sampling a function at sample pairs from a probability distribution, uniform between 0 and 1. - /// Faster than other methods but with reduced guarantees on randomness. + /// Generate samples by sampling a function at sample pairs from a probability distribution. /// - public static T[] RandomUniformMap2(int length, Func map) + public static T[] RandomMap2(int length, IContinuousDistribution distribution, Func map) { - var samples1 = SystemRandomSource.Doubles(length); - var samples2 = SystemRandomSource.Doubles(length); var data = new T[length]; for (int i = 0; i < data.Length; i++) { - data[i] = map(samples1[i], samples2[i]); + data[i] = map(distribution.Sample(), distribution.Sample()); } return data; } /// - /// Generate a sample sequence by sampling a function at sample pairs from a probability distribution, uniform between 0 and 1. - /// Faster than other methods but with reduced guarantees on randomness. + /// Generate a sample sequence by sampling a function at sample pairs from a probability distribution. /// - public static IEnumerable RandomUniformMap2Sequence(Func map) + public static IEnumerable RandomMap2Sequence(IContinuousDistribution distribution, Func map) { - var rnd1 = SystemRandomSource.Default; - for (int i = 0; i < 128; i++) - { - yield return map(rnd1.NextDouble(), rnd1.NextDouble()); - } - - var rnd2 = new System.Random(RandomSeed.Robust()); - while (true) - { - yield return map(rnd2.NextDouble(), rnd2.NextDouble()); - } + return distribution.Samples().Zip(distribution.Samples(), map); } } } diff --git a/src/UnitTests/GenerateTests.cs b/src/UnitTests/GenerateTests.cs index 30f71e16..34172a1f 100644 --- a/src/UnitTests/GenerateTests.cs +++ b/src/UnitTests/GenerateTests.cs @@ -188,12 +188,12 @@ namespace MathNet.Numerics.UnitTests public void ImpulseConsistentWithSequence() { Assert.That( - Generate.ImpulseSequence(0, 5, 40).Take(1000).ToArray(), - Is.EqualTo(Generate.Impulse(1000, 0, 5, 40)).AsCollection); + Generate.PeriodicImpulseSequence(0, 5, 40).Take(1000).ToArray(), + Is.EqualTo(Generate.PeriodicImpulse(1000, 0, 5, 40)).AsCollection); Assert.That( - Generate.ImpulseSequence(100, 5, 40).Take(1000).ToArray(), - Is.EqualTo(Generate.Impulse(1000, 100, 5, 40)).AsCollection); + Generate.PeriodicImpulseSequence(100, 5, 40).Take(1000).ToArray(), + Is.EqualTo(Generate.PeriodicImpulse(1000, 100, 5, 40)).AsCollection); } } }