FastHadamardTransformer.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.exception.MathIllegalArgumentException;
  26. import org.hipparchus.util.ArithmeticUtils;

  27. /**
  28.  * Implements the <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">Fast Hadamard Transform</a> (FHT).
  29.  * Transformation of an input vector x to the output vector y.
  30.  * <p>
  31.  * In addition to transformation of real vectors, the Hadamard transform can
  32.  * transform integer vectors into integer vectors. However, this integer transform
  33.  * cannot be inverted directly. Due to a scaling factor it may lead to rational results.
  34.  * As an example, the inverse transform of integer vector (0, 1, 0, 1) is rational
  35.  * vector (1/2, -1/2, 0, 0).
  36.  *
  37.  */
  38. public class FastHadamardTransformer implements RealTransformer, Serializable {

  39.     /** Serializable version identifier. */
  40.     static final long serialVersionUID = 20120211L;

  41.     /** Empty constructor.
  42.      * <p>
  43.      * This constructor is not strictly necessary, but it prevents spurious
  44.      * javadoc warnings with JDK 18 and later.
  45.      * </p>
  46.      * @since 3.0
  47.      */
  48.     public FastHadamardTransformer() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
  49.         // nothing to do
  50.     }

  51.     /**
  52.      * {@inheritDoc}
  53.      *
  54.      * @throws MathIllegalArgumentException if the length of the data array is
  55.      * not a power of two
  56.      */
  57.     @Override
  58.     public double[] transform(final double[] f, final TransformType type) {
  59.         if (type == TransformType.FORWARD) {
  60.             return fht(f);
  61.         }
  62.         return TransformUtils.scaleArray(fht(f), 1.0 / f.length);
  63.     }

  64.     /**
  65.      * {@inheritDoc}
  66.      *
  67.      * @throws org.hipparchus.exception.MathIllegalArgumentException
  68.      *   if the lower bound is greater than, or equal to the upper bound
  69.      * @throws org.hipparchus.exception.MathIllegalArgumentException
  70.      *   if the number of sample points is negative
  71.      * @throws MathIllegalArgumentException if the number of sample points is not a power of two
  72.      */
  73.     @Override
  74.     public double[] transform(final UnivariateFunction f,
  75.         final double min, final double max, final int n,
  76.         final TransformType type) {

  77.         return transform(FunctionUtils.sample(f, min, max, n), type);
  78.     }

  79.     /**
  80.      * Returns the forward transform of the specified integer data set.The
  81.      * integer transform cannot be inverted directly, due to a scaling factor
  82.      * which may lead to double results.
  83.      *
  84.      * @param f the integer data array to be transformed (signal)
  85.      * @return the integer transformed array (spectrum)
  86.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  87.      */
  88.     public int[] transform(final int[] f) {
  89.         return fht(f);
  90.     }

  91.     /**
  92.      * The FHT (Fast Hadamard Transformation) which uses only subtraction and
  93.      * addition. Requires {@code N * log2(N) = n * 2^n} additions.
  94.      *
  95.      * <ol>
  96.      * <li><b>x</b> is the input vector to be transformed,</li>
  97.      * <li><b>y</b> is the output vector (Fast Hadamard transform of <b>x</b>),</li>
  98.      * <li>a and b are helper rows.</li>
  99.      * </ol>
  100.      * <table border="1">
  101.      * <caption>Short Table of manual calculation for N=8</caption>
  102.      * <tbody>
  103.      * <tr>
  104.      *     <th>x</th>
  105.      *     <th>a</th>
  106.      *     <th>b</th>
  107.      *     <th>y</th>
  108.      * </tr>
  109.      * <tr>
  110.      *     <th>x<sub>0</sub></th>
  111.      *     <td>a<sub>0</sub> = x<sub>0</sub> + x<sub>1</sub></td>
  112.      *     <td>b<sub>0</sub> = a<sub>0</sub> + a<sub>1</sub></td>
  113.      *     <td>y<sub>0</sub> = b<sub>0</sub >+ b<sub>1</sub></td>
  114.      * </tr>
  115.      * <tr>
  116.      *     <th>x<sub>1</sub></th>
  117.      *     <td>a<sub>1</sub> = x<sub>2</sub> + x<sub>3</sub></td>
  118.      *     <td>b<sub>0</sub> = a<sub>2</sub> + a<sub>3</sub></td>
  119.      *     <td>y<sub>0</sub> = b<sub>2</sub> + b<sub>3</sub></td>
  120.      * </tr>
  121.      * <tr>
  122.      *     <th>x<sub>2</sub></th>
  123.      *     <td>a<sub>2</sub> = x<sub>4</sub> + x<sub>5</sub></td>
  124.      *     <td>b<sub>0</sub> = a<sub>4</sub> + a<sub>5</sub></td>
  125.      *     <td>y<sub>0</sub> = b<sub>4</sub> + b<sub>5</sub></td>
  126.      * </tr>
  127.      * <tr>
  128.      *     <th>x<sub>3</sub></th>
  129.      *     <td>a<sub>3</sub> = x<sub>6</sub> + x<sub>7</sub></td>
  130.      *     <td>b<sub>0</sub> = a<sub>6</sub> + a<sub>7</sub></td>
  131.      *     <td>y<sub>0</sub> = b<sub>6</sub> + b<sub>7</sub></td>
  132.      * </tr>
  133.      * <tr>
  134.      *     <th>x<sub>4</sub></th>
  135.      *     <td>a<sub>0</sub> = x<sub>0</sub> - x<sub>1</sub></td>
  136.      *     <td>b<sub>0</sub> = a<sub>0</sub> - a<sub>1</sub></td>
  137.      *     <td>y<sub>0</sub> = b<sub>0</sub> - b<sub>1</sub></td>
  138.      * </tr>
  139.      * <tr>
  140.      *     <th>x<sub>5</sub></th>
  141.      *     <td>a<sub>1</sub> = x<sub>2</sub> - x<sub>3</sub></td>
  142.      *     <td>b<sub>0</sub> = a<sub>2</sub> - a<sub>3</sub></td>
  143.      *     <td>y<sub>0</sub> = b<sub>2</sub> - b<sub>3</sub></td>
  144.      * </tr>
  145.      * <tr>
  146.      *     <th>x<sub>6</sub></th>
  147.      *     <td>a<sub>2</sub> = x<sub>4</sub> - x<sub>5</sub></td>
  148.      *     <td>b<sub>0</sub> = a<sub>4</sub> - a<sub>5</sub></td>
  149.      *     <td>y<sub>0</sub> = b<sub>4</sub> - b<sub>5</sub></td>
  150.      * </tr>
  151.      * <tr>
  152.      *     <th>x<sub>7</sub></th>
  153.      *     <td>a<sub>3</sub> = x<sub>6</sub> - x<sub>7</sub></td>
  154.      *     <td>b<sub>0</sub> = a<sub>6</sub> - a<sub>7</sub></td>
  155.      *     <td>y<sub>0</sub> = b<sub>6</sub> - b<sub>7</sub></td>
  156.      * </tr>
  157.      * </tbody>
  158.      * </table>
  159.      *
  160.      * <p>How it works</p>
  161.      * <ol>
  162.      * <li>Construct a matrix with {@code N} rows and {@code n + 1} columns,
  163.      * {@code hadm[n+1][N]}.<br>
  164.      * <em>(If I use [x][y] it always means [row-offset][column-offset] of a
  165.      * Matrix with n rows and m columns. Its entries go from M[0][0]
  166.      * to M[n][N])</em></li>
  167.      * <li>Place the input vector {@code x[N]} in the first column of the
  168.      * matrix {@code hadm}.</li>
  169.      * <li>The entries of the submatrix {@code D_top} are calculated as follows
  170.      *     <ul>
  171.      *         <li>{@code D_top} goes from entry {@code [0][1]} to
  172.      *         {@code [N / 2 - 1][n + 1]},</li>
  173.      *         <li>the columns of {@code D_top} are the pairwise mutually
  174.      *         exclusive sums of the previous column.</li>
  175.      *     </ul>
  176.      * </li>
  177.      * <li>The entries of the submatrix {@code D_bottom} are calculated as
  178.      * follows
  179.      *     <ul>
  180.      *         <li>{@code D_bottom} goes from entry {@code [N / 2][1]} to
  181.      *         {@code [N][n + 1]},</li>
  182.      *         <li>the columns of {@code D_bottom} are the pairwise differences
  183.      *         of the previous column.</li>
  184.      *     </ul>
  185.      * </li>
  186.      * <li>The consputation of {@code D_top} and {@code D_bottom} are best
  187.      * understood with the above example (for {@code N = 8}).
  188.      * <li>The output vector {@code y} is now in the last column of
  189.      * {@code hadm}.</li>
  190.      * <li><em>Algorithm from <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">chipcenter</a>.</em></li>
  191.      * </ol>
  192.      * <table border="1" >
  193.      * <caption>visually</caption>
  194.      * <tbody>
  195.      * <tr>
  196.      *     <td></td><th>0</th><th>1</th><th>2</th><th>3</th>
  197.      *     <th>&hellip;</th>
  198.      *     <th>n + 1</th>
  199.      * </tr>
  200.      * <tr>
  201.      *     <th>0</th>
  202.      *     <td>x<sub>0</sub></td>
  203.      *     <td colspan="5" rowspan="5" >
  204.      *         &uarr;<br>
  205.      *         &larr; D<sub>top</sub> &rarr;<br>
  206.      *         &darr;
  207.      *     </td>
  208.      * </tr>
  209.      * <tr><th>1</th><td>x<sub>1</sub></td></tr>
  210.      * <tr><th>2</th><td>x<sub>2</sub></td></tr>
  211.      * <tr><th>&hellip;</th><td>&hellip;</td></tr>
  212.      * <tr><th>N / 2 - 1</th><td>x<sub>N/2-1</sub></td></tr>
  213.      * <tr>
  214.      *     <th>N / 2</th>
  215.      *     <td>x<sub>N/2</sub></td>
  216.      *     <td colspan="5" rowspan="5" >
  217.      *         &uarr;<br>
  218.      *         &larr; D<sub>bottom</sub> &rarr;<br>
  219.      *         &darr;
  220.      *     </td>
  221.      * </tr>
  222.      * <tr><th>N / 2 + 1</th><td>x<sub>N/2+1</sub></td></tr>
  223.      * <tr><th>N / 2 + 2</th><td>x<sub>N/2+2</sub></td></tr>
  224.      * <tr><th>&hellip;</th><td>&hellip;</td></tr>
  225.      * <tr><th>N</th><td>x<sub>N</sub></td></tr>
  226.      * </tbody>
  227.      * </table>
  228.      *
  229.      * @param x the real data array to be transformed
  230.      * @return the real transformed array, {@code y}
  231.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  232.      */
  233.     protected double[] fht(double[] x) throws MathIllegalArgumentException {

  234.         final int n     = x.length;
  235.         final int halfN = n / 2;

  236.         if (!ArithmeticUtils.isPowerOfTwo(n)) {
  237.             throw new MathIllegalArgumentException(LocalizedFFTFormats.NOT_POWER_OF_TWO,
  238.                     n);
  239.         }

  240.         /*
  241.          * Instead of creating a matrix with p+1 columns and n rows, we use two
  242.          * one dimension arrays which we are used in an alternating way.
  243.          */
  244.         double[] yPrevious = new double[n];
  245.         double[] yCurrent  = x.clone();

  246.         // iterate from left to right (column)
  247.         for (int j = 1; j < n; j <<= 1) {

  248.             // switch columns
  249.             final double[] yTmp = yCurrent;
  250.             yCurrent  = yPrevious;
  251.             yPrevious = yTmp;

  252.             // iterate from top to bottom (row)
  253.             for (int i = 0; i < halfN; ++i) {
  254.                 // Dtop: the top part works with addition
  255.                 final int twoI = 2 * i;
  256.                 yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1];
  257.             }
  258.             for (int i = halfN; i < n; ++i) {
  259.                 // Dbottom: the bottom part works with subtraction
  260.                 final int twoI = 2 * i;
  261.                 yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1];
  262.             }
  263.         }

  264.         return yCurrent;

  265.     }

  266.     /**
  267.      * Returns the forward transform of the specified integer data set. The FHT
  268.      * (Fast Hadamard Transform) uses only subtraction and addition.
  269.      *
  270.      * @param x the integer data array to be transformed
  271.      * @return the integer transformed array, {@code y}
  272.      * @throws MathIllegalArgumentException if the length of the data array is not a power of two
  273.      */
  274.     protected int[] fht(int[] x) throws MathIllegalArgumentException {

  275.         final int n     = x.length;
  276.         final int halfN = n / 2;

  277.         if (!ArithmeticUtils.isPowerOfTwo(n)) {
  278.             throw new MathIllegalArgumentException(LocalizedFFTFormats.NOT_POWER_OF_TWO,
  279.                     n);
  280.         }

  281.         /*
  282.          * Instead of creating a matrix with p+1 columns and n rows, we use two
  283.          * one dimension arrays which we are used in an alternating way.
  284.          */
  285.         int[] yPrevious = new int[n];
  286.         int[] yCurrent  = x.clone();

  287.         // iterate from left to right (column)
  288.         for (int j = 1; j < n; j <<= 1) {

  289.             // switch columns
  290.             final int[] yTmp = yCurrent;
  291.             yCurrent  = yPrevious;
  292.             yPrevious = yTmp;

  293.             // iterate from top to bottom (row)
  294.             for (int i = 0; i < halfN; ++i) {
  295.                 // Dtop: the top part works with addition
  296.                 final int twoI = 2 * i;
  297.                 yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1];
  298.             }
  299.             for (int i = halfN; i < n; ++i) {
  300.                 // Dbottom: the bottom part works with subtraction
  301.                 final int twoI = 2 * i;
  302.                 yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1];
  303.             }
  304.         }

  305.         // return the last computed output vector y
  306.         return yCurrent;

  307.     }

  308. }