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