MultipleImportanceSampler.cpp

Go to the documentation of this file.
00001 /**<!-------------------------------------------------------------------->
00002    @file   MultipleImportanceSampler.cpp
00003    @author Travis Fischer (fisch0920@gmail.com)
00004    @author Matthew Jacobs (jacobs.mh@gmail.com)
00005    @date   Fall 2008
00006    
00007    @brief
00008       "Optimally" combines samples taken from multiple sampling distributions 
00009    with respect to a function, 'f', whose value we are trying to integate 
00010    over a given domain,'D'.  D is assumed to be the union of the domains of 
00011    the individual underlying distributions.
00012       The goal of importance sampling is to reduce variance while 
00013    approximating the integral of f over D by drawing random samples across D 
00014    according to some probability density function p which is proportional to 
00015    f.  By drawing more samples from the regions where f is large (concentrating
00016    our sampling effort on the "important" regions of the domain), and similarly
00017    drawing less samples from the regions where f is small (focusing less effort
00018    in the "unimportant" regions of the domain), the variance of our estimate 
00019    overall is reduced, as long as we compensate for our uneven sampling rate 
00020    by weighting each sample by the inverse probability with which it was 
00021    sampled.
00022       Think of importance sampling this way:  if we have only one shot at 
00023    sampling f (only one sample because of very limited 'resources'), we would 
00024    like to concentrate that one sample in the region of the domain that will 
00025    contribute the most to the value of the integral we are ultimately trying 
00026    to approximate.  By biasing our sampling technique to weigh "important", 
00027    "large" regions of the domain with respect to f, we get more bang for our
00028    buck and correspondingly end up with a much lower variance estimator. If 
00029    we were to instead naively sample uniformly across the domain, D, there's 
00030    a good chance that our one, precious sample would be wasted by sampling 
00031    a location, x, that is unimportant, where f(x) is relatively small.
00032       A perfect estimator would take samples from a distribution p, across D, 
00033    whose probabilities were distributed directly proportional to f.  This is, 
00034    however, unreasonable, since if we knew the exact values of f across D, 
00035    we wouldn't have to approximate the integral of f over D in the first place.
00036    We can, however, still greatly reduce variance by using knowledge about 
00037    f to guide our sampling process and instead, sample according to either 
00038    an approximation of f, or possibly according to some factorization of f 
00039    into separate functions f1, f2, etc, some of which may be easier to draw 
00040    samples from.  As long as our sampling process attempts to draw samples 
00041    from areas in D where f is "known" to be large and avoids regions in D 
00042    where f is "known" to be small, our sampler will be more efficient and 
00043    our overall variance will be reduced.
00044    
00045       Multiple importance sampling generalizes this one step further. Say 
00046    we have n different sampling distributions p1,p2,...,pn, and our function 
00047    f, may be separated or factored into several functions, f1,f2,...fm. By 
00048    designing different sampling distributions to focus on one or more of the 
00049    simpler functions, fi, we can combine samples from the different sampling 
00050    distributions, each of which may be better at estimating some important 
00051    component (subfunction) of f, we can gain a much better final sampling 
00052    distribution, p, which captures all of the relevant aspects of f in an 
00053    intuitive, modularized manner (where each pi can be tuned towards 
00054    capturing a different fi).  How to go about combining samples from n 
00055    distributions, all or some of which may be good or bad estimators of f, 
00056    is a very difficult problem in this generic of a setting.
00057       Veach and Guibas (1995) showed several provably good ways of optimally 
00058    combining sampling techniques for Monte Carlo integration.  This class 
00059    implements their multiple importance sampling model, including several 
00060    heuristics they gave which attempt to combine samples in slightly 
00061    different ways that may work better in one situation versus another.
00062    These include the balance, cutoff, power, and maximum heuristics.
00063    
00064    @note for more information and theoretical details, see
00065       Veach and Guibas. Optimally Combining Sampling Techniques for Monte 
00066       Carlo Rendering. In Proceedings of the 22nd Annual Conference on 
00067       Computer Graphics and interactive Techniques S.G. Mair and R. Cook, 
00068       Eds. SIGGRAPH '95. ACM, New York, NY, 419-428.
00069    
00070    @see http://www-graphics.stanford.edu/papers/combine/
00071    <!-------------------------------------------------------------------->**/
00072 
00073 #include "MultipleImportanceSampler.h"
00074 #include "Sampler.h"
00075 
00076 void MultipleImportanceSampler::init() {
00077    const std::string &weightFunc = getValue<std::string>("weightFunc", 
00078                                                          "balance");
00079    
00080    if (weightFunc == "balance") {
00081       m_computeWeightFunc = &MultipleImportanceSampler::_computeWeightBalance;
00082    } else if (weightFunc == "power") {
00083       m_computeWeightFunc = &MultipleImportanceSampler::_computeWeightPower;
00084       
00085       m_beta  = getValue<real_t>("beta",  2.0);
00086    } else if (weightFunc == "cutoff") {
00087       m_computeWeightFunc = &MultipleImportanceSampler::_computeWeightCutoff;
00088       
00089       m_alpha = getValue<real_t>("alpha", 0.1);
00090    } else {
00091       ASSERT(weightFunc == "maximum");
00092       m_computeWeightFunc = &MultipleImportanceSampler::_computeWeightMaximum;
00093    }
00094 }
00095 
00096 void MultipleImportanceSampler::sample(WeightedEventList &results) {
00097    ASSERT(m_computeWeightFunc != NULL);
00098    results.clear();
00099    
00100    for(unsigned i = m_n; i--;) {
00101       Sampler *sampler  = m_samplers[i];
00102       const unsigned ni = m_ni[i];
00103       
00104       for(unsigned j = ni; j--;) {
00105          results.push_back(WeightedEvent(sampler->sample()));
00106          ((this)->*(m_computeWeightFunc))(results.back(), i);
00107       }
00108    }
00109 }
00110 
00111 void MultipleImportanceSampler::_computeWeightBalance(WeightedEvent &e, 
00112                                                       unsigned i)
00113 {
00114    ASSERT(i < m_n);
00115    real_t num = 0, den = 0;
00116    
00117    // TODO: this is incorrect; m_ni[j] doesn't exist cause m_n goes up too 
00118    // high!!!
00119    NYI();
00120    for(unsigned j = m_n; j--;) {
00121       if (i == j)
00122          den += (num = ((m_ni[j] / real_t(m_N)) * e.event.getPdf()));
00123       else 
00124          den += (m_ni[j] / real_t(m_N)) * m_samplers[i]->getPdf(e.event);
00125    }
00126    
00127    ASSERT(den > 0);
00128    e.weight = num / den;
00129 }
00130 
00131 void MultipleImportanceSampler::_computeWeightCutoff(WeightedEvent &e, 
00132                                                      unsigned i)
00133 {
00134    ASSERT(i < m_n);
00135    const real_t pi = e.event.getPdf();
00136    real_t pMax = pi;
00137    
00138    for(unsigned j = m_n; j--;) {
00139       const real_t p = (i == j ? pi : m_samplers[i]->getPdf(e.event));
00140       pMax = MAX(p, pMax);
00141    }
00142    
00143    pMax *= m_alpha;
00144    e.weight = 0;
00145    
00146    if (pi >= pMax) {
00147       for(unsigned j = m_n; j--;) {
00148          const real_t p = (i == j ? pi : m_samplers[i]->getPdf(e.event));
00149          
00150          if (p >= pMax)
00151             e.weight += p;
00152       }
00153       
00154       e.weight = pi / e.weight;
00155    } // else set weight to zero (disregards samples below a cutoff threshold)
00156 }
00157 
00158 void MultipleImportanceSampler::_computeWeightPower(WeightedEvent &e, 
00159                                                     unsigned i)
00160 {
00161    ASSERT(i < m_n);
00162    const real_t pi = pow(e.event.getPdf(), m_beta);
00163    
00164    e.weight = 0;
00165    for(unsigned j = m_n; j--;) {
00166       const real_t p = (i == j ? pi : pow(m_samplers[i]->getPdf(e.event), m_beta));
00167       
00168       e.weight += p;
00169    }
00170    
00171    e.weight = pi / e.weight;
00172 }
00173 
00174 void MultipleImportanceSampler::_computeWeightMaximum(WeightedEvent &e, 
00175                                                       unsigned i)
00176 {
00177    ASSERT(i < m_n);
00178    const real_t pi = e.event.getPdf();
00179    real_t pMax = pi;
00180    
00181    for(unsigned j = m_n; j--;) {
00182       const real_t p = (i == j ? pi : m_samplers[i]->getPdf(e.event));
00183       pMax = MAX(p, pMax);
00184    }
00185    
00186    e.weight = (pi == pMax);
00187 }
00188 

Generated on 28 Feb 2009 for Milton by doxygen 1.5.6