/*! \file \brief Probability distributions for Mixtures of pdfs \author Vaclav Smidl. ----------------------------------- BDM++ - C++ library for Bayesian Decision Making under Uncertainty Using IT++ for numerical operations ----------------------------------- */ #ifndef EMIX_H #define EMIX_H #define LOG2 0.69314718055995 #include "../shared_ptr.h" #include "exp_family.h" namespace bdm { //this comes first because it is used inside emix! /*! \brief Class representing ratio of two densities which arise e.g. by applying the Bayes rule. It represents density in the form: \f[ f(rv|rvc) = \frac{f(rv,rvc)}{f(rvc)} \f] where \f$ f(rvc) = \int f(rv,rvc) d\ rv \f$. In particular this type of arise by conditioning of a mixture model. At present the only supported operation is evallogcond(). */ class mratio: public pdf { protected: //! Nominator in the form of pdf const epdf* nom; //!Denominator in the form of epdf shared_ptr den; //!flag for destructor bool destroynom; //!datalink between conditional and nom datalink_m2e dl; public: //!Default constructor. By default, the given epdf is not copied! //! It is assumed that this function will be used only temporarily. mratio ( const epdf* nom0, const RV &rv, bool copy = false ) : pdf ( ), dl ( ) { // adjust rv and rvc set_rv ( rv ); dim = rv._dsize(); rvc = nom0->_rv().subt ( rv ); dimc = rvc._dsize(); //prepare data structures if ( copy ) { bdm_error ( "todo" ); // destroynom = true; } else { nom = nom0; destroynom = false; } bdm_assert_debug ( rvc.length() > 0, "Makes no sense to use this object!" ); // build denominator den = nom->marginal ( rvc ); dl.set_connection ( rv, rvc, nom0->_rv() ); }; double evallogcond ( const vec &val, const vec &cond ) { double tmp; vec nom_val ( dimension() + dimc ); dl.pushup_cond ( nom_val, val, cond ); tmp = exp ( nom->evallog ( nom_val ) - den->evallog ( cond ) ); return tmp; } //! Returns a sample from the density conditioned on \c cond, \f$x \sim epdf(rv|cond)\f$. \param cond is numeric value of \c rv virtual vec samplecond ( const vec &cond ) NOT_IMPLEMENTED(0); //! Object takes ownership of nom and will destroy it void ownnom() { destroynom = true; } //! Default destructor ~mratio() { if ( destroynom ) { delete nom; } } private: // not implemented mratio ( const mratio & ); mratio &operator= ( const mratio & ); }; /*! * \brief Mixture of epdfs Density function: \f[ f(x) = \sum_{i=1}^{n} w_{i} f_i(x), \quad \sum_{i=1}^n w_i = 1. \f] where \f$f_i(x)\f$ is any density on random variable \f$x\f$, called \a component, */ class emix : public epdf { protected: //! weights of the components vec w; //! Component (epdfs) Array > Coms; public: //! Default constructor emix ( ) : epdf ( ) { } virtual void validate (); vec sample() const; vec mean() const; vec variance() const; double evallog ( const vec &val ) const; vec evallog_mat ( const mat &Val ) const; //! Auxiliary function that returns pdflog for each component mat evallog_coms ( const mat &Val ) const; shared_ptr marginal ( const RV &rv ) const; //! Update already existing marginal density \c target void marginal ( const RV &rv, emix &target ) const; shared_ptr condition ( const RV &rv ) const; //Access methods //! returns a reference to the internal weights. Use with Care! vec& _w() { return w; } /*! \brief returns a reference to the internal array of components. Use with Care! Set components \c Coms Shared pointers in Coms are kept inside this instance and shouldn't be modified after being passed to this method. */ Array >& _Coms ( ) { return Coms; } //! returns a reference to the internal components specified by index i. Use with Care! shared_ptr _Coms ( int i ) { return Coms ( i ); } void set_rv ( const RV &rv ) { epdf::set_rv ( rv ); for ( int i = 0; i < Coms.length(); i++ ) { Coms ( i )->set_rv ( rv ); } } //! Load from structure with elements: //! \code //! { class='emix'; //! pdfs = (..., ...); // list of pdfs in the mixture //! weights = ( 0.5, 0.5 ); // weights of pdfs in the mixture //! } //! \endcode //!@} void from_setting ( const Setting &set ); }; SHAREDPTR ( emix ); UIREGISTER ( emix ); /*! * \brief Mixture of egiws */ class egiwmix : public egiw { protected: //! weights of the components vec w; //! Component (epdfs) Array Coms; //!Flag if owning Coms bool destroyComs; public: //!Default constructor egiwmix ( ) : egiw ( ) {}; //! Set weights \c w and components \c Coms //!By default Coms are copied inside. Parameter \c copy can be set to false if Coms live externally. Use method ownComs() if Coms should be destroyed by the destructor. void set_parameters ( const vec &w, const Array &Coms, bool copy = false ); //!return expected value vec mean() const; //!return a sample from the density vec sample() const; //!return the expected variance vec variance() const; // TODO!!! Defined to follow ANSI and/or for future development void mean_mat ( mat &M, mat&R ) const {}; double evallog_nn ( const vec &val ) const { return 0; }; double lognc () const { return 0; } shared_ptr marginal ( const RV &rv ) const; //! marginal density update void marginal ( const RV &rv, emix &target ) const; //Access methods //! returns a pointer to the internal mean value. Use with Care! vec& _w() { return w; } virtual ~egiwmix() { if ( destroyComs ) { for ( int i = 0; i < Coms.length(); i++ ) { delete Coms ( i ); } } } //! Auxiliary function for taking ownership of the Coms() void ownComs() { destroyComs = true; } //!access function egiw* _Coms ( int i ) { return Coms ( i ); } void set_rv ( const RV &rv ) { egiw::set_rv ( rv ); for ( int i = 0; i < Coms.length(); i++ ) { Coms ( i )->set_rv ( rv ); } } //! Approximation of a GiW mix by a single GiW pdf egiw* approx(); }; /*! \brief Chain rule decomposition of epdf Probability density in the form of Chain-rule decomposition: \[ f(x_1,x_2,x_3) = f(x_1|x_2,x_3)f(x_2,x_3)f(x_3) \] Note that */ class mprod: public pdf { private: Array > pdfs; //! Data link for each pdfs Array > dls; public: //! \brief Default constructor mprod() { } /*!\brief Constructor from list of mFacs */ mprod ( const Array > &mFacs ) { set_elements ( mFacs ); } //! Set internal \c pdfs from given values void set_elements ( const Array > &mFacs ); double evallogcond ( const vec &val, const vec &cond ); vec evallogcond_mat ( const mat &Dt, const vec &cond ); vec evallogcond_mat ( const Array &Dt, const vec &cond ); //TODO smarter... vec samplecond ( const vec &cond ) { //! Ugly hack to help to discover if mpfs are not in proper order. Correct solution = check that explicitely. vec smp = std::numeric_limits::infinity() * ones ( dimension() ); vec smpi; // Hard assumption here!!! We are going backwards, to assure that samples that are needed from smp are already generated! for ( int i = ( pdfs.length() - 1 ); i >= 0; i-- ) { // generate contribution of this pdf smpi = pdfs ( i )->samplecond ( dls ( i )->get_cond ( smp , cond ) ); // copy contribution of this pdf into smp dls ( i )->pushup ( smp, smpi ); } return smp; } //! Load from structure with elements: //! \code //! { class='mprod'; //! pdfs = (..., ...); // list of pdfs in the order of chain rule //! } //! \endcode //!@} void from_setting ( const Setting &set ) { Array > temp_array; UI::get ( temp_array, set, "pdfs", UI::compulsory ); set_elements ( temp_array ); } }; UIREGISTER ( mprod ); SHAREDPTR ( mprod ); //! Product of independent epdfs. For dependent pdfs, use mprod. class eprod: public epdf { protected: //! Components (epdfs) Array epdfs; //! Array of indeces Array dls; public: //! Default constructor eprod () : epdfs ( 0 ), dls ( 0 ) {}; //! Set internal void set_parameters ( const Array &epdfs0, bool named = true ) { epdfs = epdfs0;//.set_length ( epdfs0.length() ); dls.set_length ( epdfs.length() ); bool independent = true; if ( named ) { for ( int i = 0; i < epdfs.length(); i++ ) { independent = rv.add ( epdfs ( i )->_rv() ); bdm_assert_debug ( independent, "eprod:: given components are not independent." ); } dim = rv._dsize(); } else { dim = 0; for ( int i = 0; i < epdfs.length(); i++ ) { dim += epdfs ( i )->dimension(); } } // int cumdim = 0; int dimi = 0; int i; for ( i = 0; i < epdfs.length(); i++ ) { dls ( i ) = new datalink; if ( named ) { dls ( i )->set_connection ( epdfs ( i )->_rv() , rv ); } else { dimi = epdfs ( i )->dimension(); dls ( i )->set_connection ( dimi, dim, linspace ( cumdim, cumdim + dimi - 1 ) ); cumdim += dimi; } } } vec mean() const; vec variance() const; vec sample() const; double evallog ( const vec &val ) const; //!access function const epdf* operator () ( int i ) const { bdm_assert_debug ( i < epdfs.length(), "wrong index" ); return epdfs ( i ); } //!Destructor ~eprod() { for ( int i = 0; i < epdfs.length(); i++ ) { delete dls ( i ); } } }; /*! \brief Mixture of pdfs with constant weights, all pdfs are of equal RV and RVC */ class mmix : public pdf { protected: //! Component (pdfs) Array > Coms; //!weights of the components vec w; public: //!Default constructor mmix() : Coms ( 0 ) { } double evallogcond ( const vec &dt, const vec &cond ) { double ll = 0.0; for ( int i = 0; i < Coms.length(); i++ ) { ll += Coms ( i )->evallogcond ( dt, cond ); } return ll; } vec samplecond ( const vec &cond ); //! Load from structure with elements: //! \code //! { class='mmix'; //! pdfs = (..., ...); // list of pdfs in the mixture //! weights = ( 0.5, 0.5 ); // weights of pdfs in the mixture //! } //! \endcode //!@} void from_setting ( const Setting &set ); virtual void validate(); }; SHAREDPTR ( mmix ); UIREGISTER ( mmix ); } #endif //MX_H