diff --git a/src/Examples/Interpolation/AkimaSpline.cs b/src/Examples/Interpolation/AkimaSpline.cs index 196bf33a..ac7d3f35 100644 --- a/src/Examples/Interpolation/AkimaSpline.cs +++ b/src/Examples/Interpolation/AkimaSpline.cs @@ -25,6 +25,7 @@ // using System; +using MathNet.Numerics; using MathNet.Numerics.Interpolation; using MathNet.Numerics.Random; using MathNet.Numerics.Signals; @@ -67,8 +68,8 @@ namespace Examples.InterpolationExamples { // 1. Generate 10 samples of the function x*x-2*x on interval [0, 10] Console.WriteLine(@"1. Generate 10 samples of the function x*x-2*x on interval [0, 10]"); - double[] points; - var values = SignalGenerator.EquidistantInterval(TargetFunction, 0, 10, 10, out points); + double[] points = Generate.LinearSpaced(10, 0, 10); + var values = Generate.Map(points, TargetFunction); Console.WriteLine(); // 2. Create akima spline interpolation diff --git a/src/Examples/Interpolation/LinearBetweenPoints.cs b/src/Examples/Interpolation/LinearBetweenPoints.cs index ef92badc..1d8c9a6a 100644 --- a/src/Examples/Interpolation/LinearBetweenPoints.cs +++ b/src/Examples/Interpolation/LinearBetweenPoints.cs @@ -67,8 +67,8 @@ namespace Examples.InterpolationExamples { // 1. Generate 20 samples of the function x*x-2*x on interval [0, 10] Console.WriteLine(@"1. Generate 20 samples of the function x*x-2*x on interval [0, 10]"); - double[] points; - var values = SignalGenerator.EquidistantInterval(TargetFunction, 0, 10, 20, out points); + double[] points = Generate.LinearSpaced(20, 0, 10); + var values = Generate.Map(points, TargetFunction); Console.WriteLine(); // 2. Create a linear spline interpolation based on arbitrary points diff --git a/src/Examples/Interpolation/RationalWithPoles.cs b/src/Examples/Interpolation/RationalWithPoles.cs index 01f378ba..d1438b93 100644 --- a/src/Examples/Interpolation/RationalWithPoles.cs +++ b/src/Examples/Interpolation/RationalWithPoles.cs @@ -66,8 +66,8 @@ namespace Examples.InterpolationExamples { // 1. Generate 20 samples of the function f(x) = x on interval [-5, 5] Console.WriteLine(@"1. Generate 20 samples of the function f(x) = x on interval [-5, 5]"); - double[] points; - var values = SignalGenerator.EquidistantInterval(TargetFunction, -5, 5, 20, out points); + double[] points = Generate.LinearSpaced(20, -5, 5); + var values = Generate.Map(points, TargetFunction); Console.WriteLine(); // 2. Create a burlish stoer rational interpolation based on arbitrary points diff --git a/src/Examples/Interpolation/RationalWithoutPoles.cs b/src/Examples/Interpolation/RationalWithoutPoles.cs index 9a042861..961fb299 100644 --- a/src/Examples/Interpolation/RationalWithoutPoles.cs +++ b/src/Examples/Interpolation/RationalWithoutPoles.cs @@ -67,8 +67,8 @@ namespace Examples.InterpolationExamples { // 1. Generate 10 samples of the function 1/(1+x*x) on interval [-5, 5] Console.WriteLine(@"1. Generate 10 samples of the function 1/(1+x*x) on interval [-5, 5]"); - double[] points; - var values = SignalGenerator.EquidistantInterval(TargetFunction, -5, 5, 10, out points); + double[] points = Generate.LinearSpaced(10, -5, 5); + var values = Generate.Map(points, TargetFunction); Console.WriteLine(); // 2. Create a floater hormann rational pole-free interpolation based on arbitrary points diff --git a/src/Examples/Signals/Equidistant.cs b/src/Examples/Signals/Equidistant.cs index 39075e61..7c738f37 100644 --- a/src/Examples/Signals/Equidistant.cs +++ b/src/Examples/Signals/Equidistant.cs @@ -25,6 +25,7 @@ // using System; +using MathNet.Numerics; using MathNet.Numerics.Signals; namespace Examples.SignalsExamples @@ -62,7 +63,7 @@ namespace Examples.SignalsExamples public void Run() { // 1. Get 11 samples of f(x) = (x * x) / 2 equidistant within interval [-5, 5] - var result = SignalGenerator.EquidistantInterval(Function, -5, 5, 11); + var result = Generate.LinearSpacedMap(11, -5, 5, Function); Console.WriteLine(@"1. Get 11 samples of f(x) = (x * x) / 2 equidistant within interval [-5, 5]"); for (var i = 0; i < result.Length; i++) { @@ -73,8 +74,8 @@ namespace Examples.SignalsExamples Console.WriteLine(); // 2. Get 10 samples of f(x) = (x * x) / 2 equidistant starting at x=1 with step = 0.5 and retrieve sample points - double[] samplePoints; - result = SignalGenerator.EquidistantStartingAt(Function, 1, 0.5, 10, out samplePoints); + double[] samplePoints = Generate.LinearSpaced(10, 1.0, 5.5); + result = Generate.Map(samplePoints, Function); Console.WriteLine(@"2. Get 10 samples of f(x) = (x * x) / 2 equidistant starting at x=1 with step = 0.5 and retrieve sample points"); Console.Write(@"Points: "); for (var i = 0; i < samplePoints.Length; i++) @@ -93,24 +94,13 @@ namespace Examples.SignalsExamples Console.WriteLine(); // 3. Get 10 samples of f(x) = (x * x) / 2 equidistant within period = 10 and period offset = 5 - result = SignalGenerator.EquidistantPeriodic(Function, 10, 5, 10); + result = Generate.PeriodicMap(10, Function, 10, 1.0, 10, 5); Console.WriteLine(@"3. Get 10 samples of f(x) = (x * x) / 2 equidistant within period = 10 and period offset = 5"); for (var i = 0; i < result.Length; i++) { Console.Write(result[i].ToString("N") + @" "); } - Console.WriteLine(); - Console.WriteLine(); - - // 4. Sample f(x) = (x * x) / 2 equidistant to an integer-domain function starting at x = 0 and step = 2 - var equidistant = SignalGenerator.EquidistantToFunction(Function, 0, 2); - Console.WriteLine(@" 4. Sample f(x) = (x * x) / 2 equidistant to an integer-domain function starting at x = 0 and step = 2"); - for (var i = 0; i < 10; i++) - { - Console.Write(equidistant(i).ToString("N") + @" "); - } - Console.WriteLine(); } diff --git a/src/Examples/SpecialFunctions/ErrorFunction.cs b/src/Examples/SpecialFunctions/ErrorFunction.cs index d5393733..039e839c 100644 --- a/src/Examples/SpecialFunctions/ErrorFunction.cs +++ b/src/Examples/SpecialFunctions/ErrorFunction.cs @@ -26,7 +26,6 @@ using System; using MathNet.Numerics; -using MathNet.Numerics.Signals; namespace Examples.SpecialFunctionsExamples { @@ -70,7 +69,7 @@ namespace Examples.SpecialFunctionsExamples // 2. Sample 10 values of the error function in [-1.0; 1.0] Console.WriteLine(@"2. Sample 10 values of the error function in [-1.0; 1.0]"); - var data = SignalGenerator.EquidistantInterval(SpecialFunctions.Erf, -1.0, 1.0, 10); + var data = Generate.LinearSpacedMap(10, -1.0, 1.0, SpecialFunctions.Erf); for (var i = 0; i < data.Length; i++) { Console.Write(data[i].ToString("N") + @" "); @@ -86,7 +85,7 @@ namespace Examples.SpecialFunctionsExamples // 4. Sample 10 values of the complementary error function in [-1.0; 1.0] Console.WriteLine(@"4. Sample 10 values of the complementary error function in [-1.0; 1.0]"); - data = SignalGenerator.EquidistantInterval(SpecialFunctions.Erfc, -1.0, 1.0, 10); + data = Generate.LinearSpacedMap(10, -1.0, 1.0, SpecialFunctions.Erfc); for (var i = 0; i < data.Length; i++) { Console.Write(data[i].ToString("N") + @" "); @@ -102,7 +101,7 @@ namespace Examples.SpecialFunctionsExamples // 6. Sample 10 values of the inverse error function in [-1.0; 1.0] Console.WriteLine(@"6. Sample 10 values of the inverse error function in [-1.0; 1.0]"); - data = SignalGenerator.EquidistantInterval(SpecialFunctions.ErfInv, -1.0, 1.0, 10); + data = Generate.LinearSpacedMap(10, -1.0, 1.0, SpecialFunctions.ErfInv); for (var i = 0; i < data.Length; i++) { Console.Write(data[i].ToString("N") + @" "); @@ -118,7 +117,7 @@ namespace Examples.SpecialFunctionsExamples // 8. Sample 10 values of the complementary inverse error function in [-1.0; 1.0] Console.WriteLine(@"8. Sample 10 values of the complementary inverse error function in [-1.0; 1.0]"); - data = SignalGenerator.EquidistantInterval(SpecialFunctions.ErfcInv, -1.0, 1.0, 10); + data = Generate.LinearSpacedMap(10, -1.0, 1.0, SpecialFunctions.ErfcInv); for (var i = 0; i < data.Length; i++) { Console.Write(data[i].ToString("N") + @" "); diff --git a/src/Examples/Statistics.cs b/src/Examples/Statistics.cs index cd5597d4..b5c3f1aa 100644 --- a/src/Examples/Statistics.cs +++ b/src/Examples/Statistics.cs @@ -25,8 +25,8 @@ // using System; +using MathNet.Numerics; using MathNet.Numerics.Distributions; -using MathNet.Numerics.Signals; using MathNet.Numerics.Statistics; namespace Examples @@ -124,8 +124,8 @@ namespace Examples Console.WriteLine(); // 6. Correlation coefficient between 1000 samples of f(x) = x * 2 and f(x) = x * x - data = SignalGenerator.EquidistantInterval(x => x * 2, 0, 100, 1000); - dataB = SignalGenerator.EquidistantInterval(x => x * x, 0, 100, 1000); + data = Generate.LinearSpacedMap(1000, 0, 100, x => x * 2); + dataB = Generate.LinearSpacedMap(1000, 0, 100, x => x * x); Console.WriteLine(@"7. Correlation coefficient between 1000 samples of f(x) = x * 2 and f(x) = x * x is {0}", Correlation.Pearson(data, dataB).ToString("N04")); Console.WriteLine(@"8. Ranked correlation coefficient between 1000 samples of f(x) = x * 2 and f(x) = x * x is {0}", Correlation.Spearman(data, dataB).ToString("N04")); Console.WriteLine(); diff --git a/src/Numerics/Generate.cs b/src/Numerics/Generate.cs index 828a6cce..455453ba 100644 --- a/src/Numerics/Generate.cs +++ b/src/Numerics/Generate.cs @@ -43,6 +43,53 @@ namespace MathNet.Numerics public static class Generate { + /// + /// Generate samples by sampling a function at the provided points. + /// + public static T[] Map(TA[] points, Func map) + { + var res = new T[points.Length]; + for (int i = 0; i < points.Length; i++) + { + res[i] = map(points[i]); + } + return res; + } + + /// + /// Generate a sample sequence by sampling a function at the provided point sequence. + /// + public static IEnumerable MapSequence(IEnumerable points, Func map) + { + return points.Select(map); + } + + /// + /// Generate samples by sampling a function at the provided points. + /// + public static T[] Map2(TA[] pointsA, TB[] pointsB, Func map) + { + if (pointsA.Length != pointsB.Length) + { + throw new ArgumentException(Resources.ArgumentArraysSameLength, "pointsB"); + } + + var res = new T[pointsA.Length]; + for (int i = 0; i < res.Length; i++) + { + res[i] = map(pointsA[i], pointsB[i]); + } + return res; + } + + /// + /// Generate a sample sequence by sampling a function at the provided point sequence. + /// + public static IEnumerable Map2Sequence(IEnumerable pointsA, IEnumerable pointsB, Func map) + { + return pointsA.Zip(pointsB, map); + } + /// /// Generate a linearly spaced sample vector of the given length between the specified values (inclusive). /// Equivalent to MATLAB linspace but with the length as first instead of last argument. @@ -63,6 +110,25 @@ namespace MathNet.Numerics return data; } + /// + /// Generate samples by sampling a function at linearly spaced points between the specified values (inclusive). + /// + public static T[] LinearSpacedMap(int length, double start, double stop, Func map) + { + if (length <= 0) return new T[0]; + if (length == 1) return new[] { map(stop) }; + + double step = (stop - start)/(length - 1); + + var data = new T[length]; + for (int i = 0; i < data.Length; i++) + { + data[i] = map(start + i*step); + } + data[data.Length - 1] = map(stop); + return data; + } + /// /// Generate a base 10 logarithmically spaced sample vector of the given length between the specified decade exponents (inclusive). /// Equivalent to MATLAB logspace but with the length as first instead of last argument. @@ -152,12 +218,149 @@ namespace MathNet.Numerics return data; } + /// + /// Generate samples by sampling a function at linearly spaced points within the inclusive interval (start, stop) and the provide step. + /// The start value is aways included as first value, but stop is only included if it stop-start is a multiple of step. + /// + public static T[] LinearRangeMap(double start, double step, double stop, Func map) + { + if (start == stop) return new T[] { map(start) }; + if (start < stop && step < 0 || start > stop && step > 0 || step == 0d) + { + return new T[0]; + } + + var data = new T[(int)Math.Floor((stop - start)/step + 1d)]; + for (int i = 0; i < data.Length; i++) + { + data[i] = map(start + i*step); + } + return data; + } + + /// + /// Create a periodic sample vector. + /// + /// The number of samples to generate. + /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. + /// Frequency in periods per time unit (Hz). + /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// Optional phase offset. + /// Optional delay, relative to the phase. + public static double[] Periodic(int length, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) + { + double step = frequency/samplingRate*amplitude; + phase = Euclid.Modulus(phase - delay*step, amplitude); + + var data = new double[length]; + for (int i = 0, k = 0; i < data.Length; i++, k++) + { + var x = phase + k*step; + if (x >= amplitude) + { + x %= amplitude; + phase = x; + k = 0; + } + + data[i] = x; + } + return data; + } + + /// + /// Create a periodic sample vector. + /// + /// The number of samples to generate. + /// + /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. + /// Frequency in periods per time unit (Hz). + /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// Optional phase offset. + /// Optional delay, relative to the phase. + public static T[] PeriodicMap(int length, Func map, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) + { + double step = frequency/samplingRate*amplitude; + phase = Euclid.Modulus(phase - delay*step, amplitude); + + var data = new T[length]; + for (int i = 0, k = 0; i < data.Length; i++, k++) + { + var x = phase + k*step; + if (x >= amplitude) + { + x %= amplitude; + phase = x; + k = 0; + } + + data[i] = map(x); + } + return data; + } + + /// + /// Create an infinite periodic sample sequence. + /// + /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. + /// Frequency in periods per time unit (Hz). + /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// Optional phase offset. + /// Optional delay, relative to the phase. + public static IEnumerable PeriodicSequence(double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) + { + double step = frequency/samplingRate*amplitude; + phase = Euclid.Modulus(phase - delay*step, amplitude); + + int k = 0; + while (true) + { + var x = phase + (k++)*step; + if (x >= amplitude) + { + x %= amplitude; + phase = x; + k = 1; + } + + yield return x; + } + } + + /// + /// Create an infinite periodic sample sequence. + /// + /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. + /// Frequency in periods per time unit (Hz). + /// The lenght of the period when sampled at one sample per time unit. This is the interval of the periodic domain, a typical value is 1.0, or 2*Pi for angular functions. + /// Optional phase offset. + /// Optional delay, relative to the phase. + public static IEnumerable PeriodicMapSequence(Func map, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0) + { + double step = frequency/samplingRate*amplitude; + phase = Euclid.Modulus(phase - delay*step, amplitude); + + int k = 0; + while (true) + { + var x = phase + (k++)*step; + if (x >= amplitude) + { + x %= amplitude; + phase = x; + k = 0; + } + + yield return map(x); + } + } + /// /// Create a Sine sample vector. /// /// The number of samples to generate. - /// Samples per unit. - /// Frequency in samples per unit. + /// Samples per time unit (Hz). Must be larger than twice the frequency to satisfy the Nyquist criterion. + /// Frequency in periods per time unit (Hz). /// The maximal reached peak. /// The mean, or dc part, of the signal. /// Optional phase offset. @@ -168,7 +371,7 @@ namespace MathNet.Numerics phase = (phase - delay*step)%Constants.Pi2; var data = new double[length]; - for (int i = 0; i < length; i++) + for (int i = 0; i < data.Length; i++) { data[i] = mean + amplitude*Math.Sin(phase + i*step); } @@ -306,64 +509,17 @@ namespace MathNet.Numerics } } - /// - /// Generate samples by sampling a function at the provided points. - /// - public static TR[] Map(T[] points, Func map) - { - var res = new TR[points.Length]; - for (int i = 0; i < points.Length; i++) - { - res[i] = map(points[i]); - } - return res; - } - - /// - /// Generate a sample sequence by sampling a function at the provided point sequence. - /// - public static IEnumerable MapSequence(IEnumerable points, Func map) - { - return points.Select(map); - } - - /// - /// Generate samples by sampling a function at the provided points. - /// - public static TR[] Map2(TA[] pointsA, TB[] pointsB, Func map) - { - if (pointsA.Length != pointsB.Length) - { - throw new ArgumentException(Resources.ArgumentArraysSameLength, "pointsB"); - } - - var res = new TR[pointsA.Length]; - for (int i = 0; i < res.Length; i++) - { - res[i] = map(pointsA[i], pointsB[i]); - } - return res; - } - - /// - /// Generate a sample sequence by sampling a function at the provided point sequence. - /// - public static IEnumerable Map2Sequence(IEnumerable pointsA, IEnumerable pointsB, Func map) - { - return pointsA.Zip(pointsB, map); - } - /// /// Generate samples by sampling a function at samples from a probability distribution. /// public static T[] RandomMap(int length, IContinuousDistribution distribution, Func map) { - var res = new T[length]; - for (int i = 0; i < res.Length; i++) + var data = new T[length]; + for (int i = 0; i < data.Length; i++) { - res[i] = map(distribution.Sample()); + data[i] = map(distribution.Sample()); } - return res; + return data; } /// @@ -379,12 +535,12 @@ namespace MathNet.Numerics /// public static T[] RandomMap2(int length, IContinuousDistribution distribution, Func map) { - var res = new T[length]; - for (int i = 0; i < res.Length; i++) + var data = new T[length]; + for (int i = 0; i < data.Length; i++) { - res[i] = map(distribution.Sample(), distribution.Sample()); + data[i] = map(distribution.Sample(), distribution.Sample()); } - return res; + return data; } /// diff --git a/src/Numerics/Signals/SignalGenerator.Equidistant.cs b/src/Numerics/Signals/SignalGenerator.Equidistant.cs index fa9fa111..0d260022 100644 --- a/src/Numerics/Signals/SignalGenerator.Equidistant.cs +++ b/src/Numerics/Signals/SignalGenerator.Equidistant.cs @@ -56,39 +56,7 @@ namespace MathNet.Numerics.Signals double intervalEnd, int sampleCount) { - if (ReferenceEquals(function, null)) - { - throw new ArgumentNullException("function"); - } - - if (sampleCount < 0) - { - throw new ArgumentOutOfRangeException("sampleCount"); - } - - if (sampleCount == 0) - { - return new T[0]; - } - - if (sampleCount == 1) - { - return new[] { function(0.5 * (intervalBegin + intervalEnd)) }; - } - - var samples = new T[sampleCount]; - var step = (intervalEnd - intervalBegin) / (sampleCount - 1); - var current = intervalBegin; - - for (int i = 0; i < samples.Length - 1; i++) - { - samples[i] = function(current); - current += step; - } - - samples[samples.Length - 1] = function(intervalEnd); - - return samples; + return Generate.LinearSpacedMap(sampleCount, intervalBegin, intervalEnd, function); } /// @@ -111,44 +79,8 @@ namespace MathNet.Numerics.Signals int sampleCount, out double[] samplePoints) { - if (ReferenceEquals(function, null)) - { - throw new ArgumentNullException("function"); - } - - if (sampleCount < 0) - { - throw new ArgumentOutOfRangeException("sampleCount"); - } - - if (sampleCount == 0) - { - samplePoints = new double[0]; - return new T[0]; - } - - if (sampleCount == 1) - { - samplePoints = new[] { 0.5 * (intervalBegin + intervalEnd) }; - return new[] { function(samplePoints[0]) }; - } - - var samples = new T[sampleCount]; - samplePoints = new double[sampleCount]; - var step = (intervalEnd - intervalBegin) / (sampleCount - 1); - var current = intervalBegin; - - for (int i = 0; i < samples.Length - 1; i++) - { - samplePoints[i] = current; - samples[i] = function(current); - current += step; - } - - samplePoints[samplePoints.Length - 1] = intervalEnd; - samples[samples.Length - 1] = function(intervalEnd); - - return samples; + samplePoints = Generate.LinearSpaced(sampleCount, intervalBegin, intervalEnd); + return Generate.Map(samplePoints, function); } /// @@ -170,27 +102,7 @@ namespace MathNet.Numerics.Signals double periodOffset, int sampleCount) { - if (ReferenceEquals(function, null)) - { - throw new ArgumentNullException("function"); - } - - if (sampleCount < 1) - { - throw new ArgumentOutOfRangeException("sampleCount"); - } - - var samples = new T[sampleCount]; - var step = periodLength / sampleCount; - var current = periodOffset; - - for (int i = 0; i < samples.Length; i++) - { - samples[i] = function(current); - current += step; - } - - return samples; + return Generate.PeriodicMap(sampleCount, function, sampleCount, 1.0, periodLength, periodOffset); } /// @@ -214,29 +126,8 @@ namespace MathNet.Numerics.Signals int sampleCount, out double[] samplePoints) { - if (ReferenceEquals(function, null)) - { - throw new ArgumentNullException("function"); - } - - if (sampleCount < 1) - { - throw new ArgumentOutOfRangeException("sampleCount"); - } - - var samples = new T[sampleCount]; - samplePoints = new double[sampleCount]; - var step = periodLength / sampleCount; - var current = periodOffset; - - for (int i = 0; i < samples.Length; i++) - { - samplePoints[i] = current; - samples[i] = function(current); - current += step; - } - - return samples; + samplePoints = Generate.Periodic(sampleCount, sampleCount, 1.0, periodLength, periodOffset); + return Generate.Map(samplePoints, function); } /// @@ -257,26 +148,7 @@ namespace MathNet.Numerics.Signals double step, int sampleCount) { - if (ReferenceEquals(function, null)) - { - throw new ArgumentNullException("function"); - } - - if (sampleCount < 0) - { - throw new ArgumentOutOfRangeException("sampleCount"); - } - - var samples = new T[sampleCount]; - var current = start; - - for (int i = 0; i < samples.Length; i++) - { - samples[i] = function(current); - current += step; - } - - return samples; + return Generate.LinearSpacedMap(sampleCount, start, start + (sampleCount - 1)*step, function); } /// @@ -299,28 +171,8 @@ namespace MathNet.Numerics.Signals int sampleCount, out double[] samplePoints) { - if (ReferenceEquals(function, null)) - { - throw new ArgumentNullException("function"); - } - - if (sampleCount < 0) - { - throw new ArgumentOutOfRangeException("sampleCount"); - } - - var samples = new T[sampleCount]; - samplePoints = new double[sampleCount]; - var current = start; - - for (int i = 0; i < samples.Length; i++) - { - samplePoints[i] = current; - samples[i] = function(current); - current += step; - } - - return samples; + samplePoints = Generate.LinearSpaced(sampleCount, start, start + (sampleCount - 1)*step); + return Generate.Map(samplePoints, function); } /// diff --git a/src/UnitTests/GenerateTests.cs b/src/UnitTests/GenerateTests.cs index 80e97483..a568cb0b 100644 --- a/src/UnitTests/GenerateTests.cs +++ b/src/UnitTests/GenerateTests.cs @@ -130,6 +130,32 @@ namespace MathNet.Numerics.UnitTests Assert.That(Generate.LinearRange(1d, -1.5d, -4d), Is.EqualTo(new[] { 1d, -0.5d, -2d, -3.5 }).AsCollection); } + [Test] + public void Periodic() + { + Assert.That(Generate.Periodic(8, 2.0, 0.5), Is.EqualTo(new[] { 0.0, 0.25, 0.5, 0.75, 0.0, 0.25, 0.5, 0.75 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.5, 1.0, delay: 1), Is.EqualTo(new[] { 0.75, 0.0, 0.25, 0.5, 0.75, 0.0, 0.25, 0.5 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.5, 1.0, delay: -1), Is.EqualTo(new[] { 0.25, 0.5, 0.75, 0.0, 0.25, 0.5, 0.75, 0.0 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.5, 1.0, phase: 0.7), Is.EqualTo(new[] { 0.7, 0.95, 0.2, 0.45, 0.7, 0.95, 0.2, 0.45 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.5, 1.0, phase: -0.3), Is.EqualTo(new[] { 0.7, 0.95, 0.2, 0.45, 0.7, 0.95, 0.2, 0.45 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.5, 2.0), Is.EqualTo(new[] { 0.0, 0.5, 1.0, 1.5, 0.0, 0.5, 1.0, 1.5 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.5, 2.0, phase: 1.9), Is.EqualTo(new[] { 1.9, 0.4, 0.9, 1.4, 1.9, 0.4, 0.9, 1.4 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 8.0, 1.0), Is.EqualTo(new[] { 0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875 }).Within(1e-12).AsCollection); + + // Angular over a full circle: + const double isq2 = Constants.Sqrt1Over2; + Assert.That(Generate.Periodic(8, 2.0, 0.5, Constants.Pi2).Select(Math.Sin), Is.EqualTo(new[] { 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0 }).Within(1e-12).AsCollection); + Assert.That(Generate.Periodic(8, 2.0, 0.25, Constants.Pi2).Select(Math.Sin), Is.EqualTo(new[] { 0.0, isq2, 1.0, isq2, 0.0, -isq2, -1.0, -isq2 }).Within(1e-12).AsCollection); + } + + [Test] + public void PreiodicConsistentWithSequence() + { + Assert.That( + Generate.PeriodicSequence(16.0, 2.0, Constants.Pi2, Constants.PiOver4, 2).Take(1000).ToArray(), + Is.EqualTo(Generate.Periodic(1000, 16.0, 2.0, Constants.Pi2, Constants.PiOver4, 2)).Within(1e-12).AsCollection); + } + [Test] public void SinusoidalConsistentWithSequence() { diff --git a/src/UnitTests/IntegralTransformsTests/FourierTest.cs b/src/UnitTests/IntegralTransformsTests/FourierTest.cs index 6921a0fc..54860777 100644 --- a/src/UnitTests/IntegralTransformsTests/FourierTest.cs +++ b/src/UnitTests/IntegralTransformsTests/FourierTest.cs @@ -28,7 +28,6 @@ using System; using MathNet.Numerics.Distributions; using MathNet.Numerics.IntegralTransforms; using MathNet.Numerics.IntegralTransforms.Algorithms; -using MathNet.Numerics.Signals; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.IntegralTransformsTests @@ -58,7 +57,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests [Test] public void NaiveTransformsRealSineCorrectly() { - var samples = SignalGenerator.EquidistantPeriodic(w => new Complex(Math.Sin(w), 0), Constants.Pi2, 0, 16); + var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2); // real-odd transforms to imaginary odd var dft = new DiscreteFourierTransform(); diff --git a/src/UnitTests/IntegralTransformsTests/MatchingNaiveTransformTest.cs b/src/UnitTests/IntegralTransformsTests/MatchingNaiveTransformTest.cs index f85dfd1b..a61e9f4f 100644 --- a/src/UnitTests/IntegralTransformsTests/MatchingNaiveTransformTest.cs +++ b/src/UnitTests/IntegralTransformsTests/MatchingNaiveTransformTest.cs @@ -28,7 +28,6 @@ using System; using MathNet.Numerics.Distributions; using MathNet.Numerics.IntegralTransforms; using MathNet.Numerics.IntegralTransforms.Algorithms; -using MathNet.Numerics.Signals; using NUnit.Framework; namespace MathNet.Numerics.UnitTests.IntegralTransformsTests @@ -80,7 +79,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests public void FourierRadix2MatchesNaiveOnRealSine(FourierOptions options) { var dft = new DiscreteFourierTransform(); - var samples = SignalGenerator.EquidistantPeriodic(w => new Complex(Math.Sin(w), 0), Constants.Pi2, 0, 16); + var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2); VerifyMatchesNaiveComplex( samples, @@ -130,7 +129,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests public void FourierBluesteinMatchesNaiveOnRealSineNonPowerOfTwo(FourierOptions options) { var dft = new DiscreteFourierTransform(); - var samples = SignalGenerator.EquidistantPeriodic(w => new Complex(Math.Sin(w), 0), Constants.Pi2, 0, 14); + var samples = Generate.PeriodicMap(14, w => new Complex(Math.Sin(w), 0), 14, 1.0, Constants.Pi2); VerifyMatchesNaiveComplex( samples,