Merge pull request #2165 from paulromano/mixture-sample-fix

Fix Mixture.sample to account for intensities of underlying distributions
This commit is contained in:
Ethan Peterson 2022-08-12 12:54:24 -04:00 committed by GitHub
commit e794513892
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 28 additions and 4 deletions

View file

@ -79,6 +79,18 @@ class Univariate(EqualityMixin, ABC):
"""
pass
def integral(self):
"""Return integral of distribution
.. versionadded:: 0.13.1
Returns
-------
float
Integral of distribution
"""
return 1.0
class Discrete(Univariate):
"""Distribution characterized by a probability mass function.
@ -1168,9 +1180,17 @@ class Mixture(Univariate):
def sample(self, n_samples=1, seed=None):
np.random.seed(seed)
idx = np.random.choice(range(len(self.distribution)),
n_samples, p=self.probability)
# Get probability of each distribution accounting for its intensity
p = np.array([prob*dist.integral() for prob, dist in
zip(self.probability, self.distribution)])
p /= p.sum()
# Sample from the distributions
idx = np.random.choice(range(len(self.distribution)),
n_samples, p=p)
# Draw samples from the distributions sampled above
out = np.empty_like(idx, dtype=float)
for i in np.unique(idx):
n_dist_samples = np.count_nonzero(idx == i)
@ -1294,7 +1314,7 @@ def combine_distributions(dists, probs):
# Combine discrete and continuous if present
if len(dist_list) > 1:
probs = [d.integral() for d in dist_list]
probs = [1.0]*len(dist_list)
dist_list[:] = [Mixture(probs, dist_list.copy())]
return dist_list[0]

View file

@ -61,12 +61,14 @@ def test_merge_discrete():
assert merged.x == pytest.approx([0.0, 0.5, 1.0, 5.0, 10.0])
assert merged.p == pytest.approx(
[0.6*0.3, 0.4*0.4, 0.6*0.2 + 0.4*0.5, 0.4*0.1, 0.6*0.5])
assert merged.integral() == pytest.approx(1.0)
# Probabilities add up but are not normalized
d1 = openmc.stats.Discrete([3.0], [1.0])
triple = openmc.stats.Discrete.merge([d1, d1, d1], [1.0, 2.0, 3.0])
assert triple.x == pytest.approx([3.0])
assert triple.p == pytest.approx([6.0])
assert triple.integral() == pytest.approx(6.0)
def test_uniform():
@ -186,6 +188,7 @@ def test_tabular():
# test histogram sampling
d = openmc.stats.Tabular(x, p, interpolation='histogram')
d.normalize()
assert d.integral() == pytest.approx(1.0)
samples = d.sample(n_samples, seed=100)
diff = np.abs(samples - d.mean())
@ -433,8 +436,9 @@ def test_combine_distributions():
t2 = openmc.stats.Tabular([0., 1.], [0.0, 2.0])
d1 = openmc.stats.Discrete([0.0], [1.0])
combined = openmc.stats.combine_distributions([t1, t2, d1], [0.25, 0.25, 0.5])
assert combined.integral() == pytest.approx(1.0)
# Sample the combined distribution and make sure the sample mean is within
# uncertainty of the expected value
samples = combined.sample(1000)
samples = combined.sample(10_000)
assert_sample_mean(samples, 0.25)