10 changed files with 843 additions and 0 deletions
@ -0,0 +1,246 @@ |
|||||
|
// <copyright file="DftTest.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.IntegralTransformsTests |
||||
|
{ |
||||
|
using System; |
||||
|
using MbUnit.Framework; |
||||
|
using IntegralTransforms; |
||||
|
using IntegralTransforms.Algorithms; |
||||
|
|
||||
|
[TestFixture] |
||||
|
public class DftTest |
||||
|
{ |
||||
|
private static Random _random = new Random(); |
||||
|
|
||||
|
private static Complex[] ProvideSamples(int count) |
||||
|
{ |
||||
|
var samples = new Complex[count]; |
||||
|
for (int i = 0; i < samples.Length; i++) |
||||
|
{ |
||||
|
samples[i] = Complex.WithRealImaginary( |
||||
|
1 - (2 * _random.NextDouble()), |
||||
|
1 - (2 * _random.NextDouble())); |
||||
|
} |
||||
|
|
||||
|
return samples; |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void NaiveTransformsRealSineCorrectly() |
||||
|
{ |
||||
|
var realSine = new Complex[16]; |
||||
|
for (int i = 0; i < realSine.Length; i++) |
||||
|
{ |
||||
|
realSine[i] = Math.Sin(i / 8.0 * Constants.Pi); |
||||
|
} |
||||
|
|
||||
|
// real-odd transforms to imaginary odd
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
var spectrum = dft.NaiveForward(realSine, FourierOptions.Matlab); |
||||
|
|
||||
|
// all real components must be zero
|
||||
|
foreach (var c in spectrum) |
||||
|
{ |
||||
|
Assert.AreApproximatelyEqual(0, c.Real, 1e-12, "real"); |
||||
|
} |
||||
|
|
||||
|
// all imaginary components except second and last musth be zero
|
||||
|
for(int i = 0; i<spectrum.Length; i++) |
||||
|
{ |
||||
|
if(i == 1) |
||||
|
{ |
||||
|
Assert.AreApproximatelyEqual(-8, spectrum[i].Imaginary, 1e-12, "imag second"); |
||||
|
} |
||||
|
else if (i == spectrum.Length - 1) |
||||
|
{ |
||||
|
Assert.AreApproximatelyEqual(8, spectrum[i].Imaginary, 1e-12, "imag last"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Assert.AreApproximatelyEqual(0, spectrum[i].Imaginary, 1e-12, "imag"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
[Row(FourierOptions.Default)] |
||||
|
[Row(FourierOptions.Matlab)] |
||||
|
public void NaiveIsReversible(FourierOptions options) |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x80); |
||||
|
var work = new Complex[samples.Length]; |
||||
|
samples.CopyTo(work, 0); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
work = dft.NaiveForward(work, options); |
||||
|
|
||||
|
Assert.IsFalse(work.AlmostEqualListWithError(samples, 1e-12)); |
||||
|
|
||||
|
work = dft.NaiveInverse(work, options); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(samples, work, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void Radix2MatchesNaiveOnRealSine() |
||||
|
{ |
||||
|
var realSine = new Complex[16]; |
||||
|
for (int i = 0; i < realSine.Length; i++) |
||||
|
{ |
||||
|
realSine[i] = Math.Sin(i / 8.0 * Constants.Pi); |
||||
|
} |
||||
|
|
||||
|
// real-odd transforms to imaginary odd
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
var spectrumNaive = dft.NaiveForward(realSine, FourierOptions.Matlab); |
||||
|
|
||||
|
var spectrumRadix2 = new Complex[realSine.Length]; |
||||
|
realSine.CopyTo(spectrumRadix2, 0); |
||||
|
dft.Radix2Forward(spectrumRadix2, FourierOptions.Matlab); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(spectrumNaive, spectrumRadix2, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void Radix2MatchesNaiveOnRandom() |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x80); |
||||
|
var work = new Complex[samples.Length]; |
||||
|
samples.CopyTo(work, 0); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
var spectrumNaive = dft.NaiveForward(samples, FourierOptions.Matlab); |
||||
|
dft.Radix2Forward(work, FourierOptions.Matlab); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(spectrumNaive, work, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
[Row(FourierOptions.Default)] |
||||
|
[Row(FourierOptions.Matlab)] |
||||
|
public void Radix2IsReversible(FourierOptions options) |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x8000); |
||||
|
var work = new Complex[samples.Length]; |
||||
|
samples.CopyTo(work, 0); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
dft.Radix2Forward(work, options); |
||||
|
|
||||
|
Assert.IsFalse(work.AlmostEqualListWithError(samples, 1e-12)); |
||||
|
|
||||
|
dft.Radix2Inverse(work, options); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(samples, work, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void Radix2ThrowsWhenNotPowerOfTwo() |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x7F); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
|
||||
|
Assert.Throws( |
||||
|
typeof(ArgumentException), |
||||
|
() => dft.Radix2Forward(samples, FourierOptions.Default)); |
||||
|
|
||||
|
Assert.Throws( |
||||
|
typeof(ArgumentException), |
||||
|
() => dft.Radix2Inverse(samples, FourierOptions.Default)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void BluesteinMatchesNaiveOnRealSine() |
||||
|
{ |
||||
|
var realSine = new Complex[14]; |
||||
|
for (int i = 0; i < realSine.Length; i++) |
||||
|
{ |
||||
|
realSine[i] = Math.Sin(i / 7.0 * Constants.Pi); |
||||
|
} |
||||
|
|
||||
|
// real-odd transforms to imaginary odd
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
var spectrumNaive = dft.NaiveForward(realSine, FourierOptions.Matlab); |
||||
|
|
||||
|
var spectrumBluestein = new Complex[realSine.Length]; |
||||
|
realSine.CopyTo(spectrumBluestein, 0); |
||||
|
dft.BluesteinForward(spectrumBluestein, FourierOptions.Matlab); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(spectrumNaive, spectrumBluestein, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void BluesteinMatchesNaiveOnRandomPowerOfTwo() |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x80); |
||||
|
var work = new Complex[samples.Length]; |
||||
|
samples.CopyTo(work, 0); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
var spectrumNaive = dft.NaiveForward(samples, FourierOptions.Matlab); |
||||
|
dft.BluesteinForward(work, FourierOptions.Matlab); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(spectrumNaive, work, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void BluesteinMatchesNaiveOnRandomNonPowerOfTwo() |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x7F); |
||||
|
var work = new Complex[samples.Length]; |
||||
|
samples.CopyTo(work, 0); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
var spectrumNaive = dft.NaiveForward(samples, FourierOptions.Matlab); |
||||
|
dft.BluesteinForward(work, FourierOptions.Matlab); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(spectrumNaive, work, 1e-12); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
[Row(FourierOptions.Default)] |
||||
|
[Row(FourierOptions.Matlab)] |
||||
|
public void BluesteinIsReversible(FourierOptions options) |
||||
|
{ |
||||
|
var samples = ProvideSamples(0x7FFF); |
||||
|
var work = new Complex[samples.Length]; |
||||
|
samples.CopyTo(work, 0); |
||||
|
|
||||
|
var dft = new DiscreteFourierTransform(); |
||||
|
dft.BluesteinForward(work, options); |
||||
|
|
||||
|
Assert.IsFalse(work.AlmostEqualListWithError(samples, 1e-12)); |
||||
|
|
||||
|
dft.BluesteinInverse(work, options); |
||||
|
|
||||
|
AssertHelpers.AlmostEqualList(samples, work, 1e-12); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,175 @@ |
|||||
|
// <copyright file="DiscreteFourierTransform.Bluestein.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.IntegralTransforms.Algorithms |
||||
|
{ |
||||
|
using System; |
||||
|
using NumberTheory; |
||||
|
using Threading; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Complex Fast (FFT) Implementation of the Discrete Fourier Transform (DFT).
|
||||
|
/// </summary>
|
||||
|
public partial class DiscreteFourierTransform |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Generate the bluestein sequence for the provided problem size.
|
||||
|
/// </summary>
|
||||
|
/// <param name="n">Number of samples.</param>
|
||||
|
/// <returns>Bluestein sequence exp(I*Pi*k^2/N)</returns>
|
||||
|
private static Complex[] BluesteinSequence(int n) |
||||
|
{ |
||||
|
double s = Constants.Pi / n; |
||||
|
var sequence = new Complex[n]; |
||||
|
|
||||
|
for (int k = 0; k < sequence.Length; k++) |
||||
|
{ |
||||
|
double t = s * (k * k); |
||||
|
sequence[k] = Complex.WithRealImaginary(Math.Cos(t), Math.Sin(t)); |
||||
|
} |
||||
|
|
||||
|
return sequence; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Convolution with the bluestein sequence.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample Vector.</param>
|
||||
|
private static void BluesteinConvolution(Complex[] samples) |
||||
|
{ |
||||
|
int n = samples.Length; |
||||
|
Complex[] sequence = BluesteinSequence(n); |
||||
|
|
||||
|
// Padding to power of two >= 2N–1 so we can apply Radix-2 FFT.
|
||||
|
int m = ((n << 1) - 1).CeilingToPowerOfTwo(); |
||||
|
Complex[] b = new Complex[m]; |
||||
|
Complex[] a = new Complex[m]; |
||||
|
|
||||
|
Parallel.Invoke( |
||||
|
() => |
||||
|
{ |
||||
|
// Build and transform padded sequence b_k = exp(I*Pi*k^2/N)
|
||||
|
for (int i = 0; i < n; i++) |
||||
|
{ |
||||
|
b[i] = sequence[i]; |
||||
|
} |
||||
|
|
||||
|
for (int i = m - n + 1; i < b.Length; i++) |
||||
|
{ |
||||
|
b[i] = sequence[m - i]; |
||||
|
} |
||||
|
|
||||
|
Radix2(b, -1); |
||||
|
}, |
||||
|
() => |
||||
|
{ |
||||
|
// Build and transform padded sequence a_k = x_k * exp(-I*Pi*k^2/N)
|
||||
|
for (int i = 0; i < samples.Length; i++) |
||||
|
{ |
||||
|
a[i] = sequence[i].Conjugate * samples[i]; |
||||
|
} |
||||
|
|
||||
|
Radix2(a, -1); |
||||
|
}); |
||||
|
|
||||
|
for (int i = 0; i < a.Length; i++) |
||||
|
{ |
||||
|
a[i] *= b[i]; |
||||
|
} |
||||
|
|
||||
|
Radix2(a, 1); |
||||
|
|
||||
|
var nbinv = 1.0 / m; |
||||
|
for (int i = 0; i < samples.Length; i++) |
||||
|
{ |
||||
|
samples[i] = nbinv * sequence[i].Conjugate * a[i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Swap the real and imaginary parts of each sample.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample Vector.</param>
|
||||
|
private static void SwapRealImaginary(Complex[] samples) |
||||
|
{ |
||||
|
for (int i = 0; i < samples.Length; i++) |
||||
|
{ |
||||
|
samples[i] = Complex.WithRealImaginary(samples[i].Imaginary, samples[i].Real); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bluestein generic DFT, useful e.g. to verify faster algorithms.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Time-space sample vector.</param>
|
||||
|
/// <param name="exponentSign">Fourier series exponent sign.</param>
|
||||
|
internal static void Bluestein(Complex[] samples, int exponentSign) |
||||
|
{ |
||||
|
int n = samples.Length; |
||||
|
if (n.IsPowerOfTwo()) |
||||
|
{ |
||||
|
Radix2(samples, exponentSign); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (exponentSign == 1) |
||||
|
{ |
||||
|
SwapRealImaginary(samples); |
||||
|
} |
||||
|
|
||||
|
BluesteinConvolution(samples); |
||||
|
|
||||
|
if (exponentSign == 1) |
||||
|
{ |
||||
|
SwapRealImaginary(samples); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bluestein forward FFT for arbitrary sample vectors.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
public void BluesteinForward(Complex[] samples, FourierOptions options) |
||||
|
{ |
||||
|
Bluestein(samples, SignByOptions(options)); |
||||
|
ForwardScaleByOptions(options, samples); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bluestein inverse FFT for arbitrary sample vectors.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
public void BluesteinInverse(Complex[] samples, FourierOptions options) |
||||
|
{ |
||||
|
Bluestein(samples, -SignByOptions(options)); |
||||
|
InverseScaleByOptions(options, samples); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// <copyright file="DiscreteFourierTransform.Naive.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.IntegralTransforms.Algorithms |
||||
|
{ |
||||
|
using System; |
||||
|
using Threading; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Complex Fast (FFT) Implementation of the Discrete Fourier Transform (DFT).
|
||||
|
/// </summary>
|
||||
|
public partial class DiscreteFourierTransform |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Naive generic DFT, useful e.g. to verify faster algorithms.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Time-space sample vector.</param>
|
||||
|
/// <param name="exponentSign">Fourier series exponent sign.</param>
|
||||
|
/// <returns>Corresponding frequency-space vector.</returns>
|
||||
|
internal static Complex[] Naive(Complex[] samples, int exponentSign) |
||||
|
{ |
||||
|
double w0 = exponentSign * 2 * Constants.Pi / samples.Length; |
||||
|
var spectrum = new Complex[samples.Length]; |
||||
|
|
||||
|
Parallel.For( |
||||
|
0, |
||||
|
samples.Length, |
||||
|
k => |
||||
|
{ |
||||
|
double wk = w0 * k; |
||||
|
Complex sum = Complex.Zero; |
||||
|
for (int n = 0; n < samples.Length; n++) |
||||
|
{ |
||||
|
double w = n * wk; |
||||
|
sum += samples[n] * Complex.WithRealImaginary(Math.Cos(w), Math.Sin(w)); |
||||
|
} |
||||
|
|
||||
|
spectrum[k] = sum; |
||||
|
}); |
||||
|
|
||||
|
return spectrum; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Naive forward DFT, useful e.g. to verify faster algorithms.
|
||||
|
/// </summary>
|
||||
|
/// <param name="timeSpace">Time-space sample vector.</param>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <returns>Corresponding frequency-space vector.</returns>
|
||||
|
public Complex[] NaiveForward(Complex[] timeSpace, FourierOptions options) |
||||
|
{ |
||||
|
var frequencySpace = Naive(timeSpace, SignByOptions(options)); |
||||
|
ForwardScaleByOptions(options, frequencySpace); |
||||
|
return frequencySpace; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Naive inverse DFT, useful e.g. to verify faster algorithms.
|
||||
|
/// </summary>
|
||||
|
/// <param name="frequencySpace">Frequency-space sample vector.</param>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <returns>Corresponding time-space vector.</returns>
|
||||
|
public Complex[] NaiveInverse(Complex[] frequencySpace, FourierOptions options) |
||||
|
{ |
||||
|
var timeSpace = Naive(frequencySpace, -SignByOptions(options)); |
||||
|
InverseScaleByOptions(options, timeSpace); |
||||
|
return timeSpace; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,93 @@ |
|||||
|
// <copyright file="DiscreteFourierTransform.Options.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.IntegralTransforms.Algorithms |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Complex Fast (FFT) Implementation of the Discrete Fourier Transform (DFT).
|
||||
|
/// </summary>
|
||||
|
public partial class DiscreteFourierTransform |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Extract the exponent sign to be used in forward transforms according to the
|
||||
|
/// provided convention options.
|
||||
|
/// </summary>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <returns>Fourier series exponent sign.</returns>
|
||||
|
private static int SignByOptions(FourierOptions options) |
||||
|
{ |
||||
|
return (options & FourierOptions.InverseExponent) == FourierOptions.InverseExponent ? 1 : -1; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Rescale FFT-the resulting vector according to the provided convention options.
|
||||
|
/// </summary>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <param name="samples">Sample Vector.</param>
|
||||
|
private static void ForwardScaleByOptions(FourierOptions options, Complex[] samples) |
||||
|
{ |
||||
|
if ((options & FourierOptions.NoScaling) == FourierOptions.NoScaling || |
||||
|
(options & FourierOptions.AsymmetricScaling) == FourierOptions.AsymmetricScaling) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var scalingFactor = Math.Sqrt(1.0 / samples.Length); |
||||
|
for (int i = 0; i < samples.Length; i++) |
||||
|
{ |
||||
|
samples[i] *= scalingFactor; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Rescale the iFFT-resulting vector according to the provided convention options.
|
||||
|
/// </summary>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <param name="samples">Sample Vector.</param>
|
||||
|
private static void InverseScaleByOptions(FourierOptions options, Complex[] samples) |
||||
|
{ |
||||
|
if ((options & FourierOptions.NoScaling) == FourierOptions.NoScaling) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var scalingFactor = 1.0 / samples.Length; |
||||
|
if ((options & FourierOptions.AsymmetricScaling) != FourierOptions.AsymmetricScaling) |
||||
|
{ |
||||
|
scalingFactor = Math.Sqrt(scalingFactor); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < samples.Length; i++) |
||||
|
{ |
||||
|
samples[i] *= scalingFactor; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,137 @@ |
|||||
|
// <copyright file="DiscreteFourierTransform.RadixN.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.IntegralTransforms.Algorithms |
||||
|
{ |
||||
|
using System; |
||||
|
using NumberTheory; |
||||
|
using Properties; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Complex Fast (FFT) Implementation of the Discrete Fourier Transform (DFT).
|
||||
|
/// </summary>
|
||||
|
public partial class DiscreteFourierTransform |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Radix-2 Reorder Helper Method
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">Sample type</typeparam>
|
||||
|
/// <param name="samples">Sample vector</param>
|
||||
|
private static void Radix2Reorder<T>(T[] samples) |
||||
|
{ |
||||
|
int j = 0; |
||||
|
for (int i = 0; i < samples.Length - 1; i++) |
||||
|
{ |
||||
|
if (i < j) |
||||
|
{ |
||||
|
T temp = samples[i]; |
||||
|
samples[i] = samples[j]; |
||||
|
samples[j] = temp; |
||||
|
} |
||||
|
|
||||
|
int m = samples.Length; |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
m >>= 1; |
||||
|
j ^= m; |
||||
|
} while ((j & m) == 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Radix-2 Step Helper Method
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample vector.</param>
|
||||
|
/// <param name="exponentSign">Fourier series exponent sign.</param>
|
||||
|
/// <param name="levelSize">Level Group Size.</param>
|
||||
|
/// <param name="k">Index inside of the level.</param>
|
||||
|
private static void Radix2Step(Complex[] samples, int exponentSign, int levelSize, int k) |
||||
|
{ |
||||
|
// Twiddle Factor
|
||||
|
double exponent = (exponentSign * k) * Constants.Pi / levelSize; |
||||
|
Complex w = Complex.WithRealImaginary(Math.Cos(exponent), Math.Sin(exponent)); |
||||
|
|
||||
|
int step = levelSize << 1; |
||||
|
for (int i = k; i < samples.Length; i += step) |
||||
|
{ |
||||
|
Complex ai = samples[i]; |
||||
|
Complex t = w * samples[i + levelSize]; |
||||
|
samples[i] = ai + t; |
||||
|
samples[i + levelSize] = ai - t; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Radix-2 generic FFT for power-of-two sample vectors.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
|
||||
|
/// <param name="exponentSign">Fourier series exponent sign.</param>
|
||||
|
/// <exception cref="ArgumentException"/>
|
||||
|
internal static void Radix2(Complex[] samples, int exponentSign) |
||||
|
{ |
||||
|
if (!samples.Length.IsPowerOfTwo()) |
||||
|
{ |
||||
|
throw new ArgumentException(Resources.ArgumentPowerOfTwo); |
||||
|
} |
||||
|
|
||||
|
Radix2Reorder(samples); |
||||
|
for (int levelSize = 1; levelSize < samples.Length; levelSize *= 2) |
||||
|
{ |
||||
|
for (int k = 0; k <= levelSize - 1; k++) |
||||
|
{ |
||||
|
Radix2Step(samples, exponentSign, levelSize, k); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Radix-2 forward FFT for power-of-two sample vectors.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <exception cref="ArgumentException"/>
|
||||
|
public void Radix2Forward(Complex[] samples, FourierOptions options) |
||||
|
{ |
||||
|
Radix2(samples, SignByOptions(options)); |
||||
|
ForwardScaleByOptions(options, samples); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Radix-2 inverse FFT for power-of-two sample vectors.
|
||||
|
/// </summary>
|
||||
|
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
|
||||
|
/// <param name="options">Fourier Transform Convention Options.</param>
|
||||
|
/// <exception cref="ArgumentException"/>
|
||||
|
public void Radix2Inverse(Complex[] samples, FourierOptions options) |
||||
|
{ |
||||
|
Radix2(samples, -SignByOptions(options)); |
||||
|
InverseScaleByOptions(options, samples); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,73 @@ |
|||||
|
// <copyright file="FourierOptions.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://mathnet.opensourcedotnet.info
|
||||
|
//
|
||||
|
// Copyright (c) 2009 Math.NET
|
||||
|
//
|
||||
|
// Permission is hereby granted, free of charge, to any person
|
||||
|
// obtaining a copy of this software and associated documentation
|
||||
|
// files (the "Software"), to deal in the Software without
|
||||
|
// restriction, including without limitation the rights to use,
|
||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
|
// copies of the Software, and to permit persons to whom the
|
||||
|
// Software is furnished to do so, subject to the following
|
||||
|
// conditions:
|
||||
|
//
|
||||
|
// The above copyright notice and this permission notice shall be
|
||||
|
// included in all copies or substantial portions of the Software.
|
||||
|
//
|
||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace MathNet.Numerics.IntegralTransforms |
||||
|
{ |
||||
|
using System; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fourier Transform Convention
|
||||
|
/// </summary>
|
||||
|
[Flags] |
||||
|
public enum FourierOptions |
||||
|
{ |
||||
|
// FLAGS:
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Inverse integrand exponent (forward: positive sign; inverse: negative sign).
|
||||
|
/// </summary>
|
||||
|
InverseExponent = 0x01, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Only scale by 1/N in the inverse direction; No scaling in forward direction.
|
||||
|
/// </summary>
|
||||
|
AsymmetricScaling = 0x02, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Don't scale at all (neither on forward nor on inverse transformation).
|
||||
|
/// </summary>
|
||||
|
NoScaling = 0x04, |
||||
|
|
||||
|
// USABILITY POINTERS:
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Universal; Symmetric scaling and common exponent (used in Maple).
|
||||
|
/// </summary>
|
||||
|
Default = 0, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Only scale by 1/N in the inverse direction; No scaling in forward direction (used in Matlab). [= AsymmetricScaling]
|
||||
|
/// </summary>
|
||||
|
Matlab = AsymmetricScaling, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Inverse integrand exponent; No scaling at all (used in all Numerical Recipes based implementations). [= InverseExponent | NoScaling]
|
||||
|
/// </summary>
|
||||
|
NumericalRecipes = InverseExponent | NoScaling |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue