Engauge Digitizer  2
TestFitting.cpp
1 #include "FittingStatistics.h"
2 #include "Logger.h"
3 #include "MainWindow.h"
4 #include <qmath.h>
5 #include <QPointF>
6 #include <QtTest/QtTest>
7 #include "Test/TestFitting.h"
8 
9 QTEST_MAIN (TestFitting)
10 
11 using namespace std;
12 
13 const int NOMINAL_ORDER = 6;
14 const int NOMINAL_SIGNIFICANT_DIGITS = 7;
15 
16 TestFitting::TestFitting(QObject *parent) :
17  QObject(parent)
18 {
19 }
20 
21 void TestFitting::cleanupTestCase ()
22 {
23 
24 }
25 
26 bool TestFitting::generalFunctionTest (int order,
27  int numPoints) const
28 {
29  int orderReduced = qMin (order, numPoints - 1);
30 
31  const double EPSILON = 0.0001;
32  FittingStatistics fitting;
33  double mse, rms, rSquared;
34  FittingCurveCoefficients coefficientsGot (MAX_POLYNOMIAL_ORDER + 1);
35 
36  // Overfitting or underfitting?
37  bool isOverfitting = (order >= numPoints - 1);
38 
39  // Create the points according to y = 0 + 1 * (x + 1) (x + 2) ... (x + order), with y=0 for order=0
40  FittingPointsConvenient points;
41  for (int iPoint = 0; iPoint < numPoints; iPoint++) {
42  double x = iPoint; // Pick arbitrary x values that are near the zeros
43  double y = 0;
44  if (orderReduced > 0) {
45  y = 1; // Multiply this by successive terms
46  for (int ord = 0; ord < orderReduced; ord++) {
47  y *= (x + ord + 1);
48  }
49  }
50 
51  points.append (QPointF (x, y));
52  }
53 
54  fitting.calculateCurveFitAndStatistics (order,
55  points,
56  coefficientsGot,
57  mse,
58  rms,
59  rSquared,
60  NOMINAL_SIGNIFICANT_DIGITS);
61 
62  bool success = true;
63 
64  // Expected coefficients are hardcoded
65  FittingCurveCoefficients coefficientsExpected (orderReduced + 1);
66  switch (orderReduced)
67  {
68  case 0: // y=0
69  coefficientsExpected [0] = 0;
70  break;
71  case 1: // y=(x+1)
72  coefficientsExpected [0] = 1;
73  coefficientsExpected [1] = 1;
74  break;
75  case 2: // y=(x+1)(x+2)
76  coefficientsExpected [0] = 2;
77  coefficientsExpected [1] = 3;
78  coefficientsExpected [2] = 1;
79  break;
80  case 3: // y=(x+1)(x+2)(x+3)
81  coefficientsExpected [0] = 6;
82  coefficientsExpected [1] = 11;
83  coefficientsExpected [2] = 6;
84  coefficientsExpected [3] = 1;
85  break;
86  case 4: // y=(x+1)(x+2)(x+3)(x+4)
87  coefficientsExpected [0] = 24;
88  coefficientsExpected [1] = 50;
89  coefficientsExpected [2] = 35;
90  coefficientsExpected [3] = 10;
91  coefficientsExpected [4] = 1;
92  break;
93  }
94 
95  for (int coef = 0; coef < order + 1; coef++) {
96  double coefGot = coefficientsGot [coef];
97 
98  double coefExpected = 0;
99  if (coef <= orderReduced) {
100  coefExpected = coefficientsExpected [coef];
101  }
102 
103  success = (success && ((qAbs (coefGot - coefExpected) < EPSILON)));
104  }
105 
106  if (isOverfitting) {
107  // Overfitting case should always have an error of zero
108  success = (success && ((qAbs (mse) < EPSILON)));
109  }
110 
111  return success;
112 }
113 
114 bool TestFitting::generalNonFunctionTest () const
115 {
116  const double EPSILON = 0.0001;
117  FittingStatistics fitting;
118  double mse, rms, rSquared;
119  FittingCurveCoefficients coefficientsGot (MAX_POLYNOMIAL_ORDER);
120 
121  // Create the points according to y = 0 + 1 * (x + 1) (x + 2) ... (x + order), with y=0 for order=0
122  FittingPointsConvenient points;
123  const double Y1 = 1, Y2 = 2;
124  points.append (QPointF (1, Y1));
125  points.append (QPointF (1, Y2));
126 
128  points,
129  coefficientsGot,
130  mse,
131  rms,
132  rSquared,
133  NOMINAL_SIGNIFICANT_DIGITS);
134 
135  bool success = true;
136 
137  // Expected coefficients are hardcoded
138  FittingCurveCoefficients coefficientsExpected (2);
139  coefficientsExpected [0] = (Y1 + Y2) / 2.0;
140  coefficientsExpected [1] = 0;
141 
142  for (int coef = 0; coef < 2; coef++) {
143  double coefGot = coefficientsGot [coef];
144 
145  double coefExpected = coefficientsExpected [coef];
146 
147  success = (success && ((qAbs (coefGot - coefExpected) < EPSILON)));
148  }
149 
150  return success;
151 }
152 
153 void TestFitting::initTestCase ()
154 {
155  const QString NO_ERROR_REPORT_LOG_FILE;
156  const QString NO_REGRESSION_OPEN_FILE;
157  const bool NO_GNUPLOT_LOG_FILES = false;
158  const bool NO_REGRESSION_IMPORT = false;
159  const bool NO_RESET = false;
160  const bool NO_EXPORT_ONLY = false;
161  const bool NO_EXTRACT_IMAGE_ONLY = false;
162  const QString NO_EXTRACT_IMAGE_EXTENSION;
163  const bool DEBUG_FLAG = false;
164  const QStringList NO_LOAD_STARTUP_FILES;
165  const QStringList NO_COMMAND_LINE;
166 
167  initializeLogging ("engauge_test",
168  "engauge_test.log",
169  DEBUG_FLAG);
170 
171  MainWindow w (NO_ERROR_REPORT_LOG_FILE,
172  NO_REGRESSION_OPEN_FILE,
173  NO_REGRESSION_IMPORT,
174  NO_GNUPLOT_LOG_FILES,
175  NO_RESET,
176  NO_EXPORT_ONLY,
177  NO_EXTRACT_IMAGE_ONLY,
178  NO_EXTRACT_IMAGE_EXTENSION,
179  NO_LOAD_STARTUP_FILES,
180  NO_COMMAND_LINE);
181  w.show ();
182 }
183 
184 int TestFitting::orderReducedVersusOrderAndSignificantDigits (int order,
185  int significantDigits) const
186 {
187  FittingPointsConvenient points;
188  FittingCurveCoefficients coefficients (MAX_POLYNOMIAL_ORDER + 1);
189 
190  // Hyperbola points
191  FittingStatistics fittingStatistics;
192  for (double x = 1; x <= 10; x += 1) {
193  double y = 100.0 / x;
194  points.append (QPointF (x, y));
195  }
196 
197  fittingStatistics.calculateCurveFit (order,
198  points,
199  coefficients,
200  significantDigits);
201 
202  // Find first nonzero coefficient. Two cases for 0th order are y<>0 (not all coefficients are zero)
203  // and y=0 (all coefficients are zero). In all other cases the order is the highest nonzero coefficient
204  int orderReduced;
205  for (orderReduced = MAX_POLYNOMIAL_ORDER; orderReduced > 0; orderReduced--) {
206  if (coefficients [orderReduced] != 0) {
207  return orderReduced;
208  }
209  }
210 
211  return orderReduced;
212 }
213 
214 void TestFitting::testFunctionExactFit01 ()
215 {
216  QVERIFY (generalFunctionTest (0, 1));
217 }
218 
219 void TestFitting::testFunctionExactFit12 ()
220 {
221  QVERIFY (generalFunctionTest (1, 2));
222 }
223 
224 void TestFitting::testFunctionExactFit23 ()
225 {
226  QVERIFY (generalFunctionTest (2, 3));
227 }
228 
229 void TestFitting::testFunctionExactFit34 ()
230 {
231  QVERIFY (generalFunctionTest (3, 4));
232 }
233 
234 void TestFitting::testFunctionOverfit11 ()
235 {
236  QVERIFY (generalFunctionTest (1, 1));
237 }
238 
239 void TestFitting::testFunctionOverfit22 ()
240 {
241  QVERIFY (generalFunctionTest (2, 2));
242 }
243 
244 void TestFitting::testFunctionOverfit33 ()
245 {
246  QVERIFY (generalFunctionTest (3, 3));
247 }
248 
249 void TestFitting::testFunctionOverfit44 ()
250 {
251  QVERIFY (generalFunctionTest (4, 4));
252 }
253 
254 void TestFitting::testFunctionUnderfit02 ()
255 {
256  QVERIFY (generalFunctionTest (0, 2));
257 }
258 
259 void TestFitting::testFunctionUnderfit13 ()
260 {
261  QVERIFY (generalFunctionTest (1, 3));
262 }
263 
264 void TestFitting::testFunctionUnderfit24 ()
265 {
266  QVERIFY (generalFunctionTest (2, 4));
267 }
268 
269 void TestFitting::testFunctionUnderfit35 ()
270 {
271  QVERIFY (generalFunctionTest (3, 5));
272 }
273 
274 void TestFitting::testNonFunction ()
275 {
276  QVERIFY (generalNonFunctionTest ());
277 }
278 
279 void TestFitting::testOrderReduced3 ()
280 {
281  QVERIFY (orderReducedVersusOrderAndSignificantDigits (3, NOMINAL_SIGNIFICANT_DIGITS) == 3);
282 }
283 
284 void TestFitting::testOrderReduced4 ()
285 {
286  QVERIFY (orderReducedVersusOrderAndSignificantDigits (4, NOMINAL_SIGNIFICANT_DIGITS) == 4);
287 }
288 
289 void TestFitting::testOrderReduced5 ()
290 {
291  QVERIFY (orderReducedVersusOrderAndSignificantDigits (5, NOMINAL_SIGNIFICANT_DIGITS) == 5);
292 }
293 
294 void TestFitting::testOrderReduced6 ()
295 {
296  QVERIFY (orderReducedVersusOrderAndSignificantDigits (6, NOMINAL_SIGNIFICANT_DIGITS) == 6);
297 }
298 
299 void TestFitting::testSignificantDigits3 ()
300 {
301  QVERIFY (orderReducedVersusOrderAndSignificantDigits (NOMINAL_ORDER, 3) == NOMINAL_ORDER);
302 }
303 
304 void TestFitting::testSignificantDigits4 ()
305 {
306  QVERIFY (orderReducedVersusOrderAndSignificantDigits (NOMINAL_ORDER, 4) == NOMINAL_ORDER);
307 }
308 
309 void TestFitting::testSignificantDigits5 ()
310 {
311  QVERIFY (orderReducedVersusOrderAndSignificantDigits (NOMINAL_ORDER, 5) == NOMINAL_ORDER);
312 }
313 
314 void TestFitting::testSignificantDigits6 ()
315 {
316  QVERIFY (orderReducedVersusOrderAndSignificantDigits (NOMINAL_ORDER, 6) == NOMINAL_ORDER);
317 }
Unit test of Fitting classes.
Definition: TestFitting.h:7
void calculateCurveFitAndStatistics(unsigned int order, const FittingPointsConvenient &pointsConvenient, FittingCurveCoefficients &coefficients, double &mse, double &rms, double &rSquared, int significantDigits)
Compute the curve fit and the statistics for that curve fit.
TestFitting(QObject *parent=0)
Single constructor.
Definition: TestFitting.cpp:16
This class does the math to compute statistics for FittingWindow.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:91