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:
parent
ed35ae3dee
commit
7e2fad01b3
|
@ -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).
|
* 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.
|
* 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$
|
* @version $Revision$ $Date$
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public class FastHadamardTransformer implements RealTransformer {
|
public class FastHadamardTransformer implements RealTransformer {
|
||||||
|
|
||||||
/** Serializable version identifier. */
|
/** Serializable version identifier. */
|
||||||
private static final long serialVersionUID = -710169279109099264L;
|
private static final long serialVersionUID = -720498949613305350L;
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
public double[] transform(double f[]) throws IllegalArgumentException {
|
public double[] transform(double f[])
|
||||||
|
throws IllegalArgumentException {
|
||||||
return fht(f);
|
return fht(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,14 +52,29 @@ public class FastHadamardTransformer implements RealTransformer {
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
public double[] inversetransform(double f[])
|
public double[] inversetransform(double f[])
|
||||||
throws IllegalArgumentException {
|
throws IllegalArgumentException {
|
||||||
return fht(f);
|
return FastFourierTransformer.scaleArray(fht(f), 1.0 / f.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
public double[] inversetransform(UnivariateRealFunction f,
|
public double[] inversetransform(UnivariateRealFunction f,
|
||||||
double min, double max, int n)
|
double min, double max, int n)
|
||||||
throws FunctionEvaluationException, IllegalArgumentException {
|
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
|
* @param x input vector
|
||||||
* @return y output 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 {
|
protected double[] fht(double x[]) throws IllegalArgumentException {
|
||||||
|
|
||||||
|
@ -177,4 +198,55 @@ public class FastHadamardTransformer implements RealTransformer {
|
||||||
return yCurrent;
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,16 +28,28 @@ public final class FastHadamardTransformerTest extends TestCase {
|
||||||
* Test of transformer for the a 8-point FHT (means n=8)
|
* Test of transformer for the a 8-point FHT (means n=8)
|
||||||
*/
|
*/
|
||||||
public void test8Points() {
|
public void test8Points() {
|
||||||
checkTransform(new double[] { 1.0, 4.0, -2.0, 3.0, 0.0, 1.0, 4.0, -1.0 },
|
checkAllTransforms(new int[] { 1, 4, -2, 3, 0, 1, 4, -1 },
|
||||||
new double[] { 10.0, -4.0, 2.0, -4.0, 2.0, -12.0, 6.0, 8.0 });
|
new int[] { 10, -4, 2, -4, 2, -12, 6, 8 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test of transformer for the a 4-points FHT (means n=4)
|
* Test of transformer for the a 4-points FHT (means n=4)
|
||||||
*/
|
*/
|
||||||
public void test4Points() {
|
public void test4Points() {
|
||||||
checkTransform(new double[] { 1.0, 2.0, 3.0, 4.0 },
|
checkAllTransforms(new int[] { 1, 2, 3, 4 },
|
||||||
new double[] { 10.0, -2.0, -4.0, 0.0 });
|
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
|
// Initiate the transformer
|
||||||
FastHadamardTransformer transformer = new FastHadamardTransformer();
|
FastHadamardTransformer transformer = new FastHadamardTransformer();
|
||||||
|
|
||||||
// transform input vector x to output vector
|
// check double transform
|
||||||
double result[] = transformer.transform(x);
|
double[] dX = new double[x.length];
|
||||||
|
for (int i = 0; i < dX.length; ++i) {
|
||||||
for (int i=0;i<result.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
|
// compare computed results to precomputed results
|
||||||
assertEquals(y[i], result[i]);
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue