C:/programs/etirm/src/ItemDichotomous.h

Go to the documentation of this file.
00001 /*! \file ItemDichotomous.h
00002 
00003   \brief
00004   Class representing a dichotomously scored item derived from ItemNR.
00005  
00006   Defines functions used in M-step of EM algorithm to compute item parameter estimates.
00007 
00008   Estimation Toolkit for Item Response Models (ETIRM)
00009   http://www.smallwaters.com/software/cpp/etirm.html
00010 
00011   Author(s): 
00012   Werner Wothke, maintenance (http://www.smallwaters.com)
00013   Brad Hanson (http://www.b-a-h.com/)
00014   See the file LICENSE for information on usage and redistribution.
00015 
00016   Copyright (C) 2008, Werner Wothke
00017   Copyright (c) 2000-2002, Bradley A. Hanson
00018  */
00019 
00020 #ifndef ETIRM_ITEMDICHOTOMOUS_H_
00021 #define ETIRM_ITEMDICHOTOMOUS_H_
00022 
00023 #ifdef ETIRM_NO_DIR_PREFIX
00024 #include "etirmtypes.h"
00025 #include "ItemNR.h"
00026 #else
00027 #include "etirm/etirmtypes.h"
00028 #include "etirm/ItemNR.h"
00029 #endif
00030 
00031 #include <cmath> // for log
00032 #if defined(ETIRM_USE_BOOST_CONFIG) || defined(BOOST_NO_LIMITS)
00033 // If no <limits> header use Boost (http://www.boost.org)
00034 // workaround. This assumes the Boost library is available.
00035 #include <boost/detail/limits.hpp>
00036 #else
00037 #include <limits>
00038 #endif
00039 
00040 // for compilers which do not put C library functions in std namespace
00041 #ifdef BOOST_NO_STDC_NAMESPACE
00042 namespace std
00043 { using ::log;}
00044 #endif
00045 
00046 namespace etirm
00047 {
00048   /*! 
00049     \brief
00050     Class representing a dichotomously scored item derived from ItemNR.
00051     
00052     Defines functions used in M-step of EM algorithm to compute item parameter estimates. 
00053 
00054     \section template_args Template Parameters
00055    
00056     \param D  Class for discrete latent variable distribution.
00057     \param ICC  Type containing functions defining the item characteristic curve (ICC)
00058         and derivatives of the ICC.
00059    */
00060   template<class D, class ICC> class ItemDichotomous : public ItemNR<D>
00061   {
00062 
00063 public:
00064     /*!
00065      \brief
00066      Class constructor.
00067      
00068      \section template_args Template Parameters
00069    
00070      \param D  Class for discrete latent variable distribution.
00071      \param ICC  Type containing functions defining the item characteristic curve (ICC)
00072          and derivatives of the ICC.
00073 
00074      \section function_args Function Parameters
00075      
00076      \param index  Zero-offset index of the item in the vector of all item responses.
00077      \param icc  Item characteristic curve for the model.
00078      \param dist Object containing information about the number of latent variable 
00079          points used in calculating the n and r.
00080      */
00081     ItemDichotomous(int index, ICC &icc, D *dist);
00082 
00083     /*! 
00084       \brief
00085       Class destructor. 
00086      */
00087     virtual ~ItemDichotomous();
00088 
00089     /*!
00090      \brief
00091      Returns a vector of estimated and fixed parameters. 
00092      
00093      The order of the parameters in the vector is determined by the 
00094      GetAllParameters member function of mICC.
00095      */
00096     virtual RealVector GetAllParameters() const
00097     {
00098       RealVector allParam(mICC.NumAllParameters());
00099       mICC.GetAllParameters(ItemNR<D>::mParameterEstimates, allParam); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00100       return allParam;
00101     }
00102 
00103     /*!
00104       \brief
00105       Assigns values of fixed and estimated parameters.
00106     
00107       \section function_args Function Parameters
00108    
00109       \param[in] &allParam  Address of vector containing all parameters of the item.
00110      */           
00111     virtual void SetAllParameters(const RealVector &allParam)
00112     {
00113       mICC.SetAllParameters(allParam.begin(), allParam.end(), ItemNR<D>::mParameterEstimates);
00114     } // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00115 
00116     /*!
00117       \brief
00118       Returns probablity of response "r".
00119 
00120       \section function_args Function Parameters
00121    
00122       \param[in]  r  Item response.
00123       \param[in]  &parameters Address of item parameter vector.
00124       \param[in]  theta Latent ability theta value.
00125      */
00126     virtual Real ICRF(Response r, const RealVector &parameters, const Real &theta) const;
00127 
00128     //! TODO: Document NormalizingConstant
00129     virtual Real NormalizingConstant() const
00130     {
00131       return mICC.GetD();
00132     }
00133 
00134     virtual int ScaleParameters(Real slope, Real intercept, bool ignorePrior = false);
00135 
00136     //! Returns string containing name of model used for item
00137     virtual std::string ModelName() const
00138     {
00139       return mICC.Name();
00140     }
00141 
00142     //! Returns type of model used for item.
00143     virtual IRTModel Model() const
00144     {
00145       return mICC.Model();
00146     }
00147 
00148     /*!
00149       \brief
00150       Assigns values of both mFirstResponse and mCorrectResponse.
00151     
00152       \section function_args Function Parameters
00153    
00154       \param[in] r  Code of incorrect item score. (r+1) is assumed to be the correct item score.
00155      */
00156     virtual void SetFirstResponse(Response r)
00157     {
00158       ItemNR<D>::mFirstResponse = r;
00159       mCorrectResponse = r+1;
00160     } // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00161 
00162     /*!
00163       \brief
00164       Returns the correct response category.
00165       
00166       For dichotomous items, the response to the second response category is 
00167       considered correct.
00168      */ 
00169     virtual Response CorrectResponse()
00170     {
00171       return mCorrectResponse;
00172     }
00173     
00174     /*!
00175       \brief
00176       Returns the incorrect response category.
00177       
00178       For dichotomous items, the response to the first response category 
00179       is considered incorrect.
00180      */
00181     Response IncorrectResponse()
00182     {
00183       return mCorrectResponse-1;
00184     }
00185     
00186     /*  The following functions provide an interface to minimization routines (such as UNCMIN)
00187      that are used in the M-step to estimates parameters for one item. */
00188 
00189     virtual double f_to_minimize(RealVector &p);
00190 
00191     virtual void gradient(RealVector &p, RealVector &g);
00192 
00193     virtual void hessian(RealVector &x, RealMatrix &h);
00194 
00195     virtual int HasAnalyticGradient() const;
00196 
00197     virtual int HasAnalyticHessian() const;
00198 
00199     virtual int ValidParameters(const RealVector &p, bool ignorePriors = false) const;
00200 
00201 protected:
00202 
00203     ICC mICC;
00204     //!< Object for calculating ICC and its derivatives
00205 
00206     /*!
00207       \brief
00208       The response associated with the correct answer.
00209        
00210       Stored as a data member to avoid overhead of 
00211       falling CorrectResponse() for member functions.
00212      */
00213     Response mCorrectResponse;
00214 
00215   };
00216 
00217   /*!
00218     \brief
00219     Class constructor.
00220       
00221     The first response category is assumed to correspond to an incorrect
00222     response and the second response category is assumed to correspond
00223     to a correct response.
00224     
00225     \section template_args Template Parameters
00226    
00227     \param D  Class for discrete latent variable distribution.
00228     \param ICC  Type containing functions defining the item characteristic curve (ICC)
00229         and derivatives of the ICC.
00230 
00231     \section function_args Function Parameters
00232 
00233     \param[in] index  Zero-offset index of the item in the vector of all item responses.
00234     \param[in] icc  Item characteristic curve for the model.
00235     \param[in] dist Object containing information about the number of latent variable 
00236         points used in calculating the n and r.
00237    */
00238   template<class D, class ICC> ItemDichotomous<D, ICC>::ItemDichotomous(int index, ICC &icc,
00239       D *dist) :
00240     ItemNR<D>(icc.NumParameters(), index, 2, dist), mICC(icc),
00241         mCorrectResponse(ItemNR<D>::mFirstResponse+1) // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00242   {
00243 
00244   }
00245 
00246   template<class D, class ICC> ItemDichotomous<D, ICC>::~ItemDichotomous()
00247   {
00248   }
00249 
00250   template<class D, class ICC> inline Real ItemDichotomous<D, ICC>::ICRF(Response r,
00251       const RealVector &parameters, const Real &theta) const
00252   {
00253     return (r == mCorrectResponse) ? mICC.ICC(parameters, theta) : (1.0 - mICC.ICC(parameters,
00254         theta));
00255   }
00256 
00257   /*! 
00258     \brief
00259     Function to maximize in M-step. 
00260    
00261     \section template_args Template Parameters
00262    
00263     \param D  Class for discrete latent variable distribution.
00264     \param ICC  Type containing functions defining the item characteristic curve (ICC)
00265         and derivatives of the ICC.
00266 
00267     \section function_args Function Parameters
00268 
00269     \param[in] &param Address of item parameter vector.
00270     
00271     Note: Technically, the negative value of the function is to be minimized by UNCMIN++,
00272          see inline comments in function code.
00273    */
00274   template<class D, class ICC> double ItemDichotomous<D, ICC>::f_to_minimize(RealVector &param)
00275   {
00276     Real value = 0.0;
00277     int ngroups = ItemNR<D>::mLatentDist->NumGroupsUnique(); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00278 
00279     /* Compute loglikelihood */
00280     for (int grp=1; grp<=ngroups; ++grp)
00281     {
00282       typename ItemNR<D>::n_iterator in = ItemNR<D>::NVector(grp); // Added typename and "ItemNR<D>::" references. ww, 1/12/2008.
00283       typename ItemNR<D>::r_iterator ir = ItemNR<D>::RVector(mCorrectResponse, grp); // Added typename and "ItemNR<D>::" references. ww, 1/12/2008.
00284       typename ItemNR<D>::point_iterator it = ItemNR<D>::mLatentDist->begin_points(grp); // Added typename and "ItemNR<D>::" references. ww, 1/12/2008.
00285       for (int i = ItemNR<D>::mNumLatentVarCat; i--; ++in, ++ir, ++it) // Added "ItemNR<D>::" references. ww, 1/12/2008.
00286       {
00287         Real prob = mICC.OpenICC(param, *it);
00288 
00289         Real t = std::log(prob) * *ir;
00290         t += (*in - *ir) * std::log((1.0 - prob));
00291         value += t;
00292       }
00293     }
00294 
00295     /* Add priors */
00296     PriorVector::const_iterator iprior = ItemNR<D>::mPriors.begin(); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00297     RealVector::iterator iparam = param.begin();
00298     for (int j = ItemNR<D>::NumParameters(); j--; ++iprior, ++iparam) // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00299     {
00300       if (*iprior)
00301       {
00302         if ((*iprior)->ZeroDensity(*iparam))
00303         {
00304           // If the prior density is zero then the log of prior density is
00305           // minus infinity (negative of minus infinity is returned
00306           // because the function is being minimized)
00307           if (std::numeric_limits<Real>::has_infinity)
00308           {
00309             return std::numeric_limits<Real>::infinity();
00310           }
00311           else
00312           {
00313             return std::numeric_limits<Real>::max();
00314           }
00315         }
00316         else
00317         {
00318           value += (*iprior)->LogDensity(*iparam);
00319         }
00320       }
00321     }
00322 
00323     return -value; // return negative value to find minimum rather than maximum
00324   }
00325 
00326   /*!
00327     \brief
00328     Gradient of function to maximize in M-step. 
00329     
00330     \section template_args Template Parameters
00331    
00332     \param D  Class for discrete latent variable distribution.
00333     \param ICC  Type containing functions defining the item characteristic curve (ICC)
00334         and derivatives of the ICC.
00335 
00336     \section function_args Function Parameters
00337 
00338     \param[in] &param Address of item parameter vector.
00339     \param[out] &g  Address of gradient with respect to item parameters. 
00340 
00341     Note: Technically, the negative value of the function is to be minimized by UNCMIN++,
00342          see inline comments in function code.
00343    */
00344   template<class D, class ICC> void ItemDichotomous<D, ICC>::gradient(RealVector &param,
00345       RealVector &g)
00346   {
00347     g = 0.0;
00348     RealVector deriv(ItemNR<D>::NumParameters()); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00349     int ngroups = ItemNR<D>::mLatentDist->NumGroupsUnique(); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00350 
00351     int i;
00352 
00353     for (int grp=1; grp<=ngroups; ++grp)
00354     {
00355       typename ItemNR<D>::n_iterator in = ItemNR<D>::NVector(grp); // Added typename and "ItemNR<D>::" references. ww, 1/12/2008.
00356       typename ItemNR<D>::r_iterator ir = ItemNR<D>::RVector(mCorrectResponse, grp); // Added typename and "ItemNR<D>::" references. ww, 1/12/2008.
00357       typename ItemNR<D>::point_iterator itheta = ItemNR<D>::mLatentDist->begin_points(grp); // Added typename and "ItemNR<D>::" references. ww, 1/12/2008.
00358       for (i = ItemNR<D>::mNumLatentVarCat; i--; ++in, ++ir, ++itheta) // Added "ItemNR<D>::" references. ww, 1/12/2008.
00359       {
00360         Real prob = mICC.OpenICC(param, *itheta);
00361 
00362         Real t = *ir - *in * prob;
00363         t /= (1.0 - prob) * prob;
00364 
00365         mICC.ICCDeriv1(param, *itheta, deriv);
00366 
00367         // use -t since function is to be minimized, not maximized
00368         deriv *= -t;
00369         g += deriv;
00370 
00371       }
00372     }
00373 
00374     /* Add priors */
00375     PriorVector::const_iterator iprior = ItemNR<D>::mPriors.begin(); // Added "ItemNR<D>::" references. ww, 1/12/2008.
00376     RealVector::iterator iparam = param.begin();
00377     RealVector::iterator ig = g.begin();
00378     for (int j = ItemNR<D>::NumParameters(); j--; ++iprior, ++iparam, ++ig) // Added "ItemNR<D>::" references. ww, 1/12/2008.
00379     {
00380       // subtract because function is to be minimized
00381       if (*iprior)
00382         *ig -= (*iprior)->DerivLogDensity1(*iparam);
00383     }
00384 
00385   }
00386 
00387   /*!
00388     \brief
00389     Hessian of function to maximize in M-step.
00390     
00391     \section template_args Template Parameters
00392    
00393     \param D  Class for discrete latent variable distribution.
00394     \param ICC  Type containing functions defining the item characteristic curve (ICC)
00395         and derivatives of the ICC.
00396 
00397     \section function_args Function Parameters
00398 
00399     \param[in] &param Address of item parameter vector.
00400     \param[out] &h  Address of hessian with respect to item parameters. 
00401 
00402     Note: Technically, the negative value of the function is to be minimized by UNCMIN++,
00403          see inline comments in function code.
00404     
00405     Note: The hessian is stored in the lower trangle of h.
00406    */
00407   template<class D, class ICC> void ItemDichotomous<D, ICC>::hessian(RealVector &param,
00408       RealMatrix &h)
00409   {
00410     int numParameters = ItemNR<D>::NumParameters(); // Added "ItemNR<D>::" references. ww, 1/12/2008.
00411 
00412     RealMatrix deriv2(numParameters, numParameters);
00413     RealVector deriv1(numParameters);
00414 
00415     RealMatrix::diag_iterator hdiag, ddiag;
00416     int i, j, k;
00417     int ngroups = ItemNR<D>::mLatentDist->NumGroupsUnique(); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00418 
00419     // Initialize lower triangle of h to zero
00420     for (i=0; i<numParameters; ++i)
00421     {
00422       hdiag = h.begin_diagonal(i+1, 1);
00423       for (j=numParameters-i; j--; ++hdiag)
00424       {
00425         *hdiag = 0.0;
00426       }
00427     }
00428 
00429     for (int grp=1; grp<=ngroups; ++grp)
00430     {
00431       typename ItemNR<D>::n_iterator in = ItemNR<D>::NVector(grp); // Added "typename" and "ItemNR<D>::" references. ww, 1/12/2008.
00432       typename ItemNR<D>::r_iterator ir = ItemNR<D>::RVector(mCorrectResponse, grp); // Added "typename" and "ItemNR<D>::" references. ww, 1/12/2008.
00433       typename ItemNR<D>::point_iterator itheta = ItemNR<D>::mLatentDist->begin_points(grp); // Added "typename" and "ItemNR<D>::" references. ww, 1/12/2008.
00434       for (k = ItemNR<D>::mNumLatentVarCat; k--; ++in, ++ir, ++itheta) // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00435       {
00436         Real prob = mICC.OpenICC(param, *itheta);
00437         Real t1 = -*in * prob * prob;
00438         t1 += 2.0 * *ir * prob;
00439         t1 -= *ir;
00440         Real t2 = *ir - *in * prob;
00441         Real t = prob * (1.0 - prob);
00442         t1 /= t * t;
00443         t2 /= t;
00444 
00445         mICC.ICCDeriv1(param, *itheta, deriv1);
00446         mICC.ICCDeriv2(param, *itheta, deriv2);
00447 
00448         for (int i=0; i<numParameters; ++i)
00449         {
00450           hdiag = h.begin_diagonal(i+1, 1);
00451           ddiag = deriv2.begin_diagonal(i+1, 1);
00452           RealVector::iterator prow = deriv1.begin() + i;
00453           RealVector::iterator pcol = deriv1.begin();
00454           for (j=numParameters-i; j--; ++prow, ++pcol, ++hdiag, ++ddiag)
00455           {
00456             // subtract rather than add since function is to be minimized
00457             *hdiag -= t1 * *prow * *pcol;
00458             *hdiag -= t2 * *ddiag;
00459           }
00460         }
00461       }
00462     }
00463 
00464     /* Add second derivatives of priors to diagonal elements of hessian */
00465     PriorVector::const_iterator iprior = ItemNR<D>::mPriors.begin(); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00466     RealVector::iterator iparam = param.begin();
00467     hdiag = h.begin_diagonal(1, 1);
00468     for (i = numParameters; i--; ++iparam, ++iprior, ++hdiag)
00469     {
00470       // subtract because function is to be minimized
00471       if (*iprior)
00472         *hdiag -= (*iprior)->DerivLogDensity2(*iparam);
00473     }
00474 
00475   }
00476 
00477   //! Indicates that the analytic gradient is available.
00478   template<class D, class ICC> int ItemDichotomous<D, ICC>::HasAnalyticGradient() const
00479   {
00480     return mICC.GradientDefined();
00481   }
00482 
00483   //! Indicates that the analytic hessian is available.
00484   template<class D, class ICC> int ItemDichotomous<D, ICC>::HasAnalyticHessian() const
00485   {
00486     return mICC.HessianDefined();
00487   }
00488 
00489   //! Indicates that the parameter vector creates a valid function value.
00490   template<class D, class ICC> int ItemDichotomous<D, ICC>::ValidParameters(
00491       const RealVector &param, bool ignorePriors) const
00492   {
00493     if (!(mICC.ValidParameters(param)))
00494       return 0;
00495 
00496     return ItemNR<D>::ValidParameters(param, ignorePriors);
00497   }
00498 
00499   /*!
00500     \brief
00501     Transforms the parameter estimates to new latent variable scale.
00502     
00503     Returns 1 if scaling results in invalid parameters. In this case parameters
00504     are not modified.
00505     
00506     
00507     \section function_args Function Parameters
00508 
00509     \param[in]  slope Slope parameter of linear scale transformation.
00510     \param[in]  intercept Intercept parameter of linear scale transformation.
00511     \param[in]  ignorePriors  If TRUE, do not check if tranformed parameters fall outside the
00512           prior regions.
00513    */
00514   template<class D, class ICC> int ItemDichotomous<D, ICC>::ScaleParameters(Real slope,
00515       Real intercept, bool ignorePriors)
00516   {
00517     RealVector scaledParam(ItemNR<D>::mParameterEstimates); // Added "ItemNR<D>::" reference. ww, 1/12/2008.
00518 
00519     mICC.Scale(slope, intercept, scaledParam);
00520 
00521     // Check if scaled parameters are valid
00522     if (!(ValidParameters(scaledParam, ignorePriors)))
00523       return 1;
00524 
00525     ItemNR<D>::mParameterEstimates = scaledParam;
00526 
00527     return 0;
00528   }
00529 
00530 }
00531 
00532 #endif //ETIRM_ITEMDICHOTOMOUS_H_

Generated on Sat Mar 1 21:40:15 2008 for ETIRM by  doxygen 1.5.4