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