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 "fitgaussian_weighted.h"
0017 #include "objectstore.h"
0018 #include "ui_fitgaussian_weightedconfig.h"
0019 
0020 #define NUM_PARAMS 4
0021 #define MAX_NUM_ITERATIONS 500
0022 
0023 #include <gsl/gsl_fit.h>
0024 #include "../non_linear_weighted.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_IN_WEIGHTS = "Weights Vector";
0029 static const QString& SCALAR_IN_OFFSET = "Offset";
0030 
0031 static const QString& VECTOR_OUT_Y_FITTED = "Fit";
0032 static const QString& VECTOR_OUT_Y_RESIDUALS = "Residuals";
0033 static const QString& VECTOR_OUT_Y_PARAMETERS = "Parameters Vector";
0034 static const QString& VECTOR_OUT_Y_COVARIANCE = "Covariance";
0035 static const QString& SCALAR_OUT = "chi^2/nu";
0036 
0037 class ConfigWidgetFitGaussianWeightedPlugin : public Kst::DataObjectConfigWidget, public Ui_FitGaussian_WeightedConfig {
0038   public:
0039     ConfigWidgetFitGaussianWeightedPlugin(QSettings* cfg) : DataObjectConfigWidget(cfg), Ui_FitGaussian_WeightedConfig() {
0040       _store = 0;
0041       setupUi(this);
0042     }
0043 
0044     ~ConfigWidgetFitGaussianWeightedPlugin() {}
0045 
0046     void setObjectStore(Kst::ObjectStore* store) { 
0047       _store = store; 
0048       _vectorX->setObjectStore(store);
0049       _vectorY->setObjectStore(store);
0050       _vectorWeights->setObjectStore(store);
0051 
0052       _scalarOffset->setObjectStore(store);
0053       _forceOffset->setChecked(false);
0054       _scalarOffset->setEnabled(false);
0055 
0056     }
0057 
0058     void setupSlots(QWidget* dialog) {
0059       if (dialog) {
0060         connect(_vectorX, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified()));
0061         connect(_vectorY, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified()));
0062         connect(_vectorWeights, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified()));
0063         connect(_scalarOffset, SIGNAL(selectionChanged(QString)), dialog, SIGNAL(modified()));
0064         _scalarOffset->setDefaultValue(0.0);
0065         connect(_forceOffset, SIGNAL(toggled(bool)), dialog, SIGNAL(modified()));
0066         connect(_forceOffset, SIGNAL(toggled(bool)), _scalarOffset, SLOT(setEnabled(bool)));
0067       }
0068     }
0069 
0070     void setVectorX(Kst::VectorPtr vector) {
0071       setSelectedVectorX(vector);
0072     }
0073 
0074     void setVectorY(Kst::VectorPtr vector) {
0075       setSelectedVectorY(vector);
0076     }
0077 
0078     Kst::ScalarPtr scalarOffset() {return _scalarOffset->selectedScalar(); };
0079     void setScalarOffset(Kst::ScalarPtr scalar) {_scalarOffset->setSelectedScalar(scalar);};
0080 
0081     void setVectorsLocked(bool locked = true) {
0082       _vectorX->setEnabled(!locked);
0083       _vectorY->setEnabled(!locked);
0084     }
0085 
0086     Kst::VectorPtr selectedVectorX() { return _vectorX->selectedVector(); };
0087     void setSelectedVectorX(Kst::VectorPtr vector) { return _vectorX->setSelectedVector(vector); };
0088 
0089     Kst::VectorPtr selectedVectorY() { return _vectorY->selectedVector(); };
0090     void setSelectedVectorY(Kst::VectorPtr vector) { return _vectorY->setSelectedVector(vector); };
0091 
0092     Kst::VectorPtr selectedVectorWeights() { return _vectorWeights->selectedVector(); };
0093     void setSelectedVectorWeights(Kst::VectorPtr vector) { return _vectorWeights->setSelectedVector(vector); };
0094 
0095     virtual void setupFromObject(Kst::Object* dataObject) {
0096       if (FitGaussianWeightedSource* source = static_cast<FitGaussianWeightedSource*>(dataObject)) {
0097         setSelectedVectorX(source->vectorX());
0098         setSelectedVectorY(source->vectorY());
0099         setSelectedVectorWeights(source->vectorWeights());
0100         _forceOffset->setChecked(source->_forceOffset);
0101         setScalarOffset(source->scalarOffset());
0102       }
0103     }
0104 
0105     virtual bool configurePropertiesFromXml(Kst::ObjectStore *store, QXmlStreamAttributes& attrs) {
0106 
0107       bool validTag = true;
0108 
0109       setObjectStore(store);
0110 
0111       QStringRef av;
0112       bool force_offset = false;
0113       av = attrs.value("ForceOffset");
0114       if (!av.isNull()) {
0115         force_offset = QVariant(av.toString()).toBool();
0116       }
0117       _forceOffset->setChecked(force_offset);
0118 //      if (force_offset) {
0119 //        av = attrs.value("Offset");
0120 //        if (!av.isNull()) {
0121 //          QString name = av.toString();
0122 //          Kst::ObjectPtr object = store->retrieveObject(name);
0123 //          Kst::ScalarPtr scalar = Kst::kst_cast<Kst::Scalar>(object);
0124 //          setScalarOffset(scalar);
0125 //        }
0126 //      }
0127 
0128       return validTag;
0129     }
0130 
0131   public slots:
0132     virtual void save() {
0133       if (_cfg) {
0134         _cfg->beginGroup("Fit Gaussian Weighted Plugin");
0135         _cfg->setValue("Input Vector X", _vectorX->selectedVector()->Name());
0136         _cfg->setValue("Input Vector Y", _vectorY->selectedVector()->Name());
0137         _cfg->setValue("Input Vector Weights", _vectorWeights->selectedVector()->Name());
0138         _cfg->setValue("Force Offset", _forceOffset->isChecked());
0139         if (_forceOffset->isChecked()) {
0140           _cfg->setValue("Offset", _scalarOffset->selectedScalar()->Name());
0141         }
0142         _cfg->endGroup();
0143       }
0144     }
0145 
0146     virtual void load() {
0147       if (_cfg && _store) {
0148         _cfg->beginGroup("Fit Gaussian Weighted Plugin");
0149         QString vectorName = _cfg->value("Input Vector X").toString();
0150         Kst::Object* object = _store->retrieveObject(vectorName);
0151         Kst::Vector* vectorx = static_cast<Kst::Vector*>(object);
0152         if (vectorx) {
0153           setSelectedVectorX(vectorx);
0154         }
0155         vectorName = _cfg->value("Input Vector Y").toString();
0156         object = _store->retrieveObject(vectorName);
0157         Kst::Vector* vectory = static_cast<Kst::Vector*>(object);
0158         if (vectory) {
0159           setSelectedVectorX(vectory);
0160         }
0161         vectorName = _cfg->value("Input Vector Weights").toString();
0162         object = _store->retrieveObject(vectorName);
0163         Kst::Vector* vectorweights = static_cast<Kst::Vector*>(object);
0164         if (vectorweights) {
0165           setSelectedVectorX(vectorweights);
0166         }
0167         bool force_offset = _cfg->value("Force Offset").toBool();
0168         _forceOffset->setChecked(force_offset);
0169         if (force_offset) {
0170           QString scalarName = _cfg->value("Offset").toString();
0171           object = _store->retrieveObject(scalarName);
0172           Kst::Scalar* scalar = static_cast<Kst::Scalar*>(object);
0173           if (scalar) {
0174             setScalarOffset(scalar);
0175           }
0176         }
0177 
0178         _cfg->endGroup();
0179       }
0180     }
0181 
0182   private:
0183     Kst::ObjectStore *_store;
0184 
0185 };
0186 
0187 
0188 FitGaussianWeightedSource::FitGaussianWeightedSource(Kst::ObjectStore *store)
0189 : Kst::BasicPlugin(store) {
0190   _forceOffset = false;
0191 }
0192 
0193 
0194 FitGaussianWeightedSource::~FitGaussianWeightedSource() {
0195 }
0196 
0197 
0198 QString FitGaussianWeightedSource::_automaticDescriptiveName() const {
0199   return tr("%1 Weighted Gaussian").arg(vectorY()->descriptiveName());
0200 }
0201 
0202 
0203 void FitGaussianWeightedSource::change(Kst::DataObjectConfigWidget *configWidget) {
0204   if (ConfigWidgetFitGaussianWeightedPlugin* config = static_cast<ConfigWidgetFitGaussianWeightedPlugin*>(configWidget)) {
0205     setInputVector(VECTOR_IN_X, config->selectedVectorX());
0206     setInputVector(VECTOR_IN_Y, config->selectedVectorY());
0207     setInputVector(VECTOR_IN_WEIGHTS, config->selectedVectorWeights());
0208     setInputScalar(SCALAR_IN_OFFSET, config->scalarOffset());
0209     _forceOffset = config->_forceOffset->isChecked();
0210   }
0211 }
0212 
0213 
0214 void FitGaussianWeightedSource::setupOutputs() {
0215   setOutputVector(VECTOR_OUT_Y_FITTED, "");
0216   setOutputVector(VECTOR_OUT_Y_RESIDUALS, "");
0217   setOutputVector(VECTOR_OUT_Y_PARAMETERS, "");
0218   setOutputVector(VECTOR_OUT_Y_COVARIANCE, "");
0219   setOutputScalar(SCALAR_OUT, "");
0220 
0221   int i=0;
0222   for (QString paramName = parameterName(i); !paramName.isEmpty(); paramName = parameterName(++i)) {
0223     setOutputScalar(paramName, "");
0224   }
0225 }
0226 
0227 void function_initial_estimate( const double X[], const double Y[], int npts, double P[] ) {
0228   double min_y = 1E300;
0229   double max_y = -1E300;
0230   double min_x = 1E300;
0231   double max_x = -1E300;
0232   double mean_y = 0.0;
0233   double x_at_min_y=0;
0234   double x_at_max_y=0;
0235 
0236   double A, C, D;
0237 
0238   // find peak, valley, and mean
0239   for (int i = 0; i<npts; i++) {
0240     if (min_y > Y[i]) {
0241       min_y = Y[i];
0242       x_at_min_y = X[i];
0243     }
0244     if (max_y < Y[i]) {
0245       max_y = Y[i];
0246       x_at_max_y = X[i];
0247     }
0248     mean_y += Y[i];
0249 
0250     if (min_x > X[i]) {
0251       min_x = X[i];
0252     }
0253     if (max_x < X[i]) {
0254       max_x = X[i];
0255     }
0256   }
0257   if (npts>0) {
0258     mean_y /= double(npts);
0259   }
0260 
0261   // Heuristic for finding the sign of the : less time is spent in the peak than
0262   // in background if the range covers more than ~+- 2 sigma.
0263   // It will fail if you are
0264   // really zoomed into the gaussian.  Not sure what happens then :-(
0265   if (max_y - mean_y > mean_y - min_y) { // positive going gaussian
0266     A = max_y - min_y;
0267     D = min_y;
0268     C = x_at_max_y;
0269   } else { // negative going gaussian
0270     A = min_y - mean_y;
0271     D = max_y;
0272     C = x_at_min_y;
0273   }
0274   // guess that the width of the gaussian is around 1/10 of the x range (?)
0275 
0276   P[0] = A;
0277   P[1] = (max_x - min_x)*0.1;
0278   P[2] = C;
0279   P[3] = D;
0280 
0281 }
0282 
0283 
0284 double function_calculate(double x, double P[]) {
0285   double A = P[0];
0286   double B = 0.5/(P[1]*P[1]);
0287   double C = P[2];
0288   double D = offset_;
0289 
0290   if (n_params == 4) {
0291     D = P[3];
0292   }
0293   x -= C;
0294 
0295   return A*exp(-B*x*x) + D;
0296 }
0297 
0298 void function_derivative( double x, double P[], double dFdP[] ) {
0299   double A = P[0];
0300   double s = P[1];
0301   double B = 0.5/(s*s);
0302   double C = P[2];
0303   //double D = P[3];
0304 
0305   x -= C;
0306 
0307   double E = exp(-B*x*x);
0308 
0309   dFdP[0] = E;
0310   dFdP[1] = A*x*x*E/(s*s*s);
0311   dFdP[2] = 2*A*B*x*E;
0312   dFdP[3] = 1.0;
0313 
0314 }
0315 
0316 
0317 bool FitGaussianWeightedSource::algorithm() {
0318   Kst::VectorPtr inputVectorX = _inputVectors[VECTOR_IN_X];
0319   Kst::VectorPtr inputVectorY = _inputVectors[VECTOR_IN_Y];
0320   Kst::VectorPtr inputVectorWeights = _inputVectors[VECTOR_IN_WEIGHTS];
0321   Kst::ScalarPtr inputScalarOffset = _inputScalars[SCALAR_IN_OFFSET];
0322 
0323   Kst::VectorPtr outputVectorYFitted = _outputVectors[VECTOR_OUT_Y_FITTED];
0324   Kst::VectorPtr outputVectorYResiduals = _outputVectors[VECTOR_OUT_Y_RESIDUALS];
0325   Kst::VectorPtr outputVectorYParameters = _outputVectors[VECTOR_OUT_Y_PARAMETERS];
0326   Kst::VectorPtr outputVectorYCovariance = _outputVectors[VECTOR_OUT_Y_COVARIANCE];
0327   Kst::ScalarPtr outputScalar = _outputScalars[SCALAR_OUT];
0328 
0329   if (_forceOffset) {
0330     if (inputScalarOffset) {
0331       offset_ = inputScalarOffset->value();
0332     } else {
0333       offset_ = 0;
0334     }
0335     n_params = 3;
0336   } else {
0337     n_params = 4;
0338   }
0339 
0340 
0341   Kst::LabelInfo label_info = inputVectorY->labelInfo();
0342   label_info.name = tr("A\\cdotexp((x-x_o)^2/2\\sigma^2) + C fit to %1").arg(label_info.name);
0343   outputVectorYFitted->setLabelInfo(label_info);
0344 
0345   label_info.name = tr("Gaussian Fit Residuals");
0346   outputVectorYResiduals->setLabelInfo(label_info);
0347 
0348 
0349   bool bReturn = false;
0350 
0351   bReturn = kstfit_nonlinear_weighted( inputVectorX, inputVectorY, inputVectorWeights,
0352                               outputVectorYFitted, outputVectorYResiduals, outputVectorYParameters,
0353                               outputVectorYCovariance, outputScalar );
0354   return bReturn;
0355 }
0356 
0357 
0358 Kst::VectorPtr FitGaussianWeightedSource::vectorX() const {
0359   return _inputVectors[VECTOR_IN_X];
0360 }
0361 
0362 
0363 Kst::VectorPtr FitGaussianWeightedSource::vectorY() const {
0364   return _inputVectors[VECTOR_IN_Y];
0365 }
0366 
0367 
0368 Kst::VectorPtr FitGaussianWeightedSource::vectorWeights() const {
0369   return _inputVectors[VECTOR_IN_WEIGHTS];
0370 }
0371 
0372 Kst::ScalarPtr FitGaussianWeightedSource::scalarOffset() const {
0373   return _inputScalars[SCALAR_IN_OFFSET];
0374 }
0375 
0376 QStringList FitGaussianWeightedSource::inputVectorList() const {
0377   QStringList vectors(VECTOR_IN_X);
0378   vectors += VECTOR_IN_Y;
0379   vectors += VECTOR_IN_WEIGHTS;
0380   return vectors;
0381 }
0382 
0383 
0384 QStringList FitGaussianWeightedSource::inputScalarList() const {
0385   QStringList scalars(SCALAR_IN_OFFSET);
0386   return scalars;
0387 }
0388 
0389 
0390 QStringList FitGaussianWeightedSource::inputStringList() const {
0391   return QStringList( /*STRING_IN*/ );
0392 }
0393 
0394 
0395 QStringList FitGaussianWeightedSource::outputVectorList() const {
0396   QStringList vectors(VECTOR_OUT_Y_FITTED);
0397   vectors += VECTOR_OUT_Y_RESIDUALS;
0398   vectors += VECTOR_OUT_Y_PARAMETERS;
0399   vectors += VECTOR_OUT_Y_COVARIANCE;
0400   vectors += VECTOR_OUT_Y_PARAMETERS;
0401   return vectors;
0402 }
0403 
0404 
0405 QStringList FitGaussianWeightedSource::outputScalarList() const {
0406   return QStringList( SCALAR_OUT );
0407 }
0408 
0409 
0410 QStringList FitGaussianWeightedSource::outputStringList() const {
0411   return QStringList( /*STRING_OUT*/ );
0412 }
0413 
0414 
0415 void FitGaussianWeightedSource::saveProperties(QXmlStreamWriter &s) {
0416   QString force_offset;
0417   force_offset.setNum(_forceOffset);
0418   s.writeAttribute("ForceOffset", force_offset);
0419 }
0420 
0421 
0422 QString FitGaussianWeightedSource::parameterName(int index) const {
0423   QString parameter;
0424   switch (index) {
0425   case 0:
0426     parameter = "A";
0427     break;
0428   case 1:
0429     parameter = "\\sigma";
0430     break;
0431   case 2:
0432     parameter = "x_o";
0433     break;
0434   case 3:
0435     parameter = "C";
0436     break;
0437   default:
0438     parameter = "";
0439     break;
0440   }
0441 
0442   return parameter;
0443 }
0444 
0445 
0446 // Name used to identify the plugin.  Used when loading the plugin.
0447 QString FitGaussianWeightedPlugin::pluginName() const { return tr("Gaussian Weighted Fit"); }
0448 QString FitGaussianWeightedPlugin::pluginDescription() const { return tr("Generates a gaussian weighted fit for a set of data."); }
0449 
0450 
0451 Kst::DataObject *FitGaussianWeightedPlugin::create(Kst::ObjectStore *store, Kst::DataObjectConfigWidget *configWidget, bool setupInputsOutputs) const {
0452 
0453   if (ConfigWidgetFitGaussianWeightedPlugin* config = static_cast<ConfigWidgetFitGaussianWeightedPlugin*>(configWidget)) {
0454 
0455     FitGaussianWeightedSource* object = store->createObject<FitGaussianWeightedSource>();
0456     object->_forceOffset = config->_forceOffset->isChecked();
0457 
0458     if (setupInputsOutputs) {
0459       object->setupOutputs();
0460       object->setInputVector(VECTOR_IN_X, config->selectedVectorX());
0461       object->setInputVector(VECTOR_IN_Y, config->selectedVectorY());
0462       object->setInputVector(VECTOR_IN_WEIGHTS, config->selectedVectorWeights());
0463       object->setInputScalar(SCALAR_IN_OFFSET, config->scalarOffset());
0464     }
0465 
0466     object->setPluginName(pluginName());
0467 
0468     object->writeLock();
0469     object->registerChange();
0470     object->unlock();
0471 
0472     return object;
0473   }
0474   return 0;
0475 }
0476 
0477 
0478 Kst::DataObjectConfigWidget *FitGaussianWeightedPlugin::configWidget(QSettings *settingsObject) const {
0479   ConfigWidgetFitGaussianWeightedPlugin *widget = new ConfigWidgetFitGaussianWeightedPlugin(settingsObject);
0480   return widget;
0481 }
0482 
0483 #ifndef QT5
0484 Q_EXPORT_PLUGIN2(kstplugin_FitGaussianWeightedPlugin, FitGaussianWeightedPlugin)
0485 #endif
0486 
0487 // vim: ts=2 sw=2 et