File indexing completed on 2024-12-22 04:18:11

0001 /***************************************************************************
0002  *                                                                         *
0003  *   copyright : (C) 2007 The University of Toronto                        *
0004  *                   netterfield@astro.utoronto.ca                         *
0005  *   copyright : (C) 2005  University of British Columbia                  *
0006  *                   dscott@phas.ubc.ca                                    *
0007  *                                                                         *
0008  *   This program is free software; you can redistribute it and/or modify  *
0009  *   it under the terms of the GNU General Public License as published by  *
0010  *   the Free Software Foundation; either version 2 of the License, or     *
0011  *   (at your option) any later version.                                   *
0012  *                                                                         *
0013  ***************************************************************************/
0014 
0015 
0016 #include "fitexponential_unweighted.h"
0017 #include "objectstore.h"
0018 #include "ui_fitexponential_unweightedconfig.h"
0019 
0020 #define NUM_PARAMS 4
0021 #define MAX_NUM_ITERATIONS 500
0022 
0023 #include <gsl/gsl_fit.h>
0024 #include "../non_linear.h"
0025 
0026 static const QString& VECTOR_IN_X = "X Vector";
0027 static const QString& VECTOR_IN_Y = "Y Vector";
0028 static const QString& VECTOR_OUT_Y_FITTED = "Fit";
0029 static const QString& VECTOR_OUT_Y_RESIDUALS = "Residuals";
0030 static const QString& VECTOR_OUT_Y_PARAMETERS = "Parameters Vector";
0031 static const QString& VECTOR_OUT_Y_COVARIANCE = "Covariance";
0032 static const QString& SCALAR_OUT = "chi^2/nu";
0033 
0034 class ConfigWidgetFitExponentialUnweightedPlugin : public Kst::DataObjectConfigWidget, public Ui_FitExponential_UnweightedConfig {
0035   public:
0036     ConfigWidgetFitExponentialUnweightedPlugin(QSettings* cfg) : DataObjectConfigWidget(cfg), Ui_FitExponential_UnweightedConfig() {
0037       _store = 0;
0038       setupUi(this);
0039     }
0040 
0041     ~ConfigWidgetFitExponentialUnweightedPlugin() {}
0042 
0043     void setObjectStore(Kst::ObjectStore* store) { 
0044       _store = store; 
0045       _vectorX->setObjectStore(store);
0046       _vectorY->setObjectStore(store);
0047     }
0048 
0049     void setupSlots(QWidget* dialog) {
0050       if (dialog) {
0051         connect(_vectorX, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified()));
0052         connect(_vectorY, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified()));
0053       }
0054     }
0055 
0056 
0057     void setVectorX(Kst::VectorPtr vector) {
0058       setSelectedVectorX(vector);
0059     }
0060 
0061     void setVectorY(Kst::VectorPtr vector) {
0062       setSelectedVectorY(vector);
0063     }
0064 
0065     void setVectorsLocked(bool locked = true) {
0066       _vectorX->setEnabled(!locked);
0067       _vectorY->setEnabled(!locked);
0068     }
0069 
0070     Kst::VectorPtr selectedVectorX() { return _vectorX->selectedVector(); };
0071     void setSelectedVectorX(Kst::VectorPtr vector) { return _vectorX->setSelectedVector(vector); };
0072 
0073     Kst::VectorPtr selectedVectorY() { return _vectorY->selectedVector(); };
0074     void setSelectedVectorY(Kst::VectorPtr vector) { return _vectorY->setSelectedVector(vector); };
0075 
0076     virtual void setupFromObject(Kst::Object* dataObject) {
0077       if (FitExponentialUnweightedSource* source = static_cast<FitExponentialUnweightedSource*>(dataObject)) {
0078         setSelectedVectorX(source->vectorX());
0079         setSelectedVectorY(source->vectorY());
0080       }
0081     }
0082 
0083     virtual bool configurePropertiesFromXml(Kst::ObjectStore *store, QXmlStreamAttributes& attrs) {
0084       Q_UNUSED(store);
0085       Q_UNUSED(attrs);
0086 
0087       bool validTag = true;
0088 
0089 //       QStringRef av;
0090 //       av = attrs.value("value");
0091 //       if (!av.isNull()) {
0092 //         _configValue = QVariant(av.toString()).toBool();
0093 //       }
0094 
0095       return validTag;
0096     }
0097 
0098   public slots:
0099     virtual void save() {
0100       if (_cfg) {
0101         _cfg->beginGroup("Fit Exponential Plugin");
0102         _cfg->setValue("Input Vector X", _vectorX->selectedVector()->Name());
0103         _cfg->setValue("Input Vector Y", _vectorY->selectedVector()->Name());
0104         _cfg->endGroup();
0105       }
0106     }
0107 
0108     virtual void load() {
0109       if (_cfg && _store) {
0110         _cfg->beginGroup("Fit Exponential Plugin");
0111         QString vectorName = _cfg->value("Input Vector X").toString();
0112         Kst::Object* object = _store->retrieveObject(vectorName);
0113         Kst::Vector* vectorx = static_cast<Kst::Vector*>(object);
0114         if (vectorx) {
0115           setSelectedVectorX(vectorx);
0116         }
0117         vectorName = _cfg->value("Input Vector Y").toString();
0118         object = _store->retrieveObject(vectorName);
0119         Kst::Vector* vectory = static_cast<Kst::Vector*>(object);
0120         if (vectory) {
0121           setSelectedVectorX(vectory);
0122         }
0123         _cfg->endGroup();
0124       }
0125     }
0126 
0127   private:
0128     Kst::ObjectStore *_store;
0129 
0130 };
0131 
0132 
0133 FitExponentialUnweightedSource::FitExponentialUnweightedSource(Kst::ObjectStore *store)
0134 : Kst::BasicPlugin(store) {
0135 }
0136 
0137 
0138 FitExponentialUnweightedSource::~FitExponentialUnweightedSource() {
0139 }
0140 
0141 
0142 QString FitExponentialUnweightedSource::_automaticDescriptiveName() const {
0143   return tr("%1 Unweighted Exponential").arg(vectorY()->descriptiveName());
0144 }
0145 
0146 
0147 void FitExponentialUnweightedSource::change(Kst::DataObjectConfigWidget *configWidget) {
0148   if (ConfigWidgetFitExponentialUnweightedPlugin* config = static_cast<ConfigWidgetFitExponentialUnweightedPlugin*>(configWidget)) {
0149     setInputVector(VECTOR_IN_X, config->selectedVectorX());
0150     setInputVector(VECTOR_IN_Y, config->selectedVectorY());
0151   }
0152 }
0153 
0154 
0155 void FitExponentialUnweightedSource::setupOutputs() {
0156   setOutputVector(VECTOR_OUT_Y_FITTED, "");
0157   setOutputVector(VECTOR_OUT_Y_RESIDUALS, "");
0158   setOutputVector(VECTOR_OUT_Y_PARAMETERS, "");
0159   setOutputVector(VECTOR_OUT_Y_COVARIANCE, "");
0160   setOutputScalar(SCALAR_OUT, "");
0161 }
0162 
0163 
0164 double _X0 = 0; // this use of a global is a hack to inject the first sample into the function.
0165 
0166 void swapDouble(double *A, double *B) {
0167   double C;
0168 
0169   C = *A;
0170   *A = *B;
0171   *B = C;
0172 }
0173 
0174 void function_initial_estimate( const double x[], const double y[], int npts, double P0[] ) {
0175   Q_UNUSED( x )
0176   Q_UNUSED( y )
0177   Q_UNUSED( npts )
0178 
0179   _X0 = x[0];
0180 
0181   // determine the signs of the terms.
0182   // get the average of the first 5%, last 5% and middle 5% of points
0183   int n_ave = npts/20;
0184   if (n_ave < 1) n_ave = 1;
0185   if (n_ave > 100) n_ave = 100;
0186 
0187   double y0 = 0;
0188   double x0 = 0;
0189   double x1 = 0;
0190   double y1 = 0;
0191   double x2 = 0;
0192   double y2 = 0;
0193   int d1 = npts/2 - n_ave/2;
0194   int d2 = npts-n_ave;
0195 
0196   if ((d1 + n_ave > npts) || (d2 + n_ave > npts)) { // bail if not enough points.
0197     P0[0] =  1.0;
0198     P0[1] =  0.0;
0199     P0[2] =  0.0;
0200     return;
0201   }
0202 
0203   for (int i=0; i<n_ave; i++) {
0204     x0+=x[i];
0205     y0+=y[i];
0206     x1+=x[i+d1];
0207     y1+=y[i+d1];
0208     x2+=x[i+d2];
0209     y2+=y[i+d2];
0210   }
0211   x0 /= (double)n_ave;
0212   y0 /= (double)n_ave;
0213   x1 /= (double)n_ave;
0214   y1 /= (double)n_ave;
0215   x2 /= (double)n_ave;
0216   y2 /= (double)n_ave;
0217 
0218   // Make sure x0, x1, x2 are monotonic.
0219   if (x2 > x0) {
0220     if (x1 > x2) {
0221       swapDouble(&x1, &x2);
0222       swapDouble(&y1, &y2);
0223     }
0224     if (x1 < x0) {
0225       swapDouble(&x1, &x0);
0226       swapDouble(&y1, &y0);
0227     }
0228   } else {
0229     if (x1 < x2) {
0230       swapDouble(&x1, &x2);
0231       swapDouble(&y1, &y2);
0232     }
0233     if (x1 > x0) {
0234       swapDouble(&x1, &x0);
0235       swapDouble(&y1, &y0);
0236     }
0237   }
0238 
0239   if ((x1 == x0) || (x2 == x0) || (x1 == x2)) { // bail if no x range
0240     P0[0] =  1.0;
0241     P0[1] =  0.0;
0242     P0[2] =  0.0;
0243     return;
0244   }
0245 
0246   P0[0] =  fabs(y2 - y0)/M_E;
0247   P0[1] =  M_E/fabs(x2 - x0);
0248   P0[2] =  y0;
0249 
0250   double m = (y2 - y0)/(x2 - x0);
0251   if (m > 0) { // rising
0252     if ((x1-x0)*m + y0 > y1) { // neg curvature +A, +B
0253       //P0[0] *= -1;
0254       //P0[1] *= -1.0;
0255     } else {   // -A, -B
0256       P0[0] *= -1;
0257       P0[1] *= -1.0;
0258     }
0259   } else { // falling
0260     if ((x1-x0)*m + y0 > y1) { // Curving down +A, -B
0261       //P0[0] *= -1;
0262       P0[1] *= -1.0;
0263     } else { // -A, +B
0264       P0[0] *= -1;
0265       //P0[1] *= -1.0;
0266     }
0267   }
0268 
0269   fflush(stdout);
0270 }
0271 
0272 // It would be nice to fit exp(B*(x-_X0)) + C, and not have A,
0273 // but A is required to at least hold the sign.  We we just fix
0274 // _X0 to the first X value in the data set, and fit for A.
0275 double function_calculate( double x, double* P ) {
0276   double A = P[0];
0277   double B = P[1];
0278   double C = P[2];
0279   double y;
0280 
0281   y  = ( A*exp( B*(x - _X0) ) ) + C;
0282 
0283   return y;
0284 }
0285 
0286 
0287 void function_derivative( double x, double* P, double* dFdP ) {
0288   double A  = P[0];
0289   double B = P[1];
0290   double exp_BxXo;
0291 
0292   // dFdA = exp(b*(x-_X0)
0293   // dFdB = A*(x-_X0)*exp(b*(x-_X0))
0294 
0295   exp_BxXo = exp( B * (x-_X0) );
0296 
0297   dFdP[0]   = exp_BxXo;
0298   dFdP[1]   = (x-_X0) * A * exp_BxXo;
0299   dFdP[2]   = 1.0;
0300 
0301 }
0302 
0303 
0304 bool FitExponentialUnweightedSource::algorithm() {
0305   Kst::VectorPtr inputVectorX = _inputVectors[VECTOR_IN_X];
0306   Kst::VectorPtr inputVectorY = _inputVectors[VECTOR_IN_Y];
0307 
0308   Kst::VectorPtr outputVectorYFitted = _outputVectors[VECTOR_OUT_Y_FITTED];
0309   Kst::VectorPtr outputVectorYResiduals = _outputVectors[VECTOR_OUT_Y_RESIDUALS];
0310   Kst::VectorPtr outputVectorYParameters = _outputVectors[VECTOR_OUT_Y_PARAMETERS];
0311   Kst::VectorPtr outputVectorYCovariance = _outputVectors[VECTOR_OUT_Y_COVARIANCE];
0312   Kst::ScalarPtr outputScalar = _outputScalars[SCALAR_OUT];
0313 
0314   Kst::LabelInfo label_info = inputVectorY->labelInfo();
0315 
0316   _X0 = inputVectorX->noNanValue()[0];
0317   n_params = 3;
0318 
0319   label_info.name = tr("A\\Cdotexp((x-x_o)/\\tau) + C fit to %1").arg(label_info.name);
0320   outputVectorYFitted->setLabelInfo(label_info);
0321 
0322   label_info.name = tr("Exponential Fit Residuals");
0323   outputVectorYResiduals->setLabelInfo(label_info);
0324 
0325   bool bReturn = false;
0326 
0327   bReturn = kstfit_nonlinear( inputVectorX, inputVectorY,
0328                               outputVectorYFitted, outputVectorYResiduals, outputVectorYParameters,
0329                               outputVectorYCovariance, outputScalar );
0330 
0331   outputVectorYParameters->raw_V_ptr()[1] = 1.0/outputVectorYParameters->raw_V_ptr()[1];
0332   outputVectorYParameters->raw_V_ptr()[3] = _X0;
0333 
0334   return bReturn;
0335 }
0336 
0337 
0338 Kst::VectorPtr FitExponentialUnweightedSource::vectorX() const {
0339   return _inputVectors[VECTOR_IN_X];
0340 }
0341 
0342 
0343 Kst::VectorPtr FitExponentialUnweightedSource::vectorY() const {
0344   return _inputVectors[VECTOR_IN_Y];
0345 }
0346 
0347 
0348 QStringList FitExponentialUnweightedSource::inputVectorList() const {
0349   QStringList vectors(VECTOR_IN_X);
0350   vectors += VECTOR_IN_Y;
0351   return vectors;
0352 }
0353 
0354 
0355 QStringList FitExponentialUnweightedSource::inputScalarList() const {
0356   return QStringList();
0357 }
0358 
0359 
0360 QStringList FitExponentialUnweightedSource::inputStringList() const {
0361   return QStringList( /*STRING_IN*/ );
0362 }
0363 
0364 
0365 QStringList FitExponentialUnweightedSource::outputVectorList() const {
0366   QStringList vectors(VECTOR_OUT_Y_FITTED);
0367   vectors += VECTOR_OUT_Y_RESIDUALS;
0368   vectors += VECTOR_OUT_Y_PARAMETERS;
0369   vectors += VECTOR_OUT_Y_COVARIANCE;
0370   vectors += VECTOR_OUT_Y_PARAMETERS;
0371   return vectors;
0372 }
0373 
0374 
0375 QStringList FitExponentialUnweightedSource::outputScalarList() const {
0376   return QStringList( SCALAR_OUT );
0377 }
0378 
0379 
0380 QStringList FitExponentialUnweightedSource::outputStringList() const {
0381   return QStringList( /*STRING_OUT*/ );
0382 }
0383 
0384 
0385 void FitExponentialUnweightedSource::saveProperties(QXmlStreamWriter &s) {
0386   Q_UNUSED(s);
0387 //   s.writeAttribute("value", _configValue);
0388 }
0389 
0390 
0391 QString FitExponentialUnweightedSource::parameterName(int index) const {
0392   QString parameter;
0393   switch (index) {
0394     case 0:
0395       parameter = "A";
0396       break;
0397     case 1:
0398       parameter = "\\tau";
0399       break;
0400     case 2:
0401       parameter = "C";
0402       break;
0403     case 3:
0404       parameter = "X_o";
0405       break;
0406     default:
0407       parameter = "";
0408       break;
0409   }
0410 
0411   return parameter;
0412 }
0413 
0414 
0415 // Name used to identify the plugin.  Used when loading the plugin.
0416 QString FitExponentialUnweightedPlugin::pluginName() const { return tr("Exponential Fit"); }
0417 QString FitExponentialUnweightedPlugin::pluginDescription() const { return tr("Generates an exponential fit for a set of data."); }
0418 
0419 
0420 Kst::DataObject *FitExponentialUnweightedPlugin::create(Kst::ObjectStore *store, Kst::DataObjectConfigWidget *configWidget, bool setupInputsOutputs) const {
0421 
0422   if (ConfigWidgetFitExponentialUnweightedPlugin* config = static_cast<ConfigWidgetFitExponentialUnweightedPlugin*>(configWidget)) {
0423 
0424     FitExponentialUnweightedSource* object = store->createObject<FitExponentialUnweightedSource>();
0425 
0426     if (setupInputsOutputs) {
0427       object->setupOutputs();
0428       object->setInputVector(VECTOR_IN_X, config->selectedVectorX());
0429       object->setInputVector(VECTOR_IN_Y, config->selectedVectorY());
0430     }
0431 
0432     object->setPluginName(pluginName());
0433 
0434     object->writeLock();
0435     object->registerChange();
0436     object->unlock();
0437 
0438     return object;
0439   }
0440   return 0;
0441 }
0442 
0443 
0444 Kst::DataObjectConfigWidget *FitExponentialUnweightedPlugin::configWidget(QSettings *settingsObject) const {
0445   ConfigWidgetFitExponentialUnweightedPlugin *widget = new ConfigWidgetFitExponentialUnweightedPlugin(settingsObject);
0446   return widget;
0447 }
0448 
0449 #ifndef QT5
0450 Q_EXPORT_PLUGIN2(kstplugin_FitExponentialUnweightedPlugin, FitExponentialUnweightedPlugin)
0451 #endif
0452 
0453 // vim: ts=2 sw=2 et