2222"""
2323
2424import warnings
25+ from collections .abc import Iterable
2526import numpy as np
2627from numpy import linalg
2728from numpy import cov as mle_cov
4344
4445class Bound :
4546 """
46- Parameters
47- ----------
48- ndim : int
49- The number of dimensions of the unit cube.
50-
47+ This is is master class for the all the bounds listing
48+ the key methods required from the bound
5149 """
5250
5351 def __init__ (self ):
54- pass
52+ self . logvol = 0
5553
5654 def contains (self , x ):
5755 """Checks if unit cube contains the point `x`."""
@@ -84,8 +82,14 @@ def samples(self, nsamples, rstate=None):
8482 def get_random_axes (self , rstate ):
8583 pass
8684
85+ def scale_to_logvol (self , rstate ):
86+ pass
87+
88+ def update (self , points , rstate = None , bootstrap = 0 , pool = None ):
89+ pass
8790
88- class UnitCube :
91+
92+ class UnitCube (Bound ):
8993 """
9094 An N-dimensional unit cube.
9195
@@ -139,8 +143,11 @@ def update(self, points, rstate=None, bootstrap=0, pool=None):
139143 def get_random_axes (self , rstate ):
140144 return np .eye (self .n )
141145
146+ def scale_to_logvol (self , logvol ):
147+ pass
142148
143- class Ellipsoid :
149+
150+ class Ellipsoid (Bound ):
144151 """
145152 An N-dimensional ellipsoid defined by::
146153
@@ -195,7 +202,6 @@ def __init__(self, ctr, cov, am=None, axes=None):
195202
196203 # Amount by which volume was increased after initialization (i.e.
197204 # cumulative factor from `scale_to_vol`).
198- self .expand = 1.
199205 self .funit = 1
200206
201207 def scale_to_logvol (self , logvol ):
@@ -342,7 +348,6 @@ def update(self,
342348 self .logvol = ell .logvol
343349 self .axlens = ell .axlens
344350 self .axes = ell .axes
345- self .expand = ell .expand
346351
347352 # Use bootstrapping to determine the volume expansion factor.
348353 if bootstrap > 0 :
@@ -376,7 +381,7 @@ def get_random_axes(self, rstate):
376381 return self .axes
377382
378383
379- class MultiEllipsoid :
384+ class MultiEllipsoid ( Bound ) :
380385 """
381386 A collection of M N-dimensional ellipsoids.
382387
@@ -418,9 +423,7 @@ def __init__(self, ells=None, ctrs=None, covs=None):
418423 self .__update_arrays ()
419424
420425 # Compute quantities.
421- self .expands = np .ones (self .nells )
422- self .logvol_tot = logsumexp (self .logvols )
423- self .expand_tot = 1.
426+ self .logvol = logsumexp (self .logvol_ells ) # ignores overlap
424427 self .funit = 1
425428
426429 def __update_arrays (self ):
@@ -430,23 +433,26 @@ def __update_arrays(self):
430433 self .ctrs = np .array ([ell .ctr for ell in self .ells ])
431434 self .covs = np .array ([ell .cov for ell in self .ells ])
432435 self .ams = np .array ([ell .am for ell in self .ells ])
433- self .logvols = np .array ([ell .logvol for ell in self .ells ])
436+ self .logvol_ells = np .array ([ell .logvol for ell in self .ells ])
434437
435- def scale_to_logvol (self , logvols ):
438+ def scale_to_logvol (self , logvol ):
436439 """Scale ellipoids to a corresponding set of
437- target volumes.
440+ target volumes or to a new total volume
441+ If the argument is scalar this is assumed to
442+ be new logvol, if it's iterable it's assumed to be
443+ array of log volumes
438444 """
445+ if isinstance (logvol , Iterable ):
446+ logvol_ells_new = logvol
447+ else :
448+ scale = logvol - self .logvol
449+ logvol_ells_new = self .logvol_ells + scale
439450 for i in range (self .nells ):
440- self .ells [i ].scale_to_logvol (logvols [i ])
451+ self .ells [i ].scale_to_logvol (logvol_ells_new [i ])
441452
442453 # IMPORTANT We must also update arrays ams, covs
443454 self .__update_arrays ()
444-
445- self .expands = np .array (
446- [self .ells [i ].expand for i in range (self .nells )])
447- logvol_tot = logsumexp (logvols )
448- self .expand_tot *= np .exp (logvol_tot - self .logvol_tot )
449- self .logvol_tot = logvol_tot
455+ self .logvol = logsumexp (self .logvol_ells )
450456
451457 def major_axis_endpoints (self ):
452458 """Return the endpoints of the major axis of each ellipsoid."""
@@ -503,7 +509,7 @@ def sample(self, rstate=None, return_q=False):
503509 else :
504510 return x , idx
505511
506- probs = np .exp (self .logvols - self .logvol_tot )
512+ probs = np .exp (self .logvol_ells - self .logvol )
507513 while True :
508514 # Select an ellipsoid at random proportional to its volume.
509515 idx = rand_choice (probs , rstate )
@@ -572,7 +578,7 @@ def monte_carlo_logvol(self,
572578 self .sample (rstate = rstate , return_q = True ) for i in range (ndraws )
573579 ]
574580 qsum = sum ((1. / q for (x , idx , q ) in samples ))
575- logvol = np .log (qsum / ndraws ) + self .logvol_tot
581+ logvol = np .log (qsum / ndraws ) + self .logvol
576582
577583 if return_overlap :
578584 # Estimate the fractional amount of overlap with the
@@ -637,17 +643,10 @@ def update(self,
637643 if not all (self .contains (p ) for p in points ):
638644 # refuse to update
639645 raise RuntimeError ('Rejecting invalid MultiEllipsoid region' )
640- self .logvol_tot = logsumexp (self .logvols )
641-
642- # Compute expansion factor.
643- expands = np .array ([ell .expand for ell in self .ells ])
644- logvols_orig = self .logvols - np .log (expands )
645- logvol_tot_orig = logsumexp (logvols_orig )
646- self .expand_tot = np .exp (self .logvol_tot - logvol_tot_orig )
646+ self .logvol = logsumexp (self .logvol_ells )
647647
648648 # Use bootstrapping to determine the volume expansion factor.
649649 if bootstrap > 0 :
650-
651650 # If provided, compute bootstraps in parallel using a pool.
652651 if pool is None :
653652 M = map
@@ -675,24 +674,24 @@ def update(self,
675674 ' or alternatively disable bootstrap (bootstrap=0)' )
676675 # If our ellipsoids are overly constrained, expand them.
677676 if expand > 1. :
678- lvs = self .logvols + ndim * np .log (expand )
677+ lvs = self .logvol_ells + ndim * np .log (expand )
679678 self .scale_to_logvol (lvs )
680679
681680 # Estimate the volume and fractional overlap with the unit cube
682681 # using Monte Carlo integration.
683682 if mc_integrate :
684- self .logvol_tot , self .funit = self .monte_carlo_logvol (
683+ self .logvol , self .funit = self .monte_carlo_logvol (
685684 rstate = rstate , return_overlap = True )
686685
687686 def get_random_axes (self , rstate ):
688- probs = np .exp (self .logvols - self .logvol_tot )
687+ probs = np .exp (self .logvol_ells - self .logvol )
689688 ell_idx = rand_choice (probs , rstate )
690689 # Choose axes.
691690 ax = self .ells [ell_idx ].axes
692691 return ax
693692
694693
695- class RadFriends :
694+ class RadFriends ( Bound ) :
696695 """
697696 A collection of N-balls of identical size centered on each live point.
698697 Importantly this class requires that the centers of the balls are set
@@ -721,7 +720,6 @@ def __init__(self, ndim, cov=None):
721720 detsign , detln = linalg .slogdet (self .am )
722721 assert detsign > 0
723722 self .logvol = logvol_prefactor (self .n ) - 0.5 * detln
724- self .expand = 1.
725723 self .funit = 1
726724
727725 def scale_to_logvol (self , logvol ):
@@ -913,7 +911,6 @@ def update(self,
913911 detsign , detln = linalg .slogdet (self .am )
914912 assert detsign > 0
915913 self .logvol = (logvol_prefactor (self .n ) - 0.5 * detln )
916- self .expand = 1.
917914
918915 # Estimate the volume and fractional overlap with the unit cube
919916 # using Monte Carlo integration.
@@ -960,7 +957,7 @@ def get_random_axes(self, rstate):
960957 return self .axes
961958
962959
963- class SupFriends :
960+ class SupFriends ( Bound ) :
964961 """
965962 A collection of N-cubes of identical size centered on each live point.
966963 Importantly this class requires that the centers of the cubes are set
@@ -989,7 +986,6 @@ def __init__(self, ndim, cov=None):
989986 detsign , detln = linalg .slogdet (self .am )
990987 assert detsign > 0
991988 self .logvol = self .n * np .log (2. ) - 0.5 * detln
992- self .expand = 1.
993989 self .funit = 1
994990
995991 def scale_to_logvol (self , logvol ):
@@ -1182,7 +1178,6 @@ def update(self,
11821178 detsign , detln = linalg .slogdet (self .am )
11831179 assert detsign > 0
11841180 self .logvol = (self .n * np .log (2. ) - 0.5 * detln )
1185- self .expand = 1.
11861181
11871182 # Estimate the volume and fractional overlap with the unit cube
11881183 # using Monte Carlo integration.
0 commit comments