1 /***************************************************************************
2     File                 : PolynomialFit.cpp
3     Project              : SciDAVis
4     --------------------------------------------------------------------
5     Copyright            : (C) 2006 by Ion Vasilief, Tilman Benkert
6     Email (use @ for *)  : ion_vasilief*yahoo.fr, thzs*gmx.net
7     Description          : Polynomial Fit and Linear Fit classes
8 
9  ***************************************************************************/
10 
11 /***************************************************************************
12  *                                                                         *
13  *  This program is free software; you can redistribute it and/or modify   *
14  *  it under the terms of the GNU General Public License as published by   *
15  *  the Free Software Foundation; either version 2 of the License, or      *
16  *  (at your option) any later version.                                    *
17  *                                                                         *
18  *  This program is distributed in the hope that it will be useful,        *
19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
21  *  GNU General Public License for more details.                           *
22  *                                                                         *
23  *   You should have received a copy of the GNU General Public License     *
24  *   along with this program; if not, write to the Free Software           *
25  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
26  *   Boston, MA  02110-1301  USA                                           *
27  *                                                                         *
28  ***************************************************************************/
29 #include "PolynomialFit.h"
30 
31 #include <QMessageBox>
32 #include <QLocale>
33 
34 #include <gsl/gsl_multifit.h>
35 #include <gsl/gsl_fit.h>
36 using namespace std;
37 
PolynomialFit(ApplicationWindow * parent,Graph * g,int order,bool legend)38 PolynomialFit::PolynomialFit(ApplicationWindow *parent, Graph *g, int order, bool legend)
39     : Fit(parent, g), d_order(order), show_legend(legend)
40 {
41     init();
42 }
43 
PolynomialFit(ApplicationWindow * parent,Graph * g,QString & curveTitle,int order,bool legend)44 PolynomialFit::PolynomialFit(ApplicationWindow *parent, Graph *g, QString &curveTitle, int order,
45                              bool legend)
46     : Fit(parent, g), d_order(order), show_legend(legend)
47 {
48     init();
49     setDataFromCurve(curveTitle);
50 }
51 
PolynomialFit(ApplicationWindow * parent,Graph * g,QString & curveTitle,double start,double end,int order,bool legend)52 PolynomialFit::PolynomialFit(ApplicationWindow *parent, Graph *g, QString &curveTitle, double start,
53                              double end, int order, bool legend)
54     : Fit(parent, g), d_order(order), show_legend(legend)
55 {
56     init();
57     setDataFromCurve(curveTitle, start, end);
58 }
59 
init()60 void PolynomialFit::init()
61 {
62     setObjectName(tr("Poly"));
63     is_non_linear = false;
64     d_explanation = tr("Polynomial");
65     d_p = d_order + 1;
66     d_min_points = d_p;
67 
68     covar = gsl_matrix_alloc(d_p, d_p);
69     d_results.resize(d_p);
70 
71     d_formula = generateFormula(d_order);
72     d_param_names = generateParameterList(d_order);
73 
74     for (unsigned i = 0; i < d_p; i++)
75         d_param_explain << "";
76 }
77 
generateFormula(int order)78 QString PolynomialFit::generateFormula(int order)
79 {
80     QString formula;
81     for (int i = 0; i < order + 1; i++) {
82         QString par = "a" + QString::number(i);
83         formula += par;
84         if (i > 0)
85             formula += "*x";
86         if (i > 1)
87             formula += "^" + QString::number(i);
88         if (i != order)
89             formula += "+";
90     }
91     return formula;
92 }
93 
generateParameterList(int order)94 QStringList PolynomialFit::generateParameterList(int order)
95 {
96     QStringList lst;
97     for (int i = 0; i < order + 1; i++)
98         lst << "a" + QString::number(i);
99     return lst;
100 }
101 
calculateFitCurveData(const vector<double> & par,std::vector<double> & X,std::vector<double> & Y)102 bool PolynomialFit::calculateFitCurveData(const vector<double> &par, std::vector<double> &X,
103                                           std::vector<double> &Y)
104 {
105     generateX(X);
106     for (int i = 0; i < d_points; i++) {
107         double yi = 0.0;
108         for (unsigned j = 0; j < d_p; j++)
109             yi += par[j] * pow(X[i], j);
110         Y[i] = yi;
111     }
112     return true;
113 }
114 
fit()115 void PolynomialFit::fit()
116 {
117     if (d_init_err)
118         return;
119 
120     if (unsigned(d_p) > d_n) {
121         QMessageBox::critical(
122                 (ApplicationWindow *)parent(), tr("Fit Error"),
123                 tr("You need at least %1 data points for this fit operation. Operation aborted!")
124                         .arg(d_p));
125         return;
126     }
127 
128     gsl_matrix *X = gsl_matrix_alloc(d_n, d_p);
129     gsl_vector *c = gsl_vector_alloc(d_p);
130 
131     for (unsigned i = 0; i < d_n; i++) {
132         for (unsigned j = 0; j < d_p; j++)
133             gsl_matrix_set(X, i, j, pow(d_x[i], j));
134     }
135 
136     gsl_vector_view y = gsl_vector_view_array(d_y, d_n);
137 
138     gsl_vector *weights = gsl_vector_alloc(d_n);
139     for (unsigned i = 0; i < d_n; i++)
140         gsl_vector_set(weights, i, 1.0 / pow(d_y_errors[i], 2));
141 
142     gsl_multifit_linear_workspace *work = gsl_multifit_linear_alloc(d_n, d_p);
143 
144     if (d_y_error_source == UnknownErrors)
145         gsl_multifit_linear(X, &y.vector, c, covar, &chi_2, work);
146     else
147         gsl_multifit_wlinear(X, weights, &y.vector, c, covar, &chi_2, work);
148 
149     for (unsigned i = 0; i < d_p; i++)
150         d_results[i] = gsl_vector_get(c, i);
151 
152     gsl_multifit_linear_free(work);
153     gsl_matrix_free(X);
154     gsl_vector_free(c);
155     gsl_vector_free(weights);
156 
157     ApplicationWindow *app = (ApplicationWindow *)parent();
158     if (app->writeFitResultsToLog)
159         app->updateLog(logFitInfo(d_results, 0, 0, d_graph->parentPlotName()));
160 
161     if (show_legend || app->pasteFitResultsToPlot)
162         showLegend();
163 
164     generateFitCurve(d_results);
165 }
166 
legendInfo()167 QString PolynomialFit::legendInfo()
168 {
169     ApplicationWindow *app = (ApplicationWindow *)parent();
170     QString legend = "";
171     if (show_legend) {
172         legend = "Y=" + QLocale().toString(d_results[0], 'g', d_prec);
173         for (unsigned j = 1; j < d_p; j++) {
174             double cj = d_results[j];
175             if (cj > 0 && !legend.isEmpty())
176                 legend += "+";
177 
178             QString s;
179             s.asprintf("%.5f", cj);
180             if (s != "1.00000")
181                 legend += QLocale().toString(cj, 'g', d_prec);
182 
183             legend += "X";
184             if (j > 1)
185                 legend += "<sup>" + QString::number(j) + "</sup>";
186         }
187     }
188     if (app->pasteFitResultsToPlot) {
189         if (show_legend) {
190             legend += "\n";
191         }
192         legend += Fit::legendInfo();
193     }
194     return legend;
195 }
196 
197 /*****************************************************************************
198  *
199  * Class LinearFit
200  *
201  *****************************************************************************/
202 
LinearFit(ApplicationWindow * parent,Graph * g)203 LinearFit::LinearFit(ApplicationWindow *parent, Graph *g) : Fit(parent, g)
204 {
205     init();
206 }
207 
LinearFit(ApplicationWindow * parent,Graph * g,const QString & curveTitle)208 LinearFit::LinearFit(ApplicationWindow *parent, Graph *g, const QString &curveTitle)
209     : Fit(parent, g)
210 {
211     init();
212     setDataFromCurve(curveTitle);
213 }
214 
LinearFit(ApplicationWindow * parent,Graph * g,const QString & curveTitle,double start,double end)215 LinearFit::LinearFit(ApplicationWindow *parent, Graph *g, const QString &curveTitle, double start,
216                      double end)
217     : Fit(parent, g)
218 {
219     init();
220     setDataFromCurve(curveTitle, start, end);
221 }
222 
init()223 void LinearFit::init()
224 {
225     d_p = 2;
226     d_min_points = d_p;
227 
228     covar = gsl_matrix_alloc(d_p, d_p);
229     d_results.resize(d_p);
230 
231     is_non_linear = false;
232     d_formula = "A*x+B";
233     d_param_names << "B"
234                   << "A";
235     d_param_explain << tr("(y-intercept)") << tr("(slope)");
236     d_explanation = tr("Linear Regression");
237     setObjectName(tr("Linear"));
238 }
239 
fit()240 void LinearFit::fit()
241 {
242     if (d_init_err)
243         return;
244 
245     if (d_p > d_n) {
246         QMessageBox::critical(
247                 (ApplicationWindow *)parent(), tr("Fit Error"),
248                 tr("You need at least %1 data points for this fit operation. Operation aborted!")
249                         .arg(d_p));
250         return;
251     }
252 
253     double c0, c1, cov00, cov01, cov11;
254 
255     double *weights = new double[d_n];
256     for (unsigned i = 0; i < d_n; i++)
257         weights[i] = 1.0 / pow(d_y_errors[i], 2);
258 
259     if (d_y_error_source == UnknownErrors)
260         gsl_fit_linear(d_x, 1, d_y, 1, d_n, &c0, &c1, &cov00, &cov01, &cov11, &chi_2);
261     else
262         gsl_fit_wlinear(d_x, 1, weights, 1, d_y, 1, d_n, &c0, &c1, &cov00, &cov01, &cov11, &chi_2);
263 
264     delete[] weights;
265 
266     d_results[0] = c0;
267     d_results[1] = c1;
268 
269     gsl_matrix_set(covar, 0, 0, cov00);
270     gsl_matrix_set(covar, 0, 1, cov01);
271     gsl_matrix_set(covar, 1, 1, cov11);
272     gsl_matrix_set(covar, 1, 0, cov01);
273 
274     ApplicationWindow *app = (ApplicationWindow *)parent();
275     if (app->writeFitResultsToLog)
276         app->updateLog(logFitInfo(d_results, 0, 0, d_graph->parentPlotName()));
277 
278     generateFitCurve(d_results);
279 }
280 
calculateFitCurveData(const vector<double> & par,std::vector<double> & X,std::vector<double> & Y)281 bool LinearFit::calculateFitCurveData(const vector<double> &par, std::vector<double> &X,
282                                       std::vector<double> &Y)
283 {
284     generateX(X);
285     for (int i = 0; i < d_points; i++)
286         Y[i] = par[0] + par[1] * X[i];
287     return true;
288 }
289