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 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.transform; 23 24 import java.io.Serializable; 25 26 import org.hipparchus.analysis.FunctionUtils; 27 import org.hipparchus.analysis.UnivariateFunction; 28 import org.hipparchus.exception.MathIllegalArgumentException; 29 import org.hipparchus.util.ArithmeticUtils; 30 31 /** 32 * Implements the <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">Fast Hadamard Transform</a> (FHT). 33 * Transformation of an input vector x to the output vector y. 34 * <p> 35 * In addition to transformation of real vectors, the Hadamard transform can 36 * transform integer vectors into integer vectors. However, this integer transform 37 * cannot be inverted directly. Due to a scaling factor it may lead to rational results. 38 * As an example, the inverse transform of integer vector (0, 1, 0, 1) is rational 39 * vector (1/2, -1/2, 0, 0). 40 * 41 */ 42 public class FastHadamardTransformer implements RealTransformer, Serializable { 43 44 /** Serializable version identifier. */ 45 static final long serialVersionUID = 20120211L; 46 47 /** Empty constructor. 48 * <p> 49 * This constructor is not strictly necessary, but it prevents spurious 50 * javadoc warnings with JDK 18 and later. 51 * </p> 52 * @since 3.0 53 */ 54 public FastHadamardTransformer() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy 55 // nothing to do 56 } 57 58 /** 59 * {@inheritDoc} 60 * 61 * @throws MathIllegalArgumentException if the length of the data array is 62 * not a power of two 63 */ 64 @Override 65 public double[] transform(final double[] f, final TransformType type) { 66 if (type == TransformType.FORWARD) { 67 return fht(f); 68 } 69 return TransformUtils.scaleArray(fht(f), 1.0 / f.length); 70 } 71 72 /** 73 * {@inheritDoc} 74 * 75 * @throws org.hipparchus.exception.MathIllegalArgumentException 76 * if the lower bound is greater than, or equal to the upper bound 77 * @throws org.hipparchus.exception.MathIllegalArgumentException 78 * if the number of sample points is negative 79 * @throws MathIllegalArgumentException if the number of sample points is not a power of two 80 */ 81 @Override 82 public double[] transform(final UnivariateFunction f, 83 final double min, final double max, final int n, 84 final TransformType type) { 85 86 return transform(FunctionUtils.sample(f, min, max, n), type); 87 } 88 89 /** 90 * Returns the forward transform of the specified integer data set.The 91 * integer transform cannot be inverted directly, due to a scaling factor 92 * which may lead to double results. 93 * 94 * @param f the integer data array to be transformed (signal) 95 * @return the integer transformed array (spectrum) 96 * @throws MathIllegalArgumentException if the length of the data array is not a power of two 97 */ 98 public int[] transform(final int[] f) { 99 return fht(f); 100 } 101 102 /** 103 * The FHT (Fast Hadamard Transformation) which uses only subtraction and 104 * addition. Requires {@code N * log2(N) = n * 2^n} additions. 105 * 106 * <ol> 107 * <li><b>x</b> is the input vector to be transformed,</li> 108 * <li><b>y</b> is the output vector (Fast Hadamard transform of <b>x</b>),</li> 109 * <li>a and b are helper rows.</li> 110 * </ol> 111 * <table border="1"> 112 * <caption>Short Table of manual calculation for N=8</caption> 113 * <tbody> 114 * <tr> 115 * <th>x</th> 116 * <th>a</th> 117 * <th>b</th> 118 * <th>y</th> 119 * </tr> 120 * <tr> 121 * <th>x<sub>0</sub></th> 122 * <td>a<sub>0</sub> = x<sub>0</sub> + x<sub>1</sub></td> 123 * <td>b<sub>0</sub> = a<sub>0</sub> + a<sub>1</sub></td> 124 * <td>y<sub>0</sub> = b<sub>0</sub >+ b<sub>1</sub></td> 125 * </tr> 126 * <tr> 127 * <th>x<sub>1</sub></th> 128 * <td>a<sub>1</sub> = x<sub>2</sub> + x<sub>3</sub></td> 129 * <td>b<sub>0</sub> = a<sub>2</sub> + a<sub>3</sub></td> 130 * <td>y<sub>0</sub> = b<sub>2</sub> + b<sub>3</sub></td> 131 * </tr> 132 * <tr> 133 * <th>x<sub>2</sub></th> 134 * <td>a<sub>2</sub> = x<sub>4</sub> + x<sub>5</sub></td> 135 * <td>b<sub>0</sub> = a<sub>4</sub> + a<sub>5</sub></td> 136 * <td>y<sub>0</sub> = b<sub>4</sub> + b<sub>5</sub></td> 137 * </tr> 138 * <tr> 139 * <th>x<sub>3</sub></th> 140 * <td>a<sub>3</sub> = x<sub>6</sub> + x<sub>7</sub></td> 141 * <td>b<sub>0</sub> = a<sub>6</sub> + a<sub>7</sub></td> 142 * <td>y<sub>0</sub> = b<sub>6</sub> + b<sub>7</sub></td> 143 * </tr> 144 * <tr> 145 * <th>x<sub>4</sub></th> 146 * <td>a<sub>0</sub> = x<sub>0</sub> - x<sub>1</sub></td> 147 * <td>b<sub>0</sub> = a<sub>0</sub> - a<sub>1</sub></td> 148 * <td>y<sub>0</sub> = b<sub>0</sub> - b<sub>1</sub></td> 149 * </tr> 150 * <tr> 151 * <th>x<sub>5</sub></th> 152 * <td>a<sub>1</sub> = x<sub>2</sub> - x<sub>3</sub></td> 153 * <td>b<sub>0</sub> = a<sub>2</sub> - a<sub>3</sub></td> 154 * <td>y<sub>0</sub> = b<sub>2</sub> - b<sub>3</sub></td> 155 * </tr> 156 * <tr> 157 * <th>x<sub>6</sub></th> 158 * <td>a<sub>2</sub> = x<sub>4</sub> - x<sub>5</sub></td> 159 * <td>b<sub>0</sub> = a<sub>4</sub> - a<sub>5</sub></td> 160 * <td>y<sub>0</sub> = b<sub>4</sub> - b<sub>5</sub></td> 161 * </tr> 162 * <tr> 163 * <th>x<sub>7</sub></th> 164 * <td>a<sub>3</sub> = x<sub>6</sub> - x<sub>7</sub></td> 165 * <td>b<sub>0</sub> = a<sub>6</sub> - a<sub>7</sub></td> 166 * <td>y<sub>0</sub> = b<sub>6</sub> - b<sub>7</sub></td> 167 * </tr> 168 * </tbody> 169 * </table> 170 * 171 * <p>How it works</p> 172 * <ol> 173 * <li>Construct a matrix with {@code N} rows and {@code n + 1} columns, 174 * {@code hadm[n+1][N]}.<br> 175 * <em>(If I use [x][y] it always means [row-offset][column-offset] of a 176 * Matrix with n rows and m columns. Its entries go from M[0][0] 177 * to M[n][N])</em></li> 178 * <li>Place the input vector {@code x[N]} in the first column of the 179 * matrix {@code hadm}.</li> 180 * <li>The entries of the submatrix {@code D_top} are calculated as follows 181 * <ul> 182 * <li>{@code D_top} goes from entry {@code [0][1]} to 183 * {@code [N / 2 - 1][n + 1]},</li> 184 * <li>the columns of {@code D_top} are the pairwise mutually 185 * exclusive sums of the previous column.</li> 186 * </ul> 187 * </li> 188 * <li>The entries of the submatrix {@code D_bottom} are calculated as 189 * follows 190 * <ul> 191 * <li>{@code D_bottom} goes from entry {@code [N / 2][1]} to 192 * {@code [N][n + 1]},</li> 193 * <li>the columns of {@code D_bottom} are the pairwise differences 194 * of the previous column.</li> 195 * </ul> 196 * </li> 197 * <li>The consputation of {@code D_top} and {@code D_bottom} are best 198 * understood with the above example (for {@code N = 8}). 199 * <li>The output vector {@code y} is now in the last column of 200 * {@code hadm}.</li> 201 * <li><em>Algorithm from <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">chipcenter</a>.</em></li> 202 * </ol> 203 * <table border="1" > 204 * <caption>visually</caption> 205 * <tbody> 206 * <tr> 207 * <td></td><th>0</th><th>1</th><th>2</th><th>3</th> 208 * <th>…</th> 209 * <th>n + 1</th> 210 * </tr> 211 * <tr> 212 * <th>0</th> 213 * <td>x<sub>0</sub></td> 214 * <td colspan="5" rowspan="5" > 215 * ↑<br> 216 * ← D<sub>top</sub> →<br> 217 * ↓ 218 * </td> 219 * </tr> 220 * <tr><th>1</th><td>x<sub>1</sub></td></tr> 221 * <tr><th>2</th><td>x<sub>2</sub></td></tr> 222 * <tr><th>…</th><td>…</td></tr> 223 * <tr><th>N / 2 - 1</th><td>x<sub>N/2-1</sub></td></tr> 224 * <tr> 225 * <th>N / 2</th> 226 * <td>x<sub>N/2</sub></td> 227 * <td colspan="5" rowspan="5" > 228 * ↑<br> 229 * ← D<sub>bottom</sub> →<br> 230 * ↓ 231 * </td> 232 * </tr> 233 * <tr><th>N / 2 + 1</th><td>x<sub>N/2+1</sub></td></tr> 234 * <tr><th>N / 2 + 2</th><td>x<sub>N/2+2</sub></td></tr> 235 * <tr><th>…</th><td>…</td></tr> 236 * <tr><th>N</th><td>x<sub>N</sub></td></tr> 237 * </tbody> 238 * </table> 239 * 240 * @param x the real data array to be transformed 241 * @return the real transformed array, {@code y} 242 * @throws MathIllegalArgumentException if the length of the data array is not a power of two 243 */ 244 protected double[] fht(double[] x) throws MathIllegalArgumentException { 245 246 final int n = x.length; 247 final int halfN = n / 2; 248 249 if (!ArithmeticUtils.isPowerOfTwo(n)) { 250 throw new MathIllegalArgumentException(LocalizedFFTFormats.NOT_POWER_OF_TWO, 251 Integer.valueOf(n)); 252 } 253 254 /* 255 * Instead of creating a matrix with p+1 columns and n rows, we use two 256 * one dimension arrays which we are used in an alternating way. 257 */ 258 double[] yPrevious = new double[n]; 259 double[] yCurrent = x.clone(); 260 261 // iterate from left to right (column) 262 for (int j = 1; j < n; j <<= 1) { 263 264 // switch columns 265 final double[] yTmp = yCurrent; 266 yCurrent = yPrevious; 267 yPrevious = yTmp; 268 269 // iterate from top to bottom (row) 270 for (int i = 0; i < halfN; ++i) { 271 // Dtop: the top part works with addition 272 final int twoI = 2 * i; 273 yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1]; 274 } 275 for (int i = halfN; i < n; ++i) { 276 // Dbottom: the bottom part works with subtraction 277 final int twoI = 2 * i; 278 yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1]; 279 } 280 } 281 282 return yCurrent; 283 284 } 285 286 /** 287 * Returns the forward transform of the specified integer data set. The FHT 288 * (Fast Hadamard Transform) uses only subtraction and addition. 289 * 290 * @param x the integer data array to be transformed 291 * @return the integer transformed array, {@code y} 292 * @throws MathIllegalArgumentException if the length of the data array is not a power of two 293 */ 294 protected int[] fht(int[] x) throws MathIllegalArgumentException { 295 296 final int n = x.length; 297 final int halfN = n / 2; 298 299 if (!ArithmeticUtils.isPowerOfTwo(n)) { 300 throw new MathIllegalArgumentException(LocalizedFFTFormats.NOT_POWER_OF_TWO, 301 Integer.valueOf(n)); 302 } 303 304 /* 305 * Instead of creating a matrix with p+1 columns and n rows, we use two 306 * one dimension arrays which we are used in an alternating way. 307 */ 308 int[] yPrevious = new int[n]; 309 int[] yCurrent = x.clone(); 310 311 // iterate from left to right (column) 312 for (int j = 1; j < n; j <<= 1) { 313 314 // switch columns 315 final int[] yTmp = yCurrent; 316 yCurrent = yPrevious; 317 yPrevious = yTmp; 318 319 // iterate from top to bottom (row) 320 for (int i = 0; i < halfN; ++i) { 321 // Dtop: the top part works with addition 322 final int twoI = 2 * i; 323 yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1]; 324 } 325 for (int i = halfN; i < n; ++i) { 326 // Dbottom: the bottom part works with subtraction 327 final int twoI = 2 * i; 328 yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1]; 329 } 330 } 331 332 // return the last computed output vector y 333 return yCurrent; 334 335 } 336 337 }