FastFourierTransformer.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.transform;

  22. import java.io.Serializable;

  23. import org.hipparchus.analysis.FunctionUtils;
  24. import org.hipparchus.analysis.UnivariateFunction;
  25. import org.hipparchus.complex.Complex;
  26. import org.hipparchus.exception.MathIllegalArgumentException;
  27. import org.hipparchus.exception.MathRuntimeException;
  28. import org.hipparchus.util.ArithmeticUtils;
  29. import org.hipparchus.util.FastMath;
  30. import org.hipparchus.util.MathArrays;
  31. import org.hipparchus.util.MathUtils;

  32. /**
  33.  * Implements the Fast Fourier Transform for transformation of one-dimensional
  34.  * real or complex data sets. For reference, see <em>Applied Numerical Linear
  35.  * Algebra</em>, ISBN 0898713897, chapter 6.
  36.  * <p>
  37.  * There are several variants of the discrete Fourier transform, with various
  38.  * normalization conventions, which are specified by the parameter
  39.  * {@link DftNormalization}.
  40.  * <p>
  41.  * The current implementation of the discrete Fourier transform as a fast
  42.  * Fourier transform requires the length of the data set to be a power of 2.
  43.  * This greatly simplifies and speeds up the code. Users can pad the data with
  44.  * zeros to meet this requirement. There are other flavors of FFT, for
  45.  * reference, see S. Winograd,
  46.  * <i>On computing the discrete Fourier transform</i>, Mathematics of
  47.  * Computation, 32 (1978), 175 - 199.
  48.  *
  49.  * @see DftNormalization
  50.  */
  51. public class FastFourierTransformer implements Serializable {

  52.     /** Serializable version identifier. */
  53.     private static final long serialVersionUID = 20120210L;

  54.     /**
  55.      * {@code W_SUB_N_R[i]} is the real part of
  56.      * {@code exp(- 2 * i * pi / n)}:
  57.      * {@code W_SUB_N_R[i] = cos(2 * pi/ n)}, where {@code n = 2^i}.
  58.      */
  59.     private static final double[] W_SUB_N_R =
  60.             {  0x1.0p0, -0x1.0p0, 0x1.1a62633145c07p-54, 0x1.6a09e667f3bcdp-1
  61.             , 0x1.d906bcf328d46p-1, 0x1.f6297cff75cbp-1, 0x1.fd88da3d12526p-1, 0x1.ff621e3796d7ep-1
  62.             , 0x1.ffd886084cd0dp-1, 0x1.fff62169b92dbp-1, 0x1.fffd8858e8a92p-1, 0x1.ffff621621d02p-1
  63.             , 0x1.ffffd88586ee6p-1, 0x1.fffff62161a34p-1, 0x1.fffffd8858675p-1, 0x1.ffffff621619cp-1
  64.             , 0x1.ffffffd885867p-1, 0x1.fffffff62161ap-1, 0x1.fffffffd88586p-1, 0x1.ffffffff62162p-1
  65.             , 0x1.ffffffffd8858p-1, 0x1.fffffffff6216p-1, 0x1.fffffffffd886p-1, 0x1.ffffffffff621p-1
  66.             , 0x1.ffffffffffd88p-1, 0x1.fffffffffff62p-1, 0x1.fffffffffffd9p-1, 0x1.ffffffffffff6p-1
  67.             , 0x1.ffffffffffffep-1, 0x1.fffffffffffffp-1, 0x1.0p0, 0x1.0p0
  68.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  69.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  70.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  71.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  72.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  73.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  74.             , 0x1.0p0, 0x1.0p0, 0x1.0p0, 0x1.0p0
  75.             , 0x1.0p0, 0x1.0p0, 0x1.0p0 };

  76.     /**
  77.      * {@code W_SUB_N_I[i]} is the imaginary part of
  78.      * {@code exp(- 2 * i * pi / n)}:
  79.      * {@code W_SUB_N_I[i] = -sin(2 * pi/ n)}, where {@code n = 2^i}.
  80.      */
  81.     private static final double[] W_SUB_N_I =
  82.             {  0x1.1a62633145c07p-52, -0x1.1a62633145c07p-53, -0x1.0p0, -0x1.6a09e667f3bccp-1
  83.             , -0x1.87de2a6aea963p-2, -0x1.8f8b83c69a60ap-3, -0x1.917a6bc29b42cp-4, -0x1.91f65f10dd814p-5
  84.             , -0x1.92155f7a3667ep-6, -0x1.921d1fcdec784p-7, -0x1.921f0fe670071p-8, -0x1.921f8becca4bap-9
  85.             , -0x1.921faaee6472dp-10, -0x1.921fb2aecb36p-11, -0x1.921fb49ee4ea6p-12, -0x1.921fb51aeb57bp-13
  86.             , -0x1.921fb539ecf31p-14, -0x1.921fb541ad59ep-15, -0x1.921fb5439d73ap-16, -0x1.921fb544197ap-17
  87.             , -0x1.921fb544387bap-18, -0x1.921fb544403c1p-19, -0x1.921fb544422c2p-20, -0x1.921fb54442a83p-21
  88.             , -0x1.921fb54442c73p-22, -0x1.921fb54442cefp-23, -0x1.921fb54442d0ep-24, -0x1.921fb54442d15p-25
  89.             , -0x1.921fb54442d17p-26, -0x1.921fb54442d18p-27, -0x1.921fb54442d18p-28, -0x1.921fb54442d18p-29
  90.             , -0x1.921fb54442d18p-30, -0x1.921fb54442d18p-31, -0x1.921fb54442d18p-32, -0x1.921fb54442d18p-33
  91.             , -0x1.921fb54442d18p-34, -0x1.921fb54442d18p-35, -0x1.921fb54442d18p-36, -0x1.921fb54442d18p-37
  92.             , -0x1.921fb54442d18p-38, -0x1.921fb54442d18p-39, -0x1.921fb54442d18p-40, -0x1.921fb54442d18p-41
  93.             , -0x1.921fb54442d18p-42, -0x1.921fb54442d18p-43, -0x1.921fb54442d18p-44, -0x1.921fb54442d18p-45
  94.             , -0x1.921fb54442d18p-46, -0x1.921fb54442d18p-47, -0x1.921fb54442d18p-48, -0x1.921fb54442d18p-49
  95.             , -0x1.921fb54442d18p-50, -0x1.921fb54442d18p-51, -0x1.921fb54442d18p-52, -0x1.921fb54442d18p-53
  96.             , -0x1.921fb54442d18p-54, -0x1.921fb54442d18p-55, -0x1.921fb54442d18p-56, -0x1.921fb54442d18p-57
  97.             , -0x1.921fb54442d18p-58, -0x1.921fb54442d18p-59, -0x1.921fb54442d18p-60 };

  98.     /** The type of DFT to be performed. */
  99.     private final DftNormalization normalization;

  100.     /**
  101.      * Creates a new instance of this class, with various normalization
  102.      * conventions.
  103.      *
  104.      * @param normalization the type of normalization to be applied to the
  105.      * transformed data
  106.      */
  107.     public FastFourierTransformer(final DftNormalization normalization) {
  108.         this.normalization = normalization;
  109.     }

  110.     /**
  111.      * Performs identical index bit reversal shuffles on two arrays of identical
  112.      * size. Each element in the array is swapped with another element based on
  113.      * the bit-reversal of the index. For example, in an array with length 16,
  114.      * item at binary index 0011 (decimal 3) would be swapped with the item at
  115.      * binary index 1100 (decimal 12).
  116.      *
  117.      * @param a the first array to be shuffled
  118.      * @param b the second array to be shuffled
  119.      */
  120.     private static void bitReversalShuffle2(double[] a, double[] b) {
  121.         final int n = a.length;
  122.         assert b.length == n;
  123.         final int halfOfN = n >> 1;

  124.         int j = 0;
  125.         for (int i = 0; i < n; i++) {
  126.             if (i < j) {
  127.                 // swap indices i & j
  128.                 double temp = a[i];
  129.                 a[i] = a[j];
  130.                 a[j] = temp;

  131.                 temp = b[i];
  132.                 b[i] = b[j];
  133.                 b[j] = temp;
  134.             }

  135.             int k = halfOfN;
  136.             while (k <= j && k > 0) {
  137.                 j -= k;
  138.                 k >>= 1;
  139.             }
  140.             j += k;
  141.         }
  142.     }

  143.     /**
  144.      * Applies the proper normalization to the specified transformed data.
  145.      *
  146.      * @param dataRI the unscaled transformed data
  147.      * @param normalization the normalization to be applied
  148.      * @param type the type of transform (forward, inverse) which resulted in the specified data
  149.      */
  150.     private static void normalizeTransformedData(final double[][] dataRI,
  151.         final DftNormalization normalization, final TransformType type) {

  152.         final double[] dataR = dataRI[0];
  153.         final double[] dataI = dataRI[1];
  154.         final int n = dataR.length;
  155.         assert dataI.length == n;

  156.         switch (normalization) {
  157.             case STANDARD:
  158.                 if (type == TransformType.INVERSE) {
  159.                     final double scaleFactor = 1.0 / n;
  160.                     for (int i = 0; i < n; i++) {
  161.                         dataR[i] *= scaleFactor;
  162.                         dataI[i] *= scaleFactor;
  163.                     }
  164.                 }
  165.                 break;
  166.             case UNITARY:
  167.                 final double scaleFactor = 1.0 / FastMath.sqrt(n);
  168.                 for (int i = 0; i < n; i++) {
  169.                     dataR[i] *= scaleFactor;
  170.                     dataI[i] *= scaleFactor;
  171.                 }
  172.                 break;
  173.             default:
  174.                 // This should never occur in normal conditions. However this
  175.                 // clause has been added as a safeguard if other types of
  176.                 // normalizations are ever implemented, and the corresponding
  177.                 // test is forgotten in the present switch.
  178.                 throw MathRuntimeException.createInternalError();
  179.         }
  180.     }

  181.     /**
  182.      * Computes the standard transform of the specified complex data. The
  183.      * computation is done in place. The input data is laid out as follows
  184.      * <ul>
  185.      *   <li>{@code dataRI[0][i]} is the real part of the {@code i}-th data point,</li>
  186.      *   <li>{@code dataRI[1][i]} is the imaginary part of the {@code i}-th data point.</li>
  187.      * </ul>
  188.      *
  189.      * @param dataRI the two dimensional array of real and imaginary parts of the data
  190.      * @param normalization the normalization to be applied to the transformed data
  191.      * @param type the type of transform (forward, inverse) to be performed
  192.      * @throws MathIllegalArgumentException if the number of rows of the specified
  193.      *   array is not two, or the array is not rectangular
  194.      * @throws MathIllegalArgumentException if the number of data points is not
  195.      *   a power of two
  196.      */
  197.     public static void transformInPlace(final double[][] dataRI,
  198.         final DftNormalization normalization, final TransformType type) {

  199.         MathUtils.checkDimension(dataRI.length, 2);
  200.         final double[] dataR = dataRI[0];
  201.         final double[] dataI = dataRI[1];
  202.         MathArrays.checkEqualLength(dataR, dataI);

  203.         final int n = dataR.length;
  204.         if (!ArithmeticUtils.isPowerOfTwo(n)) {
  205.             throw new MathIllegalArgumentException(LocalizedFFTFormats.NOT_POWER_OF_TWO_CONSIDER_PADDING,
  206.                     n);
  207.         }

  208.         if (n == 1) {
  209.             return;
  210.         } else if (n == 2) {
  211.             final double srcR0 = dataR[0];
  212.             final double srcI0 = dataI[0];
  213.             final double srcR1 = dataR[1];
  214.             final double srcI1 = dataI[1];

  215.             // X_0 = x_0 + x_1
  216.             dataR[0] = srcR0 + srcR1;
  217.             dataI[0] = srcI0 + srcI1;
  218.             // X_1 = x_0 - x_1
  219.             dataR[1] = srcR0 - srcR1;
  220.             dataI[1] = srcI0 - srcI1;

  221.             normalizeTransformedData(dataRI, normalization, type);
  222.             return;
  223.         }

  224.         bitReversalShuffle2(dataR, dataI);

  225.         // Do 4-term DFT.
  226.         if (type == TransformType.INVERSE) {
  227.             for (int i0 = 0; i0 < n; i0 += 4) {
  228.                 final int i1 = i0 + 1;
  229.                 final int i2 = i0 + 2;
  230.                 final int i3 = i0 + 3;

  231.                 final double srcR0 = dataR[i0];
  232.                 final double srcI0 = dataI[i0];
  233.                 final double srcR1 = dataR[i2];
  234.                 final double srcI1 = dataI[i2];
  235.                 final double srcR2 = dataR[i1];
  236.                 final double srcI2 = dataI[i1];
  237.                 final double srcR3 = dataR[i3];
  238.                 final double srcI3 = dataI[i3];

  239.                 // 4-term DFT
  240.                 // X_0 = x_0 + x_1 + x_2 + x_3
  241.                 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
  242.                 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
  243.                 // X_1 = x_0 - x_2 + j * (x_3 - x_1)
  244.                 dataR[i1] = srcR0 - srcR2 + (srcI3 - srcI1);
  245.                 dataI[i1] = srcI0 - srcI2 + (srcR1 - srcR3);
  246.                 // X_2 = x_0 - x_1 + x_2 - x_3
  247.                 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
  248.                 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
  249.                 // X_3 = x_0 - x_2 + j * (x_1 - x_3)
  250.                 dataR[i3] = srcR0 - srcR2 + (srcI1 - srcI3);
  251.                 dataI[i3] = srcI0 - srcI2 + (srcR3 - srcR1);
  252.             }
  253.         } else {
  254.             for (int i0 = 0; i0 < n; i0 += 4) {
  255.                 final int i1 = i0 + 1;
  256.                 final int i2 = i0 + 2;
  257.                 final int i3 = i0 + 3;

  258.                 final double srcR0 = dataR[i0];
  259.                 final double srcI0 = dataI[i0];
  260.                 final double srcR1 = dataR[i2];
  261.                 final double srcI1 = dataI[i2];
  262.                 final double srcR2 = dataR[i1];
  263.                 final double srcI2 = dataI[i1];
  264.                 final double srcR3 = dataR[i3];
  265.                 final double srcI3 = dataI[i3];

  266.                 // 4-term DFT
  267.                 // X_0 = x_0 + x_1 + x_2 + x_3
  268.                 dataR[i0] = srcR0 + srcR1 + srcR2 + srcR3;
  269.                 dataI[i0] = srcI0 + srcI1 + srcI2 + srcI3;
  270.                 // X_1 = x_0 - x_2 + j * (x_3 - x_1)
  271.                 dataR[i1] = srcR0 - srcR2 + (srcI1 - srcI3);
  272.                 dataI[i1] = srcI0 - srcI2 + (srcR3 - srcR1);
  273.                 // X_2 = x_0 - x_1 + x_2 - x_3
  274.                 dataR[i2] = srcR0 - srcR1 + srcR2 - srcR3;
  275.                 dataI[i2] = srcI0 - srcI1 + srcI2 - srcI3;
  276.                 // X_3 = x_0 - x_2 + j * (x_1 - x_3)
  277.                 dataR[i3] = srcR0 - srcR2 + (srcI3 - srcI1);
  278.                 dataI[i3] = srcI0 - srcI2 + (srcR1 - srcR3);
  279.             }
  280.         }

  281.         int lastN0 = 4;
  282.         int lastLogN0 = 2;
  283.         while (lastN0 < n) {
  284.             int n0 = lastN0 << 1;
  285.             int logN0 = lastLogN0 + 1;
  286.             double wSubN0R = W_SUB_N_R[logN0];
  287.             double wSubN0I = W_SUB_N_I[logN0];
  288.             if (type == TransformType.INVERSE) {
  289.                 wSubN0I = -wSubN0I;
  290.             }

  291.             // Combine even/odd transforms of size lastN0 into a transform of size N0 (lastN0 * 2).
  292.             for (int destEvenStartIndex = 0; destEvenStartIndex < n; destEvenStartIndex += n0) {
  293.                 int destOddStartIndex = destEvenStartIndex + lastN0;

  294.                 double wSubN0ToRR = 1;
  295.                 double wSubN0ToRI = 0;

  296.                 for (int r = 0; r < lastN0; r++) {
  297.                     double grR = dataR[destEvenStartIndex + r];
  298.                     double grI = dataI[destEvenStartIndex + r];
  299.                     double hrR = dataR[destOddStartIndex + r];
  300.                     double hrI = dataI[destOddStartIndex + r];

  301.                     // dest[destEvenStartIndex + r] = Gr + WsubN0ToR * Hr
  302.                     dataR[destEvenStartIndex + r] = grR + wSubN0ToRR * hrR - wSubN0ToRI * hrI;
  303.                     dataI[destEvenStartIndex + r] = grI + wSubN0ToRR * hrI + wSubN0ToRI * hrR;
  304.                     // dest[destOddStartIndex + r] = Gr - WsubN0ToR * Hr
  305.                     dataR[destOddStartIndex + r] = grR - (wSubN0ToRR * hrR - wSubN0ToRI * hrI);
  306.                     dataI[destOddStartIndex + r] = grI - (wSubN0ToRR * hrI + wSubN0ToRI * hrR);

  307.                     // WsubN0ToR *= WsubN0R
  308.                     double nextWsubN0ToRR = wSubN0ToRR * wSubN0R - wSubN0ToRI * wSubN0I;
  309.                     double nextWsubN0ToRI = wSubN0ToRR * wSubN0I + wSubN0ToRI * wSubN0R;
  310.                     wSubN0ToRR = nextWsubN0ToRR;
  311.                     wSubN0ToRI = nextWsubN0ToRI;
  312.                 }
  313.             }

  314.             lastN0 = n0;
  315.             lastLogN0 = logN0;
  316.         }

  317.         normalizeTransformedData(dataRI, normalization, type);
  318.     }

  319.     /**
  320.      * Returns the (forward, inverse) transform of the specified real data set.
  321.      *
  322.      * @param f the real data array to be transformed
  323.      * @param type the type of transform (forward, inverse) to be performed
  324.      * @return the complex transformed array
  325.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  326.      */
  327.     public Complex[] transform(final double[] f, final TransformType type) {
  328.         final double[][] dataRI = { f.clone(), new double[f.length] };
  329.         transformInPlace(dataRI, normalization, type);
  330.         return TransformUtils.createComplexArray(dataRI);
  331.     }

  332.     /**
  333.      * Returns the (forward, inverse) transform of the specified real function,
  334.      * sampled on the specified interval.
  335.      *
  336.      * @param f the function to be sampled and transformed
  337.      * @param min the (inclusive) lower bound for the interval
  338.      * @param max the (exclusive) upper bound for the interval
  339.      * @param n the number of sample points
  340.      * @param type the type of transform (forward, inverse) to be performed
  341.      * @return the complex transformed array
  342.      * @throws org.hipparchus.exception.MathIllegalArgumentException
  343.      *   if the lower bound is greater than, or equal to the upper bound
  344.      * @throws org.hipparchus.exception.MathIllegalArgumentException
  345.      *   if the number of sample points {@code n} is negative
  346.      * @throws MathIllegalArgumentException if the number of sample points
  347.      *   {@code n} is not a power of two
  348.      */
  349.     public Complex[] transform(final UnivariateFunction f,
  350.                                final double min, final double max, final int n,
  351.                                final TransformType type) {

  352.         final double[] data = FunctionUtils.sample(f, min, max, n);
  353.         return transform(data, type);
  354.     }

  355.     /**
  356.      * Returns the (forward, inverse) transform of the specified complex data set.
  357.      *
  358.      * @param f the complex data array to be transformed
  359.      * @param type the type of transform (forward, inverse) to be performed
  360.      * @return the complex transformed array
  361.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  362.      */
  363.     public Complex[] transform(final Complex[] f, final TransformType type) {
  364.         final double[][] dataRI = TransformUtils.createRealImaginaryArray(f);

  365.         transformInPlace(dataRI, normalization, type);

  366.         return TransformUtils.createComplexArray(dataRI);
  367.     }

  368. }