From 7e2fad01b3c5c44b489fa6de5004625c4f04970e Mon Sep 17 00:00:00 2001 From: Luc Maisonobe Date: Mon, 29 Dec 2008 09:56:18 +0000 Subject: [PATCH] fixed a forgotten scaling factor in inverse Hadamard transform added integer Hadamard transform note that the integer transform inverse is not always an integer transform git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@729849 13f79535-47bb-0310-9956-ffa450edef68 --- .../transform/FastHadamardTransformer.java | 84 +++++++++++++++++-- .../FastHadamardTransformerTest.java | 73 +++++++++++++--- 2 files changed, 140 insertions(+), 17 deletions(-) diff --git a/src/java/org/apache/commons/math/transform/FastHadamardTransformer.java b/src/java/org/apache/commons/math/transform/FastHadamardTransformer.java index e06120b55..ca398585b 100644 --- a/src/java/org/apache/commons/math/transform/FastHadamardTransformer.java +++ b/src/java/org/apache/commons/math/transform/FastHadamardTransformer.java @@ -23,16 +23,22 @@ import org.apache.commons.math.analysis.UnivariateRealFunction; /** * Implements the Fast Hadamard Transform (FHT). * Transformation of an input vector x to the output vector y. + *

In addition to transformation of real vectors, the Hadamard transform can + * transform integer vectors into integer vectors. However, this integer transform + * cannot be inverted directly. Due to a scaling factor it may lead to rational results. + * As an example, the inverse transform of integer vector (0, 1, 0, 1) is rational + * vector (1/2, -1/2, 0, 0).

* @version $Revision$ $Date$ * @since 2.0 */ public class FastHadamardTransformer implements RealTransformer { /** Serializable version identifier. */ - private static final long serialVersionUID = -710169279109099264L; + private static final long serialVersionUID = -720498949613305350L; /** {@inheritDoc} */ - public double[] transform(double f[]) throws IllegalArgumentException { + public double[] transform(double f[]) + throws IllegalArgumentException { return fht(f); } @@ -46,14 +52,29 @@ public class FastHadamardTransformer implements RealTransformer { /** {@inheritDoc} */ public double[] inversetransform(double f[]) throws IllegalArgumentException { - return fht(f); - } + return FastFourierTransformer.scaleArray(fht(f), 1.0 / f.length); + } /** {@inheritDoc} */ public double[] inversetransform(UnivariateRealFunction f, double min, double max, int n) throws FunctionEvaluationException, IllegalArgumentException { - return fht(FastFourierTransformer.sample(f, min, max, n)); + final double[] unscaled = + fht(FastFourierTransformer.sample(f, min, max, n)); + return FastFourierTransformer.scaleArray(unscaled, 1.0 / n); + } + + /** + * Transform the given real data set. + *

The integer transform cannot be inverted directly, due to a scaling + * factor it may lead to double results.

+ * @param f the integer data array to be transformed (signal) + * @return the integer transformed array (spectrum) + * @throws IllegalArgumentException if any parameters are invalid + */ + public int[] transform(int f[]) + throws IllegalArgumentException { + return fht(f); } /** @@ -131,7 +152,7 @@ public class FastHadamardTransformer implements RealTransformer { * * @param x input vector * @return y output vector - * @throws IllegalArgumentException + * @exception IllegalArgumentException if input array is not a poer of 2 */ protected double[] fht(double x[]) throws IllegalArgumentException { @@ -177,4 +198,55 @@ public class FastHadamardTransformer implements RealTransformer { return yCurrent; } + /** + * The FHT (Fast Hadamard Transformation) which uses only subtraction and addition. + * @param x input vector + * @return y output vector + * @exception IllegalArgumentException if input array is not a poer of 2 + */ + protected int[] fht(int x[]) throws IllegalArgumentException { + + // n is the row count of the input vector x + final int n = x.length; + final int halfN = n / 2; + + // n has to be of the form n = 2^p !! + if (!FastFourierTransformer.isPowerOf2(n)) { + throw MathRuntimeException.createIllegalArgumentException("{0} is not a power of 2", + new Object[] { n }); + } + + // Instead of creating a matrix with p+1 columns and n rows + // we will use two single dimension arrays which we will use in an alternating way. + int[] yPrevious = new int[n]; + int[] yCurrent = x.clone(); + + // iterate from left to right (column) + for (int j = 1; j < n; j <<= 1) { + + // switch columns + final int[] yTmp = yCurrent; + yCurrent = yPrevious; + yPrevious = yTmp; + + // iterate from top to bottom (row) + for (int i = 0; i < halfN; ++i) { + // Dtop + // The top part works with addition + final int twoI = 2 * i; + yCurrent[i] = yPrevious[twoI] + yPrevious[twoI + 1]; + } + for (int i = halfN; i < n; ++i) { + // Dbottom + // The bottom part works with subtraction + final int twoI = 2 * i; + yCurrent[i] = yPrevious[twoI - n] - yPrevious[twoI - n + 1]; + } + } + + // return the last computed output vector y + return yCurrent; + + } + } diff --git a/src/test/org/apache/commons/math/transform/FastHadamardTransformerTest.java b/src/test/org/apache/commons/math/transform/FastHadamardTransformerTest.java index 6929ee9ec..bfc355064 100644 --- a/src/test/org/apache/commons/math/transform/FastHadamardTransformerTest.java +++ b/src/test/org/apache/commons/math/transform/FastHadamardTransformerTest.java @@ -28,16 +28,28 @@ public final class FastHadamardTransformerTest extends TestCase { * Test of transformer for the a 8-point FHT (means n=8) */ public void test8Points() { - checkTransform(new double[] { 1.0, 4.0, -2.0, 3.0, 0.0, 1.0, 4.0, -1.0 }, - new double[] { 10.0, -4.0, 2.0, -4.0, 2.0, -12.0, 6.0, 8.0 }); + checkAllTransforms(new int[] { 1, 4, -2, 3, 0, 1, 4, -1 }, + new int[] { 10, -4, 2, -4, 2, -12, 6, 8 }); } /** * Test of transformer for the a 4-points FHT (means n=4) */ public void test4Points() { - checkTransform(new double[] { 1.0, 2.0, 3.0, 4.0 }, - new double[] { 10.0, -2.0, -4.0, 0.0 }); + checkAllTransforms(new int[] { 1, 2, 3, 4 }, + new int[] { 10, -2, -4, 0 }); + } + + /** + * Test the inverse transform of an integer vector is not always an integer vector + */ + public void testNoIntInverse() { + FastHadamardTransformer transformer = new FastHadamardTransformer(); + double[] x = transformer.inversetransform(new double[] { 0, 1, 0, 1}); + assertEquals( 0.5, x[0], 0); + assertEquals(-0.5, x[1], 0); + assertEquals( 0.0, x[2], 0); + assertEquals( 0.0, x[3], 0); } /** @@ -52,17 +64,56 @@ public final class FastHadamardTransformerTest extends TestCase { } } - private void checkTransform(double[]x, double[] y) { + private void checkAllTransforms(int[]x, int[] y) { + checkDoubleTransform(x, y); + checkInverseDoubleTransform(x, y); + checkIntTransform(x, y); + } + + private void checkDoubleTransform(int[]x, int[] y) { // Initiate the transformer FastHadamardTransformer transformer = new FastHadamardTransformer(); - // transform input vector x to output vector - double result[] = transformer.transform(x); - - for (int i=0;i