Mitsuba Renderer  0.5.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pmf.h
Go to the documentation of this file.
1 /*
2  This file is part of Mitsuba, a physically based rendering system.
3 
4  Copyright (c) 2007-2014 by Wenzel Jakob and others.
5 
6  Mitsuba is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License Version 3
8  as published by the Free Software Foundation.
9 
10  Mitsuba is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #pragma once
20 #if !defined(__MITSUBA_CORE_PMF_H_)
21 #define __MITSUBA_CORE_PMF_H_
22 
23 #include <mitsuba/mitsuba.h>
24 
26 
27 /**
28  * \brief Discrete probability distribution
29  *
30  * This data structure can be used to transform uniformly distributed
31  * samples to a stored discrete probability distribution.
32  *
33  * \ingroup libcore
34  */
36 public:
37  /// Allocate memory for a distribution with the given number of entries
38  explicit inline DiscreteDistribution(size_t nEntries = 0) {
39  reserve(nEntries);
40  clear();
41  }
42 
43  /// Clear all entries
44  inline void clear() {
45  m_cdf.clear();
46  m_cdf.push_back(0.0f);
47  m_normalized = false;
48  }
49 
50  /// Reserve memory for a certain number of entries
51  inline void reserve(size_t nEntries) {
52  m_cdf.reserve(nEntries+1);
53  }
54 
55  /// Append an entry with the specified discrete probability
56  inline void append(Float pdfValue) {
57  m_cdf.push_back(m_cdf[m_cdf.size()-1] + pdfValue);
58  }
59 
60  /// Return the number of entries so far
61  inline size_t size() const {
62  return m_cdf.size()-1;
63  }
64 
65  /// Access an entry by its index
66  inline Float operator[](size_t entry) const {
67  return m_cdf[entry+1] - m_cdf[entry];
68  }
69 
70  /// Have the probability densities been normalized?
71  inline bool isNormalized() const {
72  return m_normalized;
73  }
74 
75  /**
76  * \brief Return the original (unnormalized) sum of all PDF entries
77  *
78  * This assumes that \ref normalize() has previously been called
79  */
80  inline Float getSum() const {
81  return m_sum;
82  }
83 
84  /**
85  * \brief Return the normalization factor (i.e. the inverse of \ref getSum())
86  *
87  * This assumes that \ref normalize() has previously been called
88  */
89  inline Float getNormalization() const {
90  return m_normalization;
91  }
92 
93  /**
94  * \brief Normalize the distribution
95  *
96  * Throws an exception when no entries were previously
97  * added to the distribution.
98  *
99  * \return Sum of the (previously unnormalized) entries
100  */
101  inline Float normalize() {
102  SAssert(m_cdf.size() > 1);
103  m_sum = m_cdf[m_cdf.size()-1];
104  if (m_sum > 0) {
105  m_normalization = 1.0f / m_sum;
106  for (size_t i=1; i<m_cdf.size(); ++i)
107  m_cdf[i] *= m_normalization;
108  m_cdf[m_cdf.size()-1] = 1.0f;
109  m_normalized = true;
110  } else {
111  m_normalization = 0.0f;
112  }
113  return m_sum;
114  }
115 
116  /**
117  * \brief %Transform a uniformly distributed sample to the stored distribution
118  *
119  * \param[in] sampleValue
120  * An uniformly distributed sample on [0,1]
121  * \return
122  * The discrete index associated with the sample
123  */
124  inline size_t sample(Float sampleValue) const {
125  std::vector<Float>::const_iterator entry =
126  std::lower_bound(m_cdf.begin(), m_cdf.end(), sampleValue);
127  size_t index = std::min(m_cdf.size()-2,
128  (size_t) std::max((ptrdiff_t) 0, entry - m_cdf.begin() - 1));
129 
130  /* Handle a rare corner-case where a entry has probability 0
131  but is sampled nonetheless */
132  while (operator[](index) == 0 && index < m_cdf.size()-1)
133  ++index;
134 
135  return index;
136  }
137 
138  /**
139  * \brief %Transform a uniformly distributed sample to the stored distribution
140  *
141  * \param[in] sampleValue
142  * An uniformly distributed sample on [0,1]
143  * \param[out] pdf
144  * Probability value of the sample
145  * \return
146  * The discrete index associated with the sample
147  */
148  inline size_t sample(Float sampleValue, Float &pdf) const {
149  size_t index = sample(sampleValue);
150  pdf = operator[](index);
151  return index;
152  }
153 
154  /**
155  * \brief %Transform a uniformly distributed sample to the stored distribution
156  *
157  * The original sample is value adjusted so that it can be "reused".
158  *
159  * \param[in, out] sampleValue
160  * An uniformly distributed sample on [0,1]
161  * \return
162  * The discrete index associated with the sample
163  */
164  inline size_t sampleReuse(Float &sampleValue) const {
165  size_t index = sample(sampleValue);
166  sampleValue = (sampleValue - m_cdf[index])
167  / (m_cdf[index + 1] - m_cdf[index]);
168  return index;
169  }
170 
171  /**
172  * \brief %Transform a uniformly distributed sample.
173  *
174  * The original sample is value adjusted so that it can be "reused".
175  *
176  * \param[in,out]
177  * An uniformly distributed sample on [0,1]
178  * \param[out] pdf
179  * Probability value of the sample
180  * \return
181  * The discrete index associated with the sample
182  */
183  inline size_t sampleReuse(Float &sampleValue, Float &pdf) const {
184  size_t index = sample(sampleValue, pdf);
185  sampleValue = (sampleValue - m_cdf[index])
186  / (m_cdf[index + 1] - m_cdf[index]);
187  return index;
188  }
189 
190  /**
191  * \brief Turn the underlying distribution into a
192  * human-readable string format
193  */
194  std::string toString() const {
195  std::ostringstream oss;
196  oss << "DiscreteDistribution[sum=" << m_sum << ", normalized="
197  << (int) m_normalized << ", cdf={";
198  for (size_t i=0; i<m_cdf.size(); ++i) {
199  oss << m_cdf[i];
200  if (i != m_cdf.size()-1)
201  oss << ", ";
202  }
203  oss << "}]";
204  return oss.str();
205  }
206 private:
207  std::vector<Float> m_cdf;
208  Float m_sum, m_normalization;
209  bool m_normalized;
210 };
211 
212 namespace math {
213  /// Alias sampling data structure (see \ref makeAliasTable() for details)
214  template <typename QuantizedScalar, typename Index> struct AliasTableEntry {
215  /// Probability of sampling the current entry
216  QuantizedScalar prob;
217  /// Index of the alias entry
218  Index index;
219  };
220 
221  /**
222  * \brief Create the lookup table needed for Walker's alias sampling
223  * method implemented in \ref sampleAlias(). Runs in linear time.
224  *
225  * The basic idea of this method is that one can "redistribute" the
226  * probability mass of a distribution to make it uniform. This
227  * this can be done in a way such that the probability of each entry in
228  * the "flattened" PMF consists of probability mass from at most *two*
229  * entries in the original PMF. That then leads to an efficient O(1)
230  * sampling algorithm with a O(n) preprocessing step to set up this
231  * special decomposition.
232  *
233  * The downside of this method is that it generally does not preserve
234  * the nice stratification properties of QMC number sequences.
235  *
236  * \return The original (un-normalized) sum of all probabilities
237  * in \c pmf.
238  */
239  template <typename Scalar, typename QuantizedScalar, typename Index> float makeAliasTable(
240  AliasTableEntry<QuantizedScalar, Index> *tbl, Scalar *pmf, Index size) {
241  /* Allocate temporary storage for classification purposes */
242  Index *c = new Index[size],
243  *c_short = c - 1, *c_long = c + size;
244 
245  /* Begin by computing the normalization constant */
246  Scalar sum = 0;
247  for (size_t i=0; i<size; ++i)
248  sum += pmf[i];
249 
250  Scalar normalization = (Scalar) 1 / sum;
251  for (Index i=0; i<size; ++i) {
252  /* For each entry, determine whether there is
253  "too little" or "too much" probability mass */
254  Scalar value = size * normalization * pmf[i];
255  if (value < 1)
256  *++c_short = i;
257  else if (value > 1)
258  *--c_long = i;
259  tbl[i].prob = value;
260  tbl[i].index = i;
261  }
262 
263  /* Perform pairwise exchanges while there are entries
264  with too much probability mass */
265  for (Index i=0; i < size-1 && c_long - c < size; ++i) {
266  Index short_index = c[i],
267  long_index = *c_long;
268 
269  tbl[short_index].index = long_index;
270  tbl[long_index].prob -= (Scalar) 1 - tbl[short_index].prob;
271 
272  if (tbl[long_index].prob <= 1)
273  ++c_long;
274  }
275 
276  delete[] c;
277 
278  return sum;
279  }
280 
281  /// Generate a sample in constant time using the alias method
282  template <typename Scalar, typename QuantizedScalar, typename Index> Index sampleAlias(
283  const AliasTableEntry<QuantizedScalar, Index> *tbl, Index size, Scalar sample) {
284  Index l = std::min((Index) (sample * size), (Index) (size - 1));
285  Scalar prob = (Scalar) tbl[l].prob;
286 
287  sample = sample * size - l;
288 
289  if (prob == 1 || (prob != 0 && sample < prob))
290  return l;
291  else
292  return tbl[l].index;
293  }
294 
295  /**
296  * \brief Generate a sample in constant time using the alias method
297  *
298  * This variation shifts and scales the uniform random sample so
299  * that it can be reused for another sampling operation
300  */
301  template <typename Scalar, typename QuantizedScalar, typename Index> Index sampleAliasReuse(
302  const AliasTableEntry<QuantizedScalar, Index> *tbl, Index size, Scalar &sample) {
303  Index l = std::min((Index) (sample * size), (Index) (size - 1));
304  Scalar prob = (Scalar) tbl[l].prob;
305 
306  sample = sample * size - l;
307 
308  if (prob == 1 || (prob != 0 && sample < prob)) {
309  sample /= prob;
310  return l;
311  } else {
312  sample = (sample - prob) / (1 - prob);
313  return tbl[l].index;
314  }
315  }
316 };
317 
319 
320 #endif /* __MITSUBA_CORE_PMF_H_ */
Alias sampling data structure (see makeAliasTable() for details)
Definition: pmf.h:214
void append(Float pdfValue)
Append an entry with the specified discrete probability.
Definition: pmf.h:56
Index index
Index of the alias entry.
Definition: pmf.h:218
Index sampleAliasReuse(const AliasTableEntry< QuantizedScalar, Index > *tbl, Index size, Scalar &sample)
Generate a sample in constant time using the alias method.
Definition: pmf.h:301
Index sampleAlias(const AliasTableEntry< QuantizedScalar, Index > *tbl, Index size, Scalar sample)
Generate a sample in constant time using the alias method.
Definition: pmf.h:282
#define MTS_NAMESPACE_BEGIN
Definition: platform.h:137
Float getSum() const
Return the original (unnormalized) sum of all PDF entries.
Definition: pmf.h:80
Float normalize()
Normalize the distribution.
Definition: pmf.h:101
void clear()
Clear all entries.
Definition: pmf.h:44
bool isNormalized() const
Have the probability densities been normalized?
Definition: pmf.h:71
Float operator[](size_t entry) const
Access an entry by its index.
Definition: pmf.h:66
size_t sample(Float sampleValue) const
Transform a uniformly distributed sample to the stored distribution
Definition: pmf.h:124
size_t sample(Float sampleValue, Float &pdf) const
Transform a uniformly distributed sample to the stored distribution
Definition: pmf.h:148
#define SAssert(cond)
``Static&#39;&#39; assertion (to be used outside of classes that derive from Object)
Definition: logger.h:79
size_t sampleReuse(Float &sampleValue, Float &pdf) const
Transform a uniformly distributed sample.
Definition: pmf.h:183
Float getNormalization() const
Return the normalization factor (i.e. the inverse of getSum())
Definition: pmf.h:89
size_t sampleReuse(Float &sampleValue) const
Transform a uniformly distributed sample to the stored distribution
Definition: pmf.h:164
QuantizedScalar prob
Probability of sampling the current entry.
Definition: pmf.h:216
void reserve(size_t nEntries)
Reserve memory for a certain number of entries.
Definition: pmf.h:51
std::string toString() const
Turn the underlying distribution into a human-readable string format.
Definition: pmf.h:194
float makeAliasTable(AliasTableEntry< QuantizedScalar, Index > *tbl, Scalar *pmf, Index size)
Create the lookup table needed for Walker&#39;s alias sampling method implemented in sampleAlias(). Runs in linear time.
Definition: pmf.h:239
DiscreteDistribution(size_t nEntries=0)
Allocate memory for a distribution with the given number of entries.
Definition: pmf.h:38
size_t size() const
Return the number of entries so far.
Definition: pmf.h:61
#define MTS_NAMESPACE_END
Definition: platform.h:138
Discrete probability distribution.
Definition: pmf.h:35