@@ -295,6 +295,20 @@ def integral(self):
295295 """
296296 return np .sum (self .p )
297297
298+ def mean (self ) -> float :
299+ """Return mean of the discrete distribution
300+
301+ The mean is the weighted average of the discrete values.
302+
303+ .. versionadded:: 0.15.3
304+
305+ Returns
306+ -------
307+ float
308+ Mean of discrete distribution
309+ """
310+ return np .sum (self .x * self .p ) / np .sum (self .p )
311+
298312 def clip (self , tolerance : float = 1e-6 , inplace : bool = False ) -> Discrete :
299313 r"""Remove low-importance points from discrete distribution.
300314
@@ -413,6 +427,18 @@ def sample(self, n_samples=1, seed=None):
413427 rng = np .random .RandomState (seed )
414428 return rng .uniform (self .a , self .b , n_samples )
415429
430+ def mean (self ) -> float :
431+ """Return mean of the uniform distribution
432+
433+ .. versionadded:: 0.15.3
434+
435+ Returns
436+ -------
437+ float
438+ Mean of uniform distribution
439+ """
440+ return 0.5 * (self .a + self .b )
441+
416442 def to_xml_element (self , element_name : str ):
417443 """Return XML representation of the uniform distribution
418444
@@ -1123,7 +1149,7 @@ def from_xml_element(cls, elem: ET.Element):
11231149
11241150 """
11251151 interpolation = get_text (elem , 'interpolation' )
1126- params = get_elem_list (elem , "parameters" , float )
1152+ params = get_elem_list (elem , "parameters" , float )
11271153 m = (len (params ) + 1 )// 2 # +1 for when len(params) is odd
11281154 x = params [:m ]
11291155 p = params [m :]
@@ -1347,6 +1373,30 @@ def integral(self):
13471373 for p , dist in zip (self .probability , self .distribution )
13481374 ])
13491375
1376+ def mean (self ) -> float :
1377+ """Return mean of the mixture distribution
1378+
1379+ The mean is the weighted average of the means of the component
1380+ distributions, weighted by probability * integral.
1381+
1382+ .. versionadded:: 0.15.3
1383+
1384+ Returns
1385+ -------
1386+ float
1387+ Mean of the mixture distribution
1388+ """
1389+ # Weight each component by its probability and integral
1390+ weights = [p * dist .integral () for p , dist in
1391+ zip (self .probability , self .distribution )]
1392+ total_weight = sum (weights )
1393+
1394+ if total_weight == 0 :
1395+ return 0.0
1396+
1397+ return sum ([w * dist .mean () for w , dist in
1398+ zip (weights , self .distribution )]) / total_weight
1399+
13501400 def clip (self , tolerance : float = 1e-6 , inplace : bool = False ) -> Mixture :
13511401 r"""Remove low-importance points / distributions
13521402
@@ -1369,14 +1419,14 @@ def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Mixture:
13691419 Distribution with low-importance points / distributions removed
13701420
13711421 """
1372- # Determine integral of original distribution to compare later
1373- original_integral = self .integral ()
1422+ # Calculate mean * integral for original distribution to compare later.
1423+ original_mean_integral = self . mean () * self .integral ()
13741424
13751425 # Determine indices for any distributions that contribute non-negligibly
1376- # to overall intensity
1377- intensities = [prob * dist .integral () for prob , dist in
1378- zip (self .probability , self .distribution )]
1379- indices = _intensity_clip (intensities , tolerance = tolerance )
1426+ # to overall mean * integral
1427+ mean_integrals = [prob * dist . mean () * dist .integral () for prob , dist in
1428+ zip (self .probability , self .distribution )]
1429+ indices = _intensity_clip (mean_integrals , tolerance = tolerance )
13801430
13811431 # Clip mixture of distributions
13821432 probability = self .probability [indices ]
@@ -1397,12 +1447,14 @@ def clip(self, tolerance: float = 1e-6, inplace: bool = False) -> Mixture:
13971447 # Create new distribution
13981448 new_dist = type (self )(probability , distribution )
13991449
1400- # Show warning if integral of new distribution is not within
1401- # tolerance of original
1402- diff = (original_integral - new_dist .integral ())/ original_integral
1450+ # Show warning if mean * integral of new distribution is not within
1451+ # tolerance of original. For energy distributions, mean * integral
1452+ # represents total energy.
1453+ new_mean_integral = new_dist .mean () * new_dist .integral ()
1454+ diff = (original_mean_integral - new_mean_integral )/ original_mean_integral
14031455 if diff > tolerance :
1404- warn ("Clipping mixture distribution resulted in an integral that is "
1405- f" lower by a fraction of { diff } when tolerance={ tolerance } ." )
1456+ warn ("Clipping mixture distribution resulted in a mean* integral "
1457+ f"that is lower by a fraction of { diff } when tolerance={ tolerance } ." )
14061458
14071459 return new_dist
14081460
0 commit comments