#ifndef __EXAMPLE_FITTER_H__ #define __EXAMPLE_FITTER_H__ #include #include "gml_gfit.h" //============================================================================= /** We use a Weibull as our psychometric function. Our data go from 50% to 100% so we use 1.0-0.5*exp(-(C/A)^B) where A is the 81% correct point and B determines the psychometric function slope. */ inline IML_DOUBLE weibull(IML_DOUBLE C, IML_DOUBLE A, IML_DOUBLE B) { return(1.0 - 0.5*exp( -1 * pow( C/A, B) ) ); } //============================================================================= // FIRST WE DEFINE A DATA STRUCTURE WHICH WILL HOLD THE DATA AND // INTERMEDIATE MODEL VALUES. //============================================================================= /** The my_data class is used to contain example (psychometric) data. Calculated values are stored here as well. The class knows how to calculate error, in this case, negative log-likelihood, and how to calculate the minimum log-likelihood so we can try to calculate a chi-squared value once the best model is found. */ class my_data { public: /** The independent variable. */ float Contrast; /** A parameter of the experiment. */ float TrialCount; /** The dependent variable. */ float NumberCorrect; /** An alias for the dependent variable. */ float NumberInCorrect; /** Another alias for the dependent variable. */ float DataFractionCorrect; /** The model estimate of the dependent variable. */ float ModelFractionCorrect; /** Calculated as the binomial variance (estimate) for the data. */ float ConfidenceWeight; /** This will hold the value of negative log-likelihood in the case where the model passes exactly through our DataFractionCorrect. */ float BestLogLikelihood; /** This is the function that will be minimized for the model. This will only be called by the evaluate() function which must be defined in my fitter below. */ IML_DOUBLE logLikelihood(void) { return(-NumberCorrect*log(ModelFractionCorrect) - NumberInCorrect*log(1.0-ModelFractionCorrect) ); }; /** Calculate the "error" value in the case where the model passes right throught the data point. */ void set_best_LL(void) { if( !NumberCorrect || ! NumberInCorrect) BestLogLikelihood= 0; else BestLogLikelihood = -NumberCorrect*log(DataFractionCorrect) - NumberInCorrect*log(1.0-DataFractionCorrect ); }; }; //============================================================================= //============================================================================= /** The my_fitter class is an example of how to use the gml_generic_fitter class. We will us it to fit a Weibull function to psychometric data. The class is derived from the gml_generic_fitter class, as described in the documentation. All I need to do is tell gml_generic_fitter that I need two parameters, make sure to set the DataCount variable that I inherit from gml_generic_fitter, then initialize my own stuff. */ class my_fitter : public gml_generic_fitter { public: /** A pointer which holds an array of my_data structures. */ my_data IML_PTR Data; /** A variable to hold an important quantity. */ IML_DOUBLE BestLogLikelihood; /** The only constructor. It tells gml_generic_fitter that there are two parameters, and initializes the DataCount variable inherited from gml_generic_fitter, and allocates the my_data array. */ my_fitter(IML_USINT DCnt) : gml_generic_fitter(2) { DCnt = (DCnt > 0 ? DCnt : 1); DataCount = DCnt; Data = new my_data[DCnt]; BestLogLikelihood = 0; }; // end /** The only destructor. Just dallocates the my_data array. */ ~my_fitter() { if(Data) delete [] Data; }; /** An interface function that must be defined. Returns the model error for the point so that goodness-of-fit statistics can be calculated. */ virtual IML_DOUBLE model_error(register IML_ULINT I) { return(Data[I].ModelFractionCorrect - Data[I].DataFractionCorrect); }; /** An interface function that must be defined. Returns the data value of the point so that goodness-of-fit statistics can be calculated. */ virtual IML_DOUBLE data(register IML_ULINT I) { return(Data[I].DataFractionCorrect); }; /** An interface function that must be defined. Returns the model confidence weight for the point so that goodness-of-fit statistics can be calculated. */ virtual IML_DOUBLE weight(register IML_ULINT I) { return(Data[I].ConfidenceWeight); }; /** The only interface function that must absolutely be defined. Returns the model error upon which the fitter shall operate, and from which covariance and correlation matrices will eventually be calcualted. */ virtual IML_DOUBLE evaluate( register gml_double_vec &PVector) { Evaluations++; // LOOP OVER ALL THE DATA AND CALCULATE THE MODEL ESTIMATE for(register IML_LINT Index=0; Index < DataCount; Index++) { my_data &DP = Data[Index]; DP.ModelFractionCorrect = weibull(DP.Contrast, PVector[0], PVector[1]); DP.ModelFractionCorrect = GML_PFLOOR(0.001,DP.ModelFractionCorrect); DP.ModelFractionCorrect = GML_PCEIL(0.999,DP.ModelFractionCorrect); } // end for // CALCULATE THE LOGLILIHOOD register IML_DOUBLE LL=0; for(register IML_LINT Index=0; Index < DataCount; Index++) LL += Data[Index].logLikelihood(); // AND RETURN THE (PUTATIVE) CHI-SQUARE VALUE return( 2*(LL-BestLogLikelihood) ); }; // end evaluate /** Calculate the absolute minimum best fit. */ IML_DOUBLE bestLL(void) { BestLogLikelihood=0; for(register IML_LINT Index=0; Index < DataCount; Index++) { Data[Index].set_best_LL(); BestLogLikelihood += Data[Index].BestLogLikelihood; } // END FOR return(BestLogLikelihood); }; // end bestLL }; #endif // __EXAMPLE_FITTER_H__