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
This commit is contained in:
Luc Maisonobe 2008-12-29 09:56:18 +00:00
parent ed35ae3dee
commit 7e2fad01b3
2 changed files with 140 additions and 17 deletions

View File

@ -23,16 +23,22 @@ import org.apache.commons.math.analysis.UnivariateRealFunction;
/**
* Implements the <a href="http://www.archive.chipcenter.com/dsp/DSP000517F1.html">Fast Hadamard Transform</a> (FHT).
* Transformation of an input vector x to the output vector y.
* <p>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).</p>
* @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.
* <p>The integer transform cannot be inverted directly, due to a scaling
* factor it may lead to double results.</p>
* @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) {
// D<sub>top</sub>
// 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) {
// D<sub>bottom</sub>
// 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;
}
}

View File

@ -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<result.length;i++) {
// compare computed results to precomputed results
assertEquals(y[i], result[i]);
// check double transform
double[] dX = new double[x.length];
for (int i = 0; i < dX.length; ++i) {
dX[i] = (double) x[i];
}
double dResult[] = transformer.transform(dX);
for (int i = 0; i < dResult.length; i++) {
// compare computed results to precomputed results
assertEquals((double) y[i], dResult[i]);
}
}
private void checkIntTransform(int[]x, int[] y) {
// Initiate the transformer
FastHadamardTransformer transformer = new FastHadamardTransformer();
// check integer transform
int iResult[] = transformer.transform(x);
for (int i = 0; i < iResult.length; i++) {
// compare computed results to precomputed results
assertEquals(y[i], iResult[i]);
}
}
private void checkInverseDoubleTransform(int[]x, int[] y) {
// Initiate the transformer
FastHadamardTransformer transformer = new FastHadamardTransformer();
// check double transform
double[] dY = new double[y.length];
for (int i = 0; i < dY.length; ++i) {
dY[i] = (double) y[i];
}
double dResult[] = transformer.inversetransform(dY);
for (int i = 0; i < dResult.length; i++) {
// compare computed results to precomputed results
assertEquals((double) x[i], dResult[i]);
}
}
}