View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2026.
6   //
7   // This file is part of Force Field X.
8   //
9   // Force Field X is free software; you can redistribute it and/or modify it
10  // under the terms of the GNU General Public License version 3 as published by
11  // the Free Software Foundation.
12  //
13  // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16  // details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA 02111-1307 USA
21  //
22  // Linking this library statically or dynamically with other modules is making a
23  // combined work based on this library. Thus, the terms and conditions of the
24  // GNU General Public License cover the whole combination.
25  //
26  // As a special exception, the copyright holders of this library give you
27  // permission to link this library with independent modules to produce an
28  // executable, regardless of the license terms of these independent modules, and
29  // to copy and distribute the resulting executable under terms of your choice,
30  // provided that you also meet, for each linked independent module, the terms
31  // and conditions of the license of that module. An independent module is a
32  // module which is not derived from or based on this library. If you modify this
33  // library, you may extend this exception to your version of the library, but
34  // you are not obligated to do so. If you do not wish to do so, delete this
35  // exception statement from your version.
36  //
37  // ******************************************************************************
38  package ffx.numerics.fft;
39  
40  import ffx.utilities.FFXTest;
41  import org.junit.Test;
42  import org.junit.runner.RunWith;
43  import org.junit.runners.Parameterized;
44  import org.junit.runners.Parameterized.Parameters;
45  
46  import java.util.Arrays;
47  import java.util.Collection;
48  import java.util.Random;
49  
50  import static org.junit.Assert.assertEquals;
51  
52  /**
53   * Compare the forward Complex1D FFT against the Complex FFT.
54   *
55   * @author Michael J. Schnieders
56   */
57  @RunWith(Parameterized.class)
58  public class Complex1DTest extends FFXTest {
59  
60    private final String info;
61    private final int n;
62  
63    public Complex1DTest(String info, int n) {
64      this.info = info;
65      this.n = n;
66    }
67  
68    @Parameters
69    public static Collection<Object[]> data() {
70      return Arrays.asList(
71          new Object[][]{
72              {"Test n = 6", 6},
73              {"Test n = 8", 8},
74              {"Test n = 10", 10},
75              {"Test n = 12", 12},
76              {"Test n = 15", 15},
77              {"Test n = 18", 18},
78              {"Test n = 20", 20},
79              {"Test n = 21", 21},
80              {"Test n = 27", 27},
81              {"Test n = 30", 30},
82              {"Test n = 32", 32},
83              {"Test n = 38", 38},
84              {"Test n = 45", 45}
85          });
86    }
87  
88    @Test
89    public void testForwardFFTMatchesComplex() {
90      double tolerance = 1.0e-11;
91      double[] expected = createRandomComplexData();
92      double[] actual = expected.clone();
93  
94      Complex complex = new Complex(n);
95      Complex1D complex1D = new Complex1D(n);
96      complex.fft(expected, 0, 2);
97      complex1D.fft(actual, 0, 2);
98  
99      for (int i = 0; i < 2 * n; i++) {
100       assertEquals(info + " at position: " + i, expected[i], actual[i], tolerance);
101     }
102   }
103 
104   @Test
105   public void testInverseFFTMatchesComplex() {
106     double tolerance = 1.0e-11;
107     double[] expected = createRandomComplexData();
108     double[] actual = expected.clone();
109 
110     Complex complex = new Complex(n);
111     Complex1D complex1D = new Complex1D(n);
112     complex.ifft(expected, 0, 2);
113     complex1D.ifft(actual, 0, 2);
114 
115     for (int i = 0; i < 2 * n; i++) {
116       assertEquals(info + " inverse at position: " + i, expected[i], actual[i], tolerance);
117     }
118   }
119 
120   @Test
121   public void testForwardInverseRoundTrip() {
122     double tolerance = 1.0e-11;
123     double[] original = createRandomComplexData();
124     double[] actual = original.clone();
125 
126     Complex1D complex1D = new Complex1D(n);
127     complex1D.fft(actual, 0, 2);
128     complex1D.ifft(actual, 0, 2);
129 
130     for (int i = 0; i < 2 * n; i++) {
131       assertEquals(info + " round-trip at position: " + i, original[i], actual[i] / n, tolerance);
132     }
133   }
134 
135   @Test
136   public void testBlockedForwardFFTMatchesComplex() {
137     if (!Complex.preferredDimension(n)) {
138       return;
139     }
140     double tolerance = 1.0e-11;
141     double[] expected = createRandomBlockedComplexData();
142     double[] actual = expected.clone();
143 
144     Complex complex = new Complex(n, DataLayout1D.BLOCKED, n);
145     Complex1D complex1D = new Complex1D(n, DataLayout1D.BLOCKED, n);
146     complex.fft(expected, 0, 1);
147     complex1D.fft(actual, 0, 1);
148 
149     for (int i = 0; i < 2 * n; i++) {
150       assertEquals(info + " blocked at position: " + i, expected[i], actual[i], tolerance);
151     }
152   }
153 
154   @Test
155   public void testBlockedInverseFFTMatchesComplex() {
156     if (!Complex.preferredDimension(n)) {
157       return;
158     }
159     double tolerance = 1.0e-11;
160     double[] expected = createRandomBlockedComplexData();
161     double[] actual = expected.clone();
162 
163     Complex complex = new Complex(n, DataLayout1D.BLOCKED, n);
164     Complex1D complex1D = new Complex1D(n, DataLayout1D.BLOCKED, n);
165     complex.ifft(expected, 0, 1);
166     complex1D.ifft(actual, 0, 1);
167 
168     for (int i = 0; i < 2 * n; i++) {
169       assertEquals(info + " blocked inverse at position: " + i, expected[i], actual[i], tolerance);
170     }
171   }
172 
173   @Test
174   public void testBlockedForwardInverseRoundTrip() {
175     if (!Complex.preferredDimension(n)) {
176       return;
177     }
178     double tolerance = 1.0e-11;
179     double[] original = createRandomBlockedComplexData();
180     double[] actual = original.clone();
181 
182     Complex1D complex1D = new Complex1D(n, DataLayout1D.BLOCKED, n);
183     complex1D.fft(actual, 0, 1);
184     complex1D.ifft(actual, 0, 1);
185 
186     for (int i = 0; i < 2 * n; i++) {
187       assertEquals(info + " blocked round-trip at position: " + i, original[i], actual[i] / n, tolerance);
188     }
189   }
190 
191   private double[] createRandomComplexData() {
192     double[] data = new double[2 * n];
193     Random random = new Random(1);
194     for (int i = 0; i < n; i++) {
195       int index = 2 * i;
196       data[index] = random.nextDouble();
197       data[index + 1] = random.nextDouble();
198     }
199     return data;
200   }
201 
202   private double[] createRandomBlockedComplexData() {
203     double[] data = new double[2 * n];
204     Random random = new Random(1);
205     for (int i = 0; i < n; i++) {
206       data[i] = random.nextDouble();
207       data[i + n] = random.nextDouble();
208     }
209     return data;
210   }
211 }