Browse Source

Span<T> & Memory<T> on Fourier

pull/880/head
macaba 5 years ago
parent
commit
33039634e3
  1. 8
      src/Numerics.Tests/IntegralTransformsTests/FourierTest.cs
  2. 16
      src/Numerics.Tests/IntegralTransformsTests/InverseTransformTest.cs
  3. 40
      src/Numerics.Tests/IntegralTransformsTests/MatchingReferenceTransformTest.cs
  4. 8
      src/Numerics.Tests/IntegralTransformsTests/ParsevalTheoremTest.cs
  5. 44
      src/Numerics.Tests/IntegralTransformsTests/ReferenceDiscreteFourierTransform.cs
  6. 7
      src/Numerics.Tests/Providers/FourierTransform/FourierTransformProviderTests.cs
  7. 2
      src/Numerics.Tests/Providers/LinearAlgebra/Double/LinearAlgebraProviderTests.cs
  8. 12
      src/Numerics/Generate.cs
  9. 76
      src/Numerics/IntegralTransforms/Fourier.cs
  10. 8
      src/Numerics/LinearAlgebra/Builder.cs
  11. 2
      src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs
  12. 2
      src/Numerics/LinearAlgebra/Complex/DenseVector.cs
  13. 2
      src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs
  14. 2
      src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs
  15. 2
      src/Numerics/LinearAlgebra/Complex32/DenseVector.cs
  16. 2
      src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs
  17. 1
      src/Numerics/Numerics.csproj
  18. 25
      src/Numerics/Providers/FourierTransform/IFourierTransformProvider.cs
  19. 48
      src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.Bluestein.cs
  20. 38
      src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.Radix2.cs
  21. 8
      src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.Scaling.cs
  22. 48
      src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.cs

8
src/Numerics.Tests/IntegralTransformsTests/FourierTest.cs

@ -43,7 +43,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[Test]
public void ReferenceDftTransformsRealSineCorrectly32()
{
var samples = Generate.PeriodicMap(16, w => new Complex32((float)Math.Sin(w), 0), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => new Complex32((float)Math.Sin(w), 0), 16, 1.0, Constants.Pi2).ToArray();
// real-odd transforms to imaginary odd
ReferenceDiscreteFourierTransform.Forward(samples, FourierOptions.Matlab);
@ -75,7 +75,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[Test]
public void ReferenceDftTransformsRealSineCorrectly64()
{
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2).ToArray();
// real-odd transforms to imaginary odd
ReferenceDiscreteFourierTransform.Forward(samples, FourierOptions.Matlab);
@ -107,7 +107,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[Test]
public void FourierDefaultTransformsRealSineCorrectly32()
{
var samples = Generate.PeriodicMap(16, w => new Complex32((float)Math.Sin(w), 0), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => new Complex32((float)Math.Sin(w), 0), 16, 1.0, Constants.Pi2).ToArray();
// real-odd transforms to imaginary odd
Fourier.Forward(samples, FourierOptions.Matlab);
@ -139,7 +139,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[Test]
public void FourierDefaultTransformsRealSineCorrectly64()
{
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2).ToArray();
// real-odd transforms to imaginary odd
Fourier.Forward(samples, FourierOptions.Matlab);

16
src/Numerics.Tests/IntegralTransformsTests/InverseTransformTest.cs

@ -58,13 +58,13 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
{
var samples = Generate.RandomComplex32(0x80, GetUniform(1));
var work = new Complex32[samples.Length];
samples.CopyTo(work, 0);
samples.CopyTo(work);
ReferenceDiscreteFourierTransform.Forward(work, options);
Assert.IsFalse(work.ListAlmostEqual(samples, 6));
Assert.IsFalse(work.ListAlmostEqual(samples.ToArray(), 6));
ReferenceDiscreteFourierTransform.Inverse(work, options);
AssertHelpers.AlmostEqual(samples, work, 11);
AssertHelpers.AlmostEqual(samples.ToArray(), work, 11);
}
/// <summary>
@ -75,7 +75,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(FourierOptions.Matlab)]
public void ReferenceDftIsReversible64(FourierOptions options)
{
var samples = Generate.RandomComplex(0x80, GetUniform(1));
var samples = Generate.RandomComplex(0x80, GetUniform(1)).ToArray();
var work = new Complex[samples.Length];
samples.CopyTo(work, 0);
@ -94,7 +94,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(FourierOptions.Matlab)]
public void FourierRadix2IsReversible32(FourierOptions options)
{
var samples = Generate.RandomComplex32(0x8000, GetUniform(1));
var samples = Generate.RandomComplex32(0x8000, GetUniform(1)).ToArray();
var work = new Complex32[samples.Length];
samples.CopyTo(work, 0);
@ -113,7 +113,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(FourierOptions.Matlab)]
public void FourierRadix2IsReversible64(FourierOptions options)
{
var samples = Generate.RandomComplex(0x8000, GetUniform(1));
var samples = Generate.RandomComplex(0x8000, GetUniform(1)).ToArray();
var work = new Complex[samples.Length];
samples.CopyTo(work, 0);
@ -132,7 +132,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(FourierOptions.Matlab)]
public void FourierBluesteinIsReversible32(FourierOptions options)
{
var samples = Generate.RandomComplex32(0x7FFF, GetUniform(1));
var samples = Generate.RandomComplex32(0x7FFF, GetUniform(1)).ToArray();
var work = new Complex32[samples.Length];
samples.CopyTo(work, 0);
@ -151,7 +151,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(FourierOptions.Matlab)]
public void FourierBluesteinIsReversible64(FourierOptions options)
{
var samples = Generate.RandomComplex(0x7FFF, GetUniform(1));
var samples = Generate.RandomComplex(0x7FFF, GetUniform(1)).ToArray();
var work = new Complex[samples.Length];
samples.CopyTo(work, 0);

40
src/Numerics.Tests/IntegralTransformsTests/MatchingReferenceTransformTest.cs

@ -51,72 +51,72 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
}
static void Verify(
Complex32[] samples,
Memory<Complex32> samples,
int maximumErrorDecimalPlaces,
FourierOptions options,
Action<Complex32[], FourierOptions> expected,
Action<Complex32[], FourierOptions> actual)
Action<Memory<Complex32>, FourierOptions> expected,
Action<Memory<Complex32>, FourierOptions> actual)
{
var spectrumExpected = new Complex32[samples.Length];
samples.CopyTo(spectrumExpected, 0);
samples.CopyTo(spectrumExpected);
expected(spectrumExpected, options);
var spectrumActual = new Complex32[samples.Length];
samples.CopyTo(spectrumActual, 0);
samples.CopyTo(spectrumActual);
actual(spectrumActual, options);
AssertHelpers.AlmostEqual(spectrumExpected, spectrumActual, maximumErrorDecimalPlaces);
}
static void Verify(
Complex[] samples,
Memory<Complex> samples,
int maximumErrorDecimalPlaces,
FourierOptions options,
Action<Complex[], FourierOptions> expected,
Action<Complex[], FourierOptions> actual)
Action<Memory<Complex>, FourierOptions> expected,
Action<Memory<Complex>, FourierOptions> actual)
{
var spectrumExpected = new Complex[samples.Length];
samples.CopyTo(spectrumExpected, 0);
samples.CopyTo(spectrumExpected);
expected(spectrumExpected, options);
var spectrumActual = new Complex[samples.Length];
samples.CopyTo(spectrumActual, 0);
samples.CopyTo(spectrumActual);
actual(spectrumActual, options);
AssertHelpers.AlmostEqual(spectrumExpected, spectrumActual, maximumErrorDecimalPlaces);
}
static void Verify(
Complex32[] samples,
Memory<Complex32> samples,
int maximumErrorDecimalPlaces,
FourierTransformScaling options,
Action<Complex32[], FourierTransformScaling> expected,
Action<Complex32[], FourierTransformScaling> actual)
Action<Memory<Complex32>, FourierTransformScaling> expected,
Action<Memory<Complex32>, FourierTransformScaling> actual)
{
var spectrumExpected = new Complex32[samples.Length];
samples.CopyTo(spectrumExpected, 0);
samples.CopyTo(spectrumExpected);
expected(spectrumExpected, options);
var spectrumActual = new Complex32[samples.Length];
samples.CopyTo(spectrumActual, 0);
samples.CopyTo(spectrumActual);
actual(spectrumActual, options);
AssertHelpers.AlmostEqual(spectrumExpected, spectrumActual, maximumErrorDecimalPlaces);
}
static void Verify(
Complex[] samples,
Memory<Complex> samples,
int maximumErrorDecimalPlaces,
FourierTransformScaling options,
Action<Complex[], FourierTransformScaling> expected,
Action<Complex[], FourierTransformScaling> actual)
Action<Memory<Complex>, FourierTransformScaling> expected,
Action<Memory<Complex>, FourierTransformScaling> actual)
{
var spectrumExpected = new Complex[samples.Length];
samples.CopyTo(spectrumExpected, 0);
samples.CopyTo(spectrumExpected);
expected(spectrumExpected, options);
var spectrumActual = new Complex[samples.Length];
samples.CopyTo(spectrumActual, 0);
samples.CopyTo(spectrumActual);
actual(spectrumActual, options);
AssertHelpers.AlmostEqual(spectrumExpected, spectrumActual, maximumErrorDecimalPlaces);

8
src/Numerics.Tests/IntegralTransformsTests/ParsevalTheoremTest.cs

@ -58,7 +58,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(0x7FF)]
public void ReferenceDftSatisfiesParsevalsTheorem32(int count)
{
var samples = Generate.RandomComplex32(count, GetUniform(1));
var samples = Generate.RandomComplex32(count, GetUniform(1)).ToArray();
var timeSpaceEnergy = (from s in samples select s.MagnitudeSquared()).Mean();
var spectrum = new Complex32[samples.Length];
@ -77,7 +77,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(0x7FF)]
public void ReferenceDftSatisfiesParsevalsTheorem64(int count)
{
var samples = Generate.RandomComplex(count, GetUniform(1));
var samples = Generate.RandomComplex(count, GetUniform(1)).ToArray();
var timeSpaceEnergy = (from s in samples select s.MagnitudeSquared()).Mean();
var spectrum = new Complex[samples.Length];
@ -96,7 +96,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(0x7FF)]
public void FourierDefaultTransformSatisfiesParsevalsTheorem32(int count)
{
var samples = Generate.RandomComplex32(count, GetUniform(1));
var samples = Generate.RandomComplex32(count, GetUniform(1)).ToArray();
var timeSpaceEnergy = (from s in samples select s.MagnitudeSquared()).Mean();
var spectrum = new Complex32[samples.Length];
@ -115,7 +115,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
[TestCase(0x7FF)]
public void FourierDefaultTransformSatisfiesParsevalsTheorem64(int count)
{
var samples = Generate.RandomComplex(count, GetUniform(1));
var samples = Generate.RandomComplex(count, GetUniform(1)).ToArray();
var timeSpaceEnergy = (from s in samples select s.MagnitudeSquared()).Mean();
var spectrum = new Complex[samples.Length];

44
src/Numerics.Tests/IntegralTransformsTests/ReferenceDiscreteFourierTransform.cs

@ -10,7 +10,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// <summary>
/// Naive forward DFT, useful e.g. to verify faster algorithms.
/// </summary>
public static void Forward(Complex32[] samples, FourierOptions options = FourierOptions.Default)
public static void Forward(Memory<Complex32> samples, FourierOptions options = FourierOptions.Default)
{
Naive(samples, SignByOptions(options));
ForwardScaleByOptions(options, samples);
@ -19,7 +19,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// <summary>
/// Naive forward DFT, useful e.g. to verify faster algorithms.
/// </summary>
public static void Forward(Complex[] samples, FourierOptions options = FourierOptions.Default)
public static void Forward(Memory<Complex> samples, FourierOptions options = FourierOptions.Default)
{
Naive(samples, SignByOptions(options));
ForwardScaleByOptions(options, samples);
@ -28,7 +28,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// <summary>
/// Naive inverse DFT, useful e.g. to verify faster algorithms.
/// </summary>
public static void Inverse(Complex32[] spectrum, FourierOptions options = FourierOptions.Default)
public static void Inverse(Memory<Complex32> spectrum, FourierOptions options = FourierOptions.Default)
{
Naive(spectrum, -SignByOptions(options));
InverseScaleByOptions(options, spectrum);
@ -37,7 +37,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// <summary>
/// Naive inverse DFT, useful e.g. to verify faster algorithms.
/// </summary>
public static void Inverse(Complex[] spectrum, FourierOptions options = FourierOptions.Default)
public static void Inverse(Memory<Complex> spectrum, FourierOptions options = FourierOptions.Default)
{
Naive(spectrum, -SignByOptions(options));
InverseScaleByOptions(options, spectrum);
@ -49,10 +49,10 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// <param name="samples">Time-space sample vector.</param>
/// <param name="exponentSign">Fourier series exponent sign.</param>
/// <returns>Corresponding frequency-space vector.</returns>
static void Naive(Complex32[] samples, int exponentSign)
static void Naive(Memory<Complex32> samples, int exponentSign)
{
var w0 = exponentSign * Constants.Pi2 / samples.Length;
var spectrum = new Complex32[samples.Length];
Memory<Complex32> spectrum = new Complex32[samples.Length];
CommonParallel.For(0, samples.Length, (u, v) =>
{
@ -63,14 +63,14 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
for (var n = 0; n < samples.Length; n++)
{
var w = n * wk;
sum += samples[n] * new Complex32((float)Math.Cos(w), (float)Math.Sin(w));
sum += samples.Span[n] * new Complex32((float)Math.Cos(w), (float)Math.Sin(w));
}
spectrum[i] = sum;
spectrum.Span[i] = sum;
}
});
spectrum.Copy(samples);
spectrum.CopyTo(samples);
}
/// <summary>
@ -79,10 +79,10 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// <param name="samples">Time-space sample vector.</param>
/// <param name="exponentSign">Fourier series exponent sign.</param>
/// <returns>Corresponding frequency-space vector.</returns>
static void Naive(Complex[] samples, int exponentSign)
static void Naive(Memory<Complex> samples, int exponentSign)
{
var w0 = exponentSign * Constants.Pi2 / samples.Length;
var spectrum = new Complex[samples.Length];
Memory<Complex> spectrum = new Complex[samples.Length];
CommonParallel.For(0, samples.Length, (u, v) =>
{
@ -93,14 +93,14 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
for (var n = 0; n < samples.Length; n++)
{
var w = n * wk;
sum += samples[n] * new Complex(Math.Cos(w), Math.Sin(w));
sum += samples.Span[n] * new Complex(Math.Cos(w), Math.Sin(w));
}
spectrum[i] = sum;
spectrum.Span[i] = sum;
}
});
spectrum.Copy(samples);
spectrum.CopyTo(samples);
}
/// <summary>
@ -119,7 +119,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// </summary>
/// <param name="options">Fourier Transform Convention Options.</param>
/// <param name="samples">Sample Vector.</param>
static void ForwardScaleByOptions(FourierOptions options, Complex32[] samples)
static void ForwardScaleByOptions(FourierOptions options, Memory<Complex32> samples)
{
if ((options & FourierOptions.NoScaling) == FourierOptions.NoScaling ||
(options & FourierOptions.AsymmetricScaling) == FourierOptions.AsymmetricScaling)
@ -130,7 +130,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
var scalingFactor = (float)Math.Sqrt(1.0 / samples.Length);
for (int i = 0; i < samples.Length; i++)
{
samples[i] *= scalingFactor;
samples.Span[i] *= scalingFactor;
}
}
@ -139,7 +139,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// </summary>
/// <param name="options">Fourier Transform Convention Options.</param>
/// <param name="samples">Sample Vector.</param>
static void ForwardScaleByOptions(FourierOptions options, Complex[] samples)
static void ForwardScaleByOptions(FourierOptions options, Memory<Complex> samples)
{
if ((options & FourierOptions.NoScaling) == FourierOptions.NoScaling ||
(options & FourierOptions.AsymmetricScaling) == FourierOptions.AsymmetricScaling)
@ -150,7 +150,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
var scalingFactor = Math.Sqrt(1.0 / samples.Length);
for (int i = 0; i < samples.Length; i++)
{
samples[i] *= scalingFactor;
samples.Span[i] *= scalingFactor;
}
}
@ -159,7 +159,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// </summary>
/// <param name="options">Fourier Transform Convention Options.</param>
/// <param name="samples">Sample Vector.</param>
static void InverseScaleByOptions(FourierOptions options, Complex32[] samples)
static void InverseScaleByOptions(FourierOptions options, Memory<Complex32> samples)
{
if ((options & FourierOptions.NoScaling) == FourierOptions.NoScaling)
{
@ -174,7 +174,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
for (int i = 0; i < samples.Length; i++)
{
samples[i] *= scalingFactor;
samples.Span[i] *= scalingFactor;
}
}
@ -183,7 +183,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
/// </summary>
/// <param name="options">Fourier Transform Convention Options.</param>
/// <param name="samples">Sample Vector.</param>
static void InverseScaleByOptions(FourierOptions options, Complex[] samples)
static void InverseScaleByOptions(FourierOptions options, Memory<Complex> samples)
{
if ((options & FourierOptions.NoScaling) == FourierOptions.NoScaling)
{
@ -198,7 +198,7 @@ namespace MathNet.Numerics.UnitTests.IntegralTransformsTests
for (int i = 0; i < samples.Length; i++)
{
samples[i] *= scalingFactor;
samples.Span[i] *= scalingFactor;
}
}
}

7
src/Numerics.Tests/Providers/FourierTransform/FourierTransformProviderTests.cs

@ -45,7 +45,7 @@ namespace MathNet.Numerics.UnitTests.Providers.FourierTransform
[Test]
public void ProviderSurvivesFreeResources()
{
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2).ToArray();
var spectrum1 = new Complex[samples.Length];
samples.Copy(spectrum1);
@ -66,7 +66,7 @@ namespace MathNet.Numerics.UnitTests.Providers.FourierTransform
[Test]
public void ForwardInplaceRealSine()
{
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => new Complex(Math.Sin(w), 0), 16, 1.0, Constants.Pi2).ToArray();
var spectrum = new Complex[samples.Length];
// real-odd transforms to imaginary odd
@ -101,7 +101,7 @@ namespace MathNet.Numerics.UnitTests.Providers.FourierTransform
[TestCase(0x7FF)]
public void ForwardInplaceParsevalTheorem(int count)
{
var samples = Generate.RandomComplex(count, GetUniform(1));
var samples = Generate.RandomComplex(count, GetUniform(1)).ToArray();
var timeSpaceEnergy = Generate.Map(samples, s => s.MagnitudeSquared()).Mean();
FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling);
@ -114,6 +114,5 @@ namespace MathNet.Numerics.UnitTests.Providers.FourierTransform
{
return new ContinuousUniform(-1, 1, new System.Random(seed));
}
}
}

2
src/Numerics.Tests/Providers/LinearAlgebra/Double/LinearAlgebraProviderTests.cs

@ -75,7 +75,7 @@ namespace MathNet.Numerics.UnitTests.Providers.LinearAlgebra.Double
[Test]
public void ProviderSurvivesFreeResources()
{
var samples = Generate.PeriodicMap(16, w => Math.Sin(w), 16, 1.0, Constants.Pi2);
var samples = Generate.PeriodicMap(16, w => Math.Sin(w), 16, 1.0, Constants.Pi2).ToArray();
var result1 = new double[samples.Length];
LinearAlgebraControl.Provider.ScaleArray(2.5, samples, result1);

12
src/Numerics/Generate.cs

@ -367,7 +367,7 @@ namespace MathNet.Numerics
/// <param name="amplitude">The length 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.</param>
/// <param name="phase">Optional phase offset.</param>
/// <param name="delay">Optional delay, relative to the phase.</param>
public static T[] PeriodicMap<T>(int length, Func<double, T> map, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0)
public static Memory<T> PeriodicMap<T>(int length, Func<double, T> map, double samplingRate, double frequency, double amplitude = 1.0, double phase = 0.0, int delay = 0)
{
if (length < 0)
{
@ -511,7 +511,7 @@ namespace MathNet.Numerics
/// <param name="lowValue">Sample value to be emitted during the low phase.</param>
/// <param name="highValue">Sample value to be emitted during the high phase.</param>
/// <param name="delay">Optional delay.</param>
public static double[] Square(int length, int highDuration, int lowDuration, double lowValue, double highValue, int delay = 0)
public static Memory<double> Square(int length, int highDuration, int lowDuration, double lowValue, double highValue, int delay = 0)
{
var duration = highDuration + lowDuration;
return PeriodicMap(length, x => x < highDuration ? highValue : lowValue, 1.0, 1.0/duration, duration, 0.0, delay);
@ -540,7 +540,7 @@ namespace MathNet.Numerics
/// <param name="lowValue">Lowest sample value.</param>
/// <param name="highValue">Highest sample value.</param>
/// <param name="delay">Optional delay.</param>
public static double[] Triangle(int length, int raiseDuration, int fallDuration, double lowValue, double highValue, int delay = 0)
public static Memory<double> Triangle(int length, int raiseDuration, int fallDuration, double lowValue, double highValue, int delay = 0)
{
var duration = raiseDuration + fallDuration;
var height = highValue - lowValue;
@ -574,7 +574,7 @@ namespace MathNet.Numerics
/// <param name="lowValue">Lowest sample value.</param>
/// <param name="highValue">Highest sample value.</param>
/// <param name="delay">Optional delay.</param>
public static double[] Sawtooth(int length, int period, double lowValue, double highValue, int delay = 0)
public static Memory<double> Sawtooth(int length, int period, double lowValue, double highValue, int delay = 0)
{
var height = highValue - lowValue;
return PeriodicMap(length, x => x + lowValue, 1.0, 1.0/period, height*period/(period-1), 0.0, delay);
@ -1041,7 +1041,7 @@ namespace MathNet.Numerics
/// <summary>
/// Create random samples.
/// </summary>
public static Complex[] RandomComplex(int length, IContinuousDistribution distribution)
public static Memory<Complex> RandomComplex(int length, IContinuousDistribution distribution)
{
return RandomMap2(length, distribution, (r, i) => new Complex(r, i));
}
@ -1057,7 +1057,7 @@ namespace MathNet.Numerics
/// <summary>
/// Create random samples.
/// </summary>
public static Complex32[] RandomComplex32(int length, IContinuousDistribution distribution)
public static Memory<Complex32> RandomComplex32(int length, IContinuousDistribution distribution)
{
return RandomMap2(length, distribution, (r, i) => new Complex32((float)r, (float)i));
}

76
src/Numerics/IntegralTransforms/Fourier.cs

@ -44,7 +44,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors.
/// </summary>
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
public static void Forward(Complex32[] samples)
public static void Forward(Memory<Complex32> samples)
{
FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling);
}
@ -53,7 +53,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// Applies the forward Fast Fourier Transform (FFT) to arbitrary-length sample vectors.
/// </summary>
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
public static void Forward(Complex[] samples)
public static void Forward(Memory<Complex> samples)
{
FourierTransformControl.Provider.Forward(samples, FourierTransformScaling.SymmetricScaling);
}
@ -63,7 +63,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// </summary>
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Forward(Complex32[] samples, FourierOptions options)
public static void Forward(Memory<Complex32> samples, FourierOptions options)
{
switch (options)
{
@ -89,7 +89,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// </summary>
/// <param name="samples">Sample vector, where the FFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Forward(Complex[] samples, FourierOptions options)
public static void Forward(Memory<Complex> samples, FourierOptions options)
{
switch (options)
{
@ -116,7 +116,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="real">Real part of the sample vector, where the FFT is evaluated in place.</param>
/// <param name="imaginary">Imaginary part of the sample vector, where the FFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Forward(float[] real, float[] imaginary, FourierOptions options = FourierOptions.Default)
public static void Forward(Span<float> real, Span<float> imaginary, FourierOptions options = FourierOptions.Default)
{
if (real.Length != imaginary.Length)
{
@ -147,7 +147,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="real">Real part of the sample vector, where the FFT is evaluated in place.</param>
/// <param name="imaginary">Imaginary part of the sample vector, where the FFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Forward(double[] real, double[] imaginary, FourierOptions options = FourierOptions.Default)
public static void Forward(Span<double> real, Span<double> imaginary, FourierOptions options = FourierOptions.Default)
{
if (real.Length != imaginary.Length)
{
@ -181,7 +181,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="data">Data array of length N+2 (if N is even) or N+1 (if N is odd).</param>
/// <param name="n">The number of samples.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void ForwardReal(float[] data, int n, FourierOptions options = FourierOptions.Default)
public static void ForwardReal(Span<float> data, int n, FourierOptions options = FourierOptions.Default)
{
int length = n.IsEven() ? n + 2 : n + 1;
if (data.Length < length)
@ -215,7 +215,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="data">Data array of length N+2 (if N is even) or N+1 (if N is odd).</param>
/// <param name="n">The number of samples.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void ForwardReal(double[] data, int n, FourierOptions options = FourierOptions.Default)
public static void ForwardReal(Span<double> data, int n, FourierOptions options = FourierOptions.Default)
{
int length = n.IsEven() ? n + 2 : n + 1;
if (data.Length < length)
@ -249,7 +249,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row.
/// </param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void ForwardMultiDim(Complex32[] samples, int[] dimensions, FourierOptions options = FourierOptions.Default)
public static void ForwardMultiDim(Span<Complex32> samples, Span<int> dimensions, FourierOptions options = FourierOptions.Default)
{
switch (options)
{
@ -279,7 +279,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row.
/// </param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void ForwardMultiDim(Complex[] samples, int[] dimensions, FourierOptions options = FourierOptions.Default)
public static void ForwardMultiDim(Span<Complex> samples, Span<int> dimensions, FourierOptions options = FourierOptions.Default)
{
switch (options)
{
@ -308,7 +308,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="columns">The number of columns.</param>
/// <remarks>Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments.</remarks>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Forward2D(Complex32[] samplesRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
public static void Forward2D(Span<Complex32> samplesRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
{
ForwardMultiDim(samplesRowWise, new[] { rows, columns }, options);
}
@ -321,7 +321,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="columns">The number of columns.</param>
/// <remarks>Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments.</remarks>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Forward2D(Complex[] samplesRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
public static void Forward2D(Span<Complex> samplesRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
{
ForwardMultiDim(samplesRowWise, new[] { rows, columns }, options);
}
@ -386,7 +386,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors.
/// </summary>
/// <param name="spectrum">Spectrum data, where the iFFT is evaluated in place.</param>
public static void Inverse(Complex32[] spectrum)
public static void Inverse(Memory<Complex32> spectrum)
{
FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.SymmetricScaling);
}
@ -395,7 +395,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// Applies the inverse Fast Fourier Transform (iFFT) to arbitrary-length sample vectors.
/// </summary>
/// <param name="spectrum">Spectrum data, where the iFFT is evaluated in place.</param>
public static void Inverse(Complex[] spectrum)
public static void Inverse(Memory<Complex> spectrum)
{
FourierTransformControl.Provider.Backward(spectrum, FourierTransformScaling.SymmetricScaling);
}
@ -405,7 +405,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// </summary>
/// <param name="spectrum">Spectrum data, where the iFFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Inverse(Complex32[] spectrum, FourierOptions options)
public static void Inverse(Memory<Complex32> spectrum, FourierOptions options)
{
switch (options)
{
@ -435,7 +435,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// </summary>
/// <param name="spectrum">Spectrum data, where the iFFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Inverse(Complex[] spectrum, FourierOptions options)
public static void Inverse(Memory<Complex> spectrum, FourierOptions options)
{
switch (options)
{
@ -466,7 +466,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="real">Real part of the sample vector, where the iFFT is evaluated in place.</param>
/// <param name="imaginary">Imaginary part of the sample vector, where the iFFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Inverse(float[] real, float[] imaginary, FourierOptions options = FourierOptions.Default)
public static void Inverse(Memory<float> real, Memory<float> imaginary, FourierOptions options = FourierOptions.Default)
{
if (real.Length != imaginary.Length)
{
@ -479,15 +479,15 @@ namespace MathNet.Numerics.IntegralTransforms
Complex32[] data = new Complex32[real.Length];
for (int i = 0; i < data.Length; i++)
{
data[i] = new Complex32(real[i], imaginary[i]);
data[i] = new Complex32(real.Span[i], imaginary.Span[i]);
}
Inverse(data, options);
for (int i = 0; i < data.Length; i++)
{
real[i] = data[i].Real;
imaginary[i] = data[i].Imaginary;
real.Span[i] = data[i].Real;
imaginary.Span[i] = data[i].Imaginary;
}
}
@ -497,7 +497,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="real">Real part of the sample vector, where the iFFT is evaluated in place.</param>
/// <param name="imaginary">Imaginary part of the sample vector, where the iFFT is evaluated in place.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Inverse(double[] real, double[] imaginary, FourierOptions options = FourierOptions.Default)
public static void Inverse(Memory<double> real, Memory<double> imaginary, FourierOptions options = FourierOptions.Default)
{
if (real.Length != imaginary.Length)
{
@ -510,15 +510,15 @@ namespace MathNet.Numerics.IntegralTransforms
Complex[] data = new Complex[real.Length];
for (int i = 0; i < data.Length; i++)
{
data[i] = new Complex(real[i], imaginary[i]);
data[i] = new Complex(real.Span[i], imaginary.Span[i]);
}
Inverse(data, options);
for (int i = 0; i < data.Length; i++)
{
real[i] = data[i].Real;
imaginary[i] = data[i].Imaginary;
real.Span[i] = data[i].Real;
imaginary.Span[i] = data[i].Imaginary;
}
}
@ -531,7 +531,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="data">Data array of length N+2 (if N is even) or N+1 (if N is odd).</param>
/// <param name="n">The number of samples.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void InverseReal(float[] data, int n, FourierOptions options = FourierOptions.Default)
public static void InverseReal(Memory<float> data, int n, FourierOptions options = FourierOptions.Default)
{
int length = n.IsEven() ? n + 2 : n + 1;
if (data.Length < length)
@ -547,13 +547,13 @@ namespace MathNet.Numerics.IntegralTransforms
switch (options)
{
case FourierOptions.NoScaling:
FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.NoScaling);
FourierTransformControl.Provider.BackwardReal(data.Span, n, FourierTransformScaling.NoScaling);
break;
case FourierOptions.AsymmetricScaling:
FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.BackwardScaling);
FourierTransformControl.Provider.BackwardReal(data.Span, n, FourierTransformScaling.BackwardScaling);
break;
default:
FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.SymmetricScaling);
FourierTransformControl.Provider.BackwardReal(data.Span, n, FourierTransformScaling.SymmetricScaling);
break;
}
}
@ -567,7 +567,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="data">Data array of length N+2 (if N is even) or N+1 (if N is odd).</param>
/// <param name="n">The number of samples.</param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void InverseReal(double[] data, int n, FourierOptions options = FourierOptions.Default)
public static void InverseReal(Memory<double> data, int n, FourierOptions options = FourierOptions.Default)
{
int length = n.IsEven() ? n + 2 : n + 1;
if (data.Length < length)
@ -583,13 +583,13 @@ namespace MathNet.Numerics.IntegralTransforms
switch (options)
{
case FourierOptions.NoScaling:
FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.NoScaling);
FourierTransformControl.Provider.BackwardReal(data.Span, n, FourierTransformScaling.NoScaling);
break;
case FourierOptions.AsymmetricScaling:
FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.BackwardScaling);
FourierTransformControl.Provider.BackwardReal(data.Span, n, FourierTransformScaling.BackwardScaling);
break;
default:
FourierTransformControl.Provider.BackwardReal(data, n, FourierTransformScaling.SymmetricScaling);
FourierTransformControl.Provider.BackwardReal(data.Span, n, FourierTransformScaling.SymmetricScaling);
break;
}
}
@ -603,7 +603,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row.
/// </param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void InverseMultiDim(Complex32[] spectrum, int[] dimensions, FourierOptions options = FourierOptions.Default)
public static void InverseMultiDim(Span<Complex32> spectrum, Span<int> dimensions, FourierOptions options = FourierOptions.Default)
{
switch (options)
{
@ -637,7 +637,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// For example, with two dimensions "rows" and "columns" the samples are assumed to be organized row by row.
/// </param>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void InverseMultiDim(Complex[] spectrum, int[] dimensions, FourierOptions options = FourierOptions.Default)
public static void InverseMultiDim(Span<Complex> spectrum, Span<int> dimensions, FourierOptions options = FourierOptions.Default)
{
switch (options)
{
@ -670,7 +670,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="columns">The number of columns.</param>
/// <remarks>Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments.</remarks>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Inverse2D(Complex32[] spectrumRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
public static void Inverse2D(Span<Complex32> spectrumRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
{
InverseMultiDim(spectrumRowWise, new[] { rows, columns }, options);
}
@ -683,7 +683,7 @@ namespace MathNet.Numerics.IntegralTransforms
/// <param name="columns">The number of columns.</param>
/// <remarks>Data available organized column by column instead of row by row can be processed directly by swapping the rows and columns arguments.</remarks>
/// <param name="options">Fourier Transform Convention Options.</param>
public static void Inverse2D(Complex[] spectrumRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
public static void Inverse2D(Span<Complex> spectrumRowWise, int rows, int columns, FourierOptions options = FourierOptions.Default)
{
InverseMultiDim(spectrumRowWise, new[] { rows, columns }, options);
}
@ -753,9 +753,9 @@ namespace MathNet.Numerics.IntegralTransforms
/// </summary>
/// <param name="length">Number of samples.</param>
/// <param name="sampleRate">The sampling rate of the time-space data.</param>
public static double[] FrequencyScale(int length, double sampleRate)
public static Span<double> FrequencyScale(int length, double sampleRate)
{
double[] scale = new double[length];
Span<double> scale = new double[length];
double f = 0, step = sampleRate / length;
int secondHalf = (length >> 1) + 1;
for (int i = 0; i < secondHalf; i++)

8
src/Numerics/LinearAlgebra/Builder.cs

@ -198,7 +198,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
public override Matrix<Complex> Random(int rows, int columns, IContinuousDistribution distribution)
{
return Dense(rows, columns, Generate.RandomComplex(rows*columns, distribution));
return Dense(rows, columns, Generate.RandomComplex(rows*columns, distribution).ToArray());
}
public override IIterationStopCriterion<Complex>[] IterativeSolverStopCriteria(int maxIterations = 1000)
@ -236,7 +236,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
public override Vector<Complex> Random(int length, IContinuousDistribution distribution)
{
return Dense(Generate.RandomComplex(length, distribution));
return Dense(Generate.RandomComplex(length, distribution).ToArray());
}
}
}
@ -266,7 +266,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
public override Matrix<Numerics.Complex32> Random(int rows, int columns, IContinuousDistribution distribution)
{
return Dense(rows, columns, Generate.RandomComplex32(rows*columns, distribution));
return Dense(rows, columns, Generate.RandomComplex32(rows*columns, distribution).ToArray());
}
public override IIterationStopCriterion<Numerics.Complex32>[] IterativeSolverStopCriteria(int maxIterations = 1000)
@ -304,7 +304,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
public override Vector<Numerics.Complex32> Random(int length, IContinuousDistribution distribution)
{
return Dense(Generate.RandomComplex32(length, distribution));
return Dense(Generate.RandomComplex32(length, distribution).ToArray());
}
}
}

2
src/Numerics/LinearAlgebra/Complex/DenseMatrix.cs

@ -396,7 +396,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
/// </summary>
public static DenseMatrix CreateRandom(int rows, int columns, IContinuousDistribution distribution)
{
return new DenseMatrix(new DenseColumnMajorMatrixStorage<Complex>(rows, columns, Generate.RandomComplex(rows*columns, distribution)));
return new DenseMatrix(new DenseColumnMajorMatrixStorage<Complex>(rows, columns, Generate.RandomComplex(rows*columns, distribution).ToArray()));
}
/// <summary>

2
src/Numerics/LinearAlgebra/Complex/DenseVector.cs

@ -164,7 +164,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
/// </summary>
public static DenseVector CreateRandom(int length, IContinuousDistribution distribution)
{
var samples = Generate.RandomComplex(length, distribution);
var samples = Generate.RandomComplex(length, distribution).ToArray();
return new DenseVector(new DenseVectorStorage<Complex>(length, samples));
}

2
src/Numerics/LinearAlgebra/Complex/DiagonalMatrix.cs

@ -190,7 +190,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex
/// </summary>
public static DiagonalMatrix CreateRandom(int rows, int columns, IContinuousDistribution distribution)
{
return new DiagonalMatrix(new DiagonalMatrixStorage<Complex>(rows, columns, Generate.RandomComplex(Math.Min(rows, columns), distribution)));
return new DiagonalMatrix(new DiagonalMatrixStorage<Complex>(rows, columns, Generate.RandomComplex(Math.Min(rows, columns), distribution).ToArray()));
}
/// <summary>

2
src/Numerics/LinearAlgebra/Complex32/DenseMatrix.cs

@ -396,7 +396,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
/// </summary>
public static DenseMatrix CreateRandom(int rows, int columns, IContinuousDistribution distribution)
{
return new DenseMatrix(new DenseColumnMajorMatrixStorage<Complex32>(rows, columns, Generate.RandomComplex32(rows*columns, distribution)));
return new DenseMatrix(new DenseColumnMajorMatrixStorage<Complex32>(rows, columns, Generate.RandomComplex32(rows*columns, distribution).ToArray()));
}
/// <summary>

2
src/Numerics/LinearAlgebra/Complex32/DenseVector.cs

@ -164,7 +164,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
/// </summary>
public static DenseVector CreateRandom(int length, IContinuousDistribution distribution)
{
var samples = Generate.RandomComplex32(length, distribution);
var samples = Generate.RandomComplex32(length, distribution).ToArray();
return new DenseVector(new DenseVectorStorage<Complex32>(length, samples));
}

2
src/Numerics/LinearAlgebra/Complex32/DiagonalMatrix.cs

@ -190,7 +190,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32
/// </summary>
public static DiagonalMatrix CreateRandom(int rows, int columns, IContinuousDistribution distribution)
{
return new DiagonalMatrix(new DiagonalMatrixStorage<Complex32>(rows, columns, Generate.RandomComplex32(Math.Min(rows, columns), distribution)));
return new DiagonalMatrix(new DiagonalMatrixStorage<Complex32>(rows, columns, Generate.RandomComplex32(Math.Min(rows, columns), distribution).ToArray()));
}
/// <summary>

1
src/Numerics/Numerics.csproj

@ -49,6 +49,7 @@ Control.Describe now includes CPU architecture and family identifier if know</Pa
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" Condition="'$(TargetFramework)' == 'net461'" />
</ItemGroup>
</Project>

25
src/Numerics/Providers/FourierTransform/IFourierTransformProvider.cs

@ -27,6 +27,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using System;
using Complex = System.Numerics.Complex;
namespace MathNet.Numerics.Providers.FourierTransform
@ -58,19 +59,19 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// </summary>
void FreeResources();
void Forward(Complex32[] samples, FourierTransformScaling scaling);
void Forward(Complex[] samples, FourierTransformScaling scaling);
void Backward(Complex32[] spectrum, FourierTransformScaling scaling);
void Backward(Complex[] spectrum, FourierTransformScaling scaling);
void Forward(Memory<Complex32> samples, FourierTransformScaling scaling);
void Forward(Memory<Complex> samples, FourierTransformScaling scaling);
void Backward(Memory<Complex32> spectrum, FourierTransformScaling scaling);
void Backward(Memory<Complex> spectrum, FourierTransformScaling scaling);
void ForwardReal(float[] samples, int n, FourierTransformScaling scaling);
void ForwardReal(double[] samples, int n, FourierTransformScaling scaling);
void BackwardReal(float[] spectrum, int n, FourierTransformScaling scaling);
void BackwardReal(double[] spectrum, int n, FourierTransformScaling scaling);
void ForwardReal(Span<float> samples, int n, FourierTransformScaling scaling);
void ForwardReal(Span<double> samples, int n, FourierTransformScaling scaling);
void BackwardReal(Span<float> spectrum, int n, FourierTransformScaling scaling);
void BackwardReal(Span<double> spectrum, int n, FourierTransformScaling scaling);
void ForwardMultidim(Complex32[] samples, int[] dimensions, FourierTransformScaling scaling);
void ForwardMultidim(Complex[] samples, int[] dimensions, FourierTransformScaling scaling);
void BackwardMultidim(Complex32[] spectrum, int[] dimensions, FourierTransformScaling scaling);
void BackwardMultidim(Complex[] spectrum, int[] dimensions, FourierTransformScaling scaling);
void ForwardMultidim(Span<Complex32> samples, Span<int> dimensions, FourierTransformScaling scaling);
void ForwardMultidim(Span<Complex> samples, Span<int> dimensions, FourierTransformScaling scaling);
void BackwardMultidim(Span<Complex32> spectrum, Span<int> dimensions, FourierTransformScaling scaling);
void BackwardMultidim(Span<Complex> spectrum, Span<int> dimensions, FourierTransformScaling scaling);
}
}

48
src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.Bluestein.cs

@ -45,7 +45,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// </summary>
/// <param name="n">Number of samples.</param>
/// <returns>Bluestein sequence exp(I*Pi*k^2/N)</returns>
static Complex32[] BluesteinSequence32(int n)
static Memory<Complex32> BluesteinSequence32(int n)
{
double s = Constants.Pi / n;
var sequence = new Complex32[n];
@ -77,7 +77,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// </summary>
/// <param name="n">Number of samples.</param>
/// <returns>Bluestein sequence exp(I*Pi*k^2/N)</returns>
static Complex[] BluesteinSequence(int n)
static Memory<Complex> BluesteinSequence(int n)
{
double s = Constants.Pi / n;
var sequence = new Complex[n];
@ -108,10 +108,10 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Convolution with the bluestein sequence (Parallel Version).
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void BluesteinConvolutionParallel(Complex32[] samples)
static void BluesteinConvolutionParallel(Memory<Complex32> samples)
{
int n = samples.Length;
Complex32[] sequence = BluesteinSequence32(n);
Memory<Complex32> sequence = BluesteinSequence32(n);
// Padding to power of two >= 2N–1 so we can apply Radix-2 FFT.
int m = ((n << 1) - 1).CeilingToPowerOfTwo();
@ -124,12 +124,12 @@ namespace MathNet.Numerics.Providers.FourierTransform
// Build and transform padded sequence b_k = exp(I*Pi*k^2/N)
for (int i = 0; i < n; i++)
{
b[i] = sequence[i];
b[i] = sequence.Span[i];
}
for (int i = m - n + 1; i < b.Length; i++)
{
b[i] = sequence[m - i];
b[i] = sequence.Span[m - i];
}
Radix2Forward(b);
@ -139,7 +139,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
// 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];
a[i] = sequence.Span[i].Conjugate() * samples.Span[i];
}
Radix2Forward(a);
@ -155,7 +155,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
var nbinv = 1.0f / m;
for (int i = 0; i < samples.Length; i++)
{
samples[i] = nbinv * sequence[i].Conjugate() * a[i];
samples.Span[i] = nbinv * sequence.Span[i].Conjugate() * a[i];
}
}
@ -163,10 +163,10 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Convolution with the bluestein sequence (Parallel Version).
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void BluesteinConvolutionParallel(Complex[] samples)
static void BluesteinConvolutionParallel(Memory<Complex> samples)
{
int n = samples.Length;
Complex[] sequence = BluesteinSequence(n);
Memory<Complex> sequence = BluesteinSequence(n);
// Padding to power of two >= 2N–1 so we can apply Radix-2 FFT.
int m = ((n << 1) - 1).CeilingToPowerOfTwo();
@ -179,12 +179,12 @@ namespace MathNet.Numerics.Providers.FourierTransform
// Build and transform padded sequence b_k = exp(I*Pi*k^2/N)
for (int i = 0; i < n; i++)
{
b[i] = sequence[i];
b[i] = sequence.Span[i];
}
for (int i = m - n + 1; i < b.Length; i++)
{
b[i] = sequence[m - i];
b[i] = sequence.Span[m - i];
}
Radix2Forward(b);
@ -194,7 +194,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
// 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];
a[i] = sequence.Span[i].Conjugate() * samples.Span[i];
}
Radix2Forward(a);
@ -210,7 +210,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
var nbinv = 1.0 / m;
for (int i = 0; i < samples.Length; i++)
{
samples[i] = nbinv * sequence[i].Conjugate() * a[i];
samples.Span[i] = nbinv * sequence.Span[i].Conjugate() * a[i];
}
}
@ -218,7 +218,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Swap the real and imaginary parts of each sample.
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void SwapRealImaginary(Complex32[] samples)
static void SwapRealImaginary(Span<Complex32> samples)
{
for (int i = 0; i < samples.Length; i++)
{
@ -230,7 +230,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Swap the real and imaginary parts of each sample.
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void SwapRealImaginary(Complex[] samples)
static void SwapRealImaginary(Span<Complex> samples)
{
for (int i = 0; i < samples.Length; i++)
{
@ -241,7 +241,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Bluestein generic FFT for arbitrary sized sample vectors.
/// </summary>
static void BluesteinForward(Complex[] samples)
static void BluesteinForward(Memory<Complex> samples)
{
BluesteinConvolutionParallel(samples);
}
@ -249,17 +249,17 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Bluestein generic FFT for arbitrary sized sample vectors.
/// </summary>
static void BluesteinInverse(Complex[] spectrum)
static void BluesteinInverse(Memory<Complex> spectrum)
{
SwapRealImaginary(spectrum);
SwapRealImaginary(spectrum.Span);
BluesteinConvolutionParallel(spectrum);
SwapRealImaginary(spectrum);
SwapRealImaginary(spectrum.Span);
}
/// <summary>
/// Bluestein generic FFT for arbitrary sized sample vectors.
/// </summary>
static void BluesteinForward(Complex32[] samples)
static void BluesteinForward(Memory<Complex32> samples)
{
BluesteinConvolutionParallel(samples);
}
@ -267,11 +267,11 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Bluestein generic FFT for arbitrary sized sample vectors.
/// </summary>
static void BluesteinInverse(Complex32[] spectrum)
static void BluesteinInverse(Memory<Complex32> spectrum)
{
SwapRealImaginary(spectrum);
SwapRealImaginary(spectrum.Span);
BluesteinConvolutionParallel(spectrum);
SwapRealImaginary(spectrum);
SwapRealImaginary(spectrum.Span);
}
}
}

38
src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.Radix2.cs

@ -40,7 +40,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// </summary>
/// <typeparam name="T">Sample type</typeparam>
/// <param name="samples">Sample vector</param>
static void Radix2Reorder<T>(T[] samples)
static void Radix2Reorder<T>(Span<T> samples)
{
var j = 0;
for (var i = 0; i < samples.Length - 1; i++)
@ -73,7 +73,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
static void Radix2Step(Complex32[] samples, int exponentSign, int levelSize, int k)
static void Radix2Step(Span<Complex32> samples, int exponentSign, int levelSize, int k)
{
// Twiddle Factor
var exponent = (exponentSign * k) * Constants.Pi / levelSize;
@ -99,7 +99,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
static void Radix2Step(Complex[] samples, int exponentSign, int levelSize, int k)
static void Radix2Step(Span<Complex> samples, int exponentSign, int levelSize, int k)
{
// Twiddle Factor
var exponent = (exponentSign * k) * Constants.Pi / levelSize;
@ -118,7 +118,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sized sample vectors.
/// </summary>
static void Radix2Forward(Complex32[] data)
static void Radix2Forward(Span<Complex32> data)
{
Radix2Reorder(data);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
@ -133,7 +133,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sized sample vectors.
/// </summary>
static void Radix2Forward(Complex[] data)
static void Radix2Forward(Span<Complex> data)
{
Radix2Reorder(data);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
@ -148,7 +148,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sized sample vectors.
/// </summary>
static void Radix2Inverse(Complex32[] data)
static void Radix2Inverse(Span<Complex32> data)
{
Radix2Reorder(data);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
@ -163,7 +163,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sized sample vectors.
/// </summary>
static void Radix2Inverse(Complex[] data)
static void Radix2Inverse(Span<Complex> data)
{
Radix2Reorder(data);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
@ -178,9 +178,9 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sample vectors (Parallel Version).
/// </summary>
static void Radix2ForwardParallel(Complex32[] data)
static void Radix2ForwardParallel(Memory<Complex32> data)
{
Radix2Reorder(data);
Radix2Reorder(data.Span);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
{
var size = levelSize;
@ -189,7 +189,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
for (int i = u; i < v; i++)
{
Radix2Step(data, -1, size, i);
Radix2Step(data.Span, -1, size, i);
}
});
}
@ -198,9 +198,9 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sample vectors (Parallel Version).
/// </summary>
static void Radix2ForwardParallel(Complex[] data)
static void Radix2ForwardParallel(Memory<Complex> data)
{
Radix2Reorder(data);
Radix2Reorder(data.Span);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
{
var size = levelSize;
@ -209,7 +209,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
for (int i = u; i < v; i++)
{
Radix2Step(data, -1, size, i);
Radix2Step(data.Span, -1, size, i);
}
});
}
@ -218,9 +218,9 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sample vectors (Parallel Version).
/// </summary>
static void Radix2InverseParallel(Complex32[] data)
static void Radix2InverseParallel(Memory<Complex32> data)
{
Radix2Reorder(data);
Radix2Reorder(data.Span);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
{
var size = levelSize;
@ -229,7 +229,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
for (int i = u; i < v; i++)
{
Radix2Step(data, 1, size, i);
Radix2Step(data.Span, 1, size, i);
}
});
}
@ -238,9 +238,9 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// <summary>
/// Radix-2 generic FFT for power-of-two sample vectors (Parallel Version).
/// </summary>
static void Radix2InverseParallel(Complex[] data)
static void Radix2InverseParallel(Memory<Complex> data)
{
Radix2Reorder(data);
Radix2Reorder(data.Span);
for (var levelSize = 1; levelSize < data.Length; levelSize *= 2)
{
var size = levelSize;
@ -249,7 +249,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
for (int i = u; i < v; i++)
{
Radix2Step(data, 1, size, i);
Radix2Step(data.Span, 1, size, i);
}
});
}

8
src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.Scaling.cs

@ -39,7 +39,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Fully rescale the FFT result.
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void FullRescale(Complex32[] samples)
static void FullRescale(Span<Complex32> samples)
{
var scalingFactor = (float)1.0 / samples.Length;
for (int i = 0; i < samples.Length; i++)
@ -52,7 +52,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Fully rescale the FFT result.
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void FullRescale(Complex[] samples)
static void FullRescale(Span<Complex> samples)
{
var scalingFactor = 1.0 / samples.Length;
for (int i = 0; i < samples.Length; i++)
@ -65,7 +65,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Half rescale the FFT result (e.g. for symmetric transforms).
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void HalfRescale(Complex32[] samples)
static void HalfRescale(Span<Complex32> samples)
{
var scalingFactor = (float)Math.Sqrt(1.0 / samples.Length);
for (int i = 0; i < samples.Length; i++)
@ -78,7 +78,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
/// Fully rescale the FFT result (e.g. for symmetric transforms).
/// </summary>
/// <param name="samples">Sample Vector.</param>
static void HalfRescale(Complex[] samples)
static void HalfRescale(Span<Complex> samples)
{
var scalingFactor = Math.Sqrt(1.0 / samples.Length);
for (int i = 0; i < samples.Length; i++)

48
src/Numerics/Providers/FourierTransform/ManagedFourierTransformProvider.cs

@ -64,7 +64,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
return "Managed";
}
public void Forward(Complex32[] samples, FourierTransformScaling scaling)
public void Forward(Memory<Complex32> samples, FourierTransformScaling scaling)
{
if (samples.Length.IsPowerOfTwo())
{
@ -74,7 +74,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
}
else
{
Radix2Forward(samples);
Radix2Forward(samples.Span);
}
}
else
@ -86,18 +86,18 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
case FourierTransformScaling.SymmetricScaling:
{
HalfRescale(samples);
HalfRescale(samples.Span);
break;
}
case FourierTransformScaling.ForwardScaling:
{
FullRescale(samples);
FullRescale(samples.Span);
break;
}
}
}
public void Forward(Complex[] samples, FourierTransformScaling scaling)
public void Forward(Memory<Complex> samples, FourierTransformScaling scaling)
{
if (samples.Length.IsPowerOfTwo())
{
@ -107,7 +107,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
}
else
{
Radix2Forward(samples);
Radix2Forward(samples.Span);
}
}
else
@ -119,18 +119,18 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
case FourierTransformScaling.SymmetricScaling:
{
HalfRescale(samples);
HalfRescale(samples.Span);
break;
}
case FourierTransformScaling.ForwardScaling:
{
FullRescale(samples);
FullRescale(samples.Span);
break;
}
}
}
public void Backward(Complex32[] spectrum, FourierTransformScaling scaling)
public void Backward(Memory<Complex32> spectrum, FourierTransformScaling scaling)
{
if (spectrum.Length.IsPowerOfTwo())
{
@ -140,7 +140,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
}
else
{
Radix2Inverse(spectrum);
Radix2Inverse(spectrum.Span);
}
}
else
@ -152,18 +152,18 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
case FourierTransformScaling.SymmetricScaling:
{
HalfRescale(spectrum);
HalfRescale(spectrum.Span);
break;
}
case FourierTransformScaling.BackwardScaling:
{
FullRescale(spectrum);
FullRescale(spectrum.Span);
break;
}
}
}
public void Backward(Complex[] spectrum, FourierTransformScaling scaling)
public void Backward(Memory<Complex> spectrum, FourierTransformScaling scaling)
{
if (spectrum.Length.IsPowerOfTwo())
{
@ -173,7 +173,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
}
else
{
Radix2Inverse(spectrum);
Radix2Inverse(spectrum.Span);
}
}
else
@ -185,18 +185,18 @@ namespace MathNet.Numerics.Providers.FourierTransform
{
case FourierTransformScaling.SymmetricScaling:
{
HalfRescale(spectrum);
HalfRescale(spectrum.Span);
break;
}
case FourierTransformScaling.BackwardScaling:
{
FullRescale(spectrum);
FullRescale(spectrum.Span);
break;
}
}
}
public void ForwardReal(float[] samples, int n, FourierTransformScaling scaling)
public void ForwardReal(Span<float> samples, int n, FourierTransformScaling scaling)
{
// TODO: backport proper, optimized implementation from Iridium
@ -227,7 +227,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
}
}
public void ForwardReal(double[] samples, int n, FourierTransformScaling scaling)
public void ForwardReal(Span<double> samples, int n, FourierTransformScaling scaling)
{
// TODO: backport proper, optimized implementation from Iridium
@ -258,7 +258,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
}
}
public void BackwardReal(float[] spectrum, int n, FourierTransformScaling scaling)
public void BackwardReal(Span<float> spectrum, int n, FourierTransformScaling scaling)
{
// TODO: backport proper, optimized implementation from Iridium
@ -288,7 +288,7 @@ namespace MathNet.Numerics.Providers.FourierTransform
spectrum[n] = 0f;
}
public void BackwardReal(double[] spectrum, int n, FourierTransformScaling scaling)
public void BackwardReal(Span<double> spectrum, int n, FourierTransformScaling scaling)
{
// TODO: backport proper, optimized implementation from Iridium
@ -318,22 +318,22 @@ namespace MathNet.Numerics.Providers.FourierTransform
spectrum[n] = 0d;
}
public void ForwardMultidim(Complex32[] samples, int[] dimensions, FourierTransformScaling scaling)
public void ForwardMultidim(Span<Complex32> samples, Span<int> dimensions, FourierTransformScaling scaling)
{
throw new NotSupportedException();
}
public void ForwardMultidim(Complex[] samples, int[] dimensions, FourierTransformScaling scaling)
public void ForwardMultidim(Span<Complex> samples, Span<int> dimensions, FourierTransformScaling scaling)
{
throw new NotSupportedException();
}
public void BackwardMultidim(Complex32[] spectrum, int[] dimensions, FourierTransformScaling scaling)
public void BackwardMultidim(Span<Complex32> spectrum, Span<int> dimensions, FourierTransformScaling scaling)
{
throw new NotSupportedException();
}
public void BackwardMultidim(Complex[] spectrum, int[] dimensions, FourierTransformScaling scaling)
public void BackwardMultidim(Span<Complex> spectrum, Span<int> dimensions, FourierTransformScaling scaling)
{
throw new NotSupportedException();
}

Loading…
Cancel
Save