def test_init_walkers_dict(self, pipe): sam_set = lhd(10, pipe._modellink._n_par, pipe._modellink._par_rng, 'center', pipe._criterion, 100) sam_dict = sdict(zip(pipe._modellink._par_name, sam_set.T)) p0_walkers = get_walkers(pipe, init_walkers=sam_dict)[1] if pipe._is_controller: assert isinstance(p0_walkers, dict)
def test_3_2_constraints_outside(self): constraints = np.random.rand(2, 2) val_rng = [[1, 2], [2, 3]] assert np.allclose(lhd(3, 2, val_rng=val_rng, constraints=constraints), np.array([[1.54863137, 2.46114717], [1.14121827, 2.32122092], [1.81252907, 2.93057501]]))
def test_3_2_fixed(self): assert np.allclose(lhd(3, 2, method='fixed'), np.array([[1, 1], [0.5, 0], [0, 0.5]]))
def test_3_2_random(self): assert np.allclose(lhd(3, 2, method='random'), np.array([[0.18293783, 0.47919574], [0.86758779, 0.96392433], [0.57172979, 0.21529804]]))
def test_3_2_maximin_str(self): assert np.allclose(lhd(3, 2, method='random', criterion='maximin'), np.array([[1, 1], [0.5, 0], [0, 0.5]]))
def test_3_2_no_constraints(self): assert np.allclose(lhd(3, 2, constraints=[]), np.array([[0.18293783, 0.47919574], [0.86758779, 0.96392433], [0.57172979, 0.21529804]]))
def test_3_2_same_constraints(self): constraints = [0, 0] assert np.allclose(lhd(3, 2, constraints=constraints, criterion=0), np.array([[0, 1], [1, 0], [0.5, 0.5]]))
def test_3_3_invalid_val_rng_shape(self): val_rng = [[0, 2], [0, 1]] with pytest.raises(ShapeError): lhd(3, 3, val_rng=val_rng)
def test_2_2_fixed(self): assert np.allclose(lhd(2, 2, method='fixed', criterion=0), np.array([[1, 0], [0, 1]]))
def test_1_2_fixed(self): assert np.allclose(lhd(1, 2, method='fixed', criterion=0), np.array([[0.5, 0.5]]))
def test_3_2_multi_float(self): assert np.allclose(lhd(3, 2, method='fixed', criterion=0.5), np.array([[1, 1], [0.5, 0], [0, 0.5]]))
def test_3_2_multi_str(self): assert np.allclose(lhd(3, 2, method='center', criterion='multi'), np.array([[0.83333333, 0.83333333], [0.5, 0.16666667], [0.16666667, 0.5]]))
def test_3_2_correlation_float(self): assert np.allclose(lhd(3, 2, method='center', criterion=1), np.array([[0.83333333, 0.83333333], [0.5, 0.16666667], [0.16666667, 0.5]]))
def test_no_plausible_init_walkers(self, pipe): with pytest.raises(InputError): get_walkers(pipe, init_walkers=lhd(1, pipe._modellink._n_par, pipe._modellink._par_rng, 'center', pipe._criterion, 100))
def test_3_2_center(self): assert np.allclose(lhd(3, 2, method='center'), np.array([[0.83333333, 0.83333333], [0.5, 0.16666667], [0.16666667, 0.5]]))
def test_3_2_custom_rng(self): val_rng = [[0, 2], [1, 4]] assert np.allclose(lhd(3, 2, val_rng=val_rng), np.array([[0.36587567, 2.43758721], [1.73517558, 3.89177300], [1.14345958, 1.64589411]]))
def test_3_1_fixed(self): assert np.allclose(lhd(3, 1, method='fixed', criterion=0), np.array([[1], [0.5], [0]]))
def test_3_2_constraints(self): constraints = np.random.rand(2, 2) assert np.allclose(lhd(3, 2, constraints=constraints, criterion=0), np.array([[1., 1], [0.5, 0], [0, 0.5]]))
def test_3_2_invalid_criterion_str(self): with pytest.raises(ValueError): lhd(3, 2, criterion='test')
def test_3_2_invalid_criterion_float(self): with pytest.raises(ValueError): lhd(3, 2, criterion=1.5)
def test_3_2_score(self): results = lhd(3, 2, criterion=0, get_score=True) assert np.allclose(results[0], np.array([[1, 1], [0.5, 0], [0, 0.5]])) assert np.allclose(results[1], np.array([0.80444958, 0.5, 0.80444958]))
def test_3_2_invalid_constraints(self): constraints = np.random.rand(2, 3) with pytest.raises(ShapeError): lhd(3, 2, constraints=constraints)
def test_4_2_no_quick_scan(self): assert np.allclose(lhd(4, 2, criterion=0, iterations=100, quickscan=False), np.array([[0.33333333, 0], [0, 0.66666667], [1, 0.33333333], [0.66666667, 1]]))
def get_walkers(pipeline_obj, *, emul_i=None, init_walkers=None, req_n_walkers=None, unit_space=False, lnpost_fn=None, **kwargs): """ Analyzes proposed `init_walkers` and returns plausible `p0_walkers`. Analyzes sample set `init_walkers` in the provided `pipeline_obj` at iteration `emul_i` and returns all samples that are plausible to be used as starting positions for MCMC walkers. The provided samples and returned walkers should be/are given in unit space if `unit_space` is *True*. If `init_walkers` is *None*, returns :attr:`~prism.Pipeline.impl_sam` instead if it is available. This function needs to be called by all MPI ranks. Parameters ---------- pipeline_obj : :obj:`~prism.Pipeline` object The instance of the :class:`~prism.Pipeline` class that needs to be used for determining the plausibility of the proposed starting positions. Optional -------- %(emul_i)s init_walkers : 2D array_like, dict, int or None. Default: None Sample set of proposed initial MCMC walker positions. All plausible samples in `init_walkers` will be returned. If int, generate an LHD of provided size and return all plausible samples. If *None*, return :attr:`~prism.Pipeline.impl_sam` corresponding to iteration `emul_i` instead. req_n_walkers : int or None. Default: None The minimum required number of plausible starting positions that should be returned. If *None*, all plausible starting positions in `init_walkers` are returned instead. .. versionadded:: 1.2.0 unit_space : bool. Default: False Bool determining whether or not the provided samples and returned walkers are given in unit space. lnpost_fn : function or None. Default: None If function, call :func:`~get_hybrid_lnpost_fn` using `lnpost_fn` and the same values for `pipeline_obj`, `emul_i` and `unit_space`, and return the resulting function definition `hybrid_lnpost()`. Any additionally provided `kwargs` are also passed to it. Returns ------- n_walkers : int Number of returned MCMC walkers. Note that this number can be higher than `req_n_walkers` if not *None*. p0_walkers : 2D :obj:`~numpy.ndarray` object or dict Array containing plausible starting positions of valid MCMC walkers. If `init_walkers` was provided as a dict, `p0_walkers` will be a dict. hybrid_lnpost : function (if `lnpost_fn` is a function) The function returned by :func:`~get_hybrid_lnpost_fn` using `lnpost_fn`, `pipeline_obj`, `emul_i`, `unit_space` and `kwargs` as the input values. See also -------- :func:`~get_hybrid_lnpost_fn` Returns a function definition ``hybrid_lnpost(par_set, *args, **kwargs)``. :attr:`~prism.Pipeline.worker_mode` Special context manager within which all code is executed in worker mode. Notes ----- If `init_walkers` is *None* and emulator iteration `emul_i` has not been analyzed yet, a :class:`~prism._internal.RequestError` will be raised. If `req_n_walkers` is not *None*, a custom Metropolis-Hastings sampling algorithm is used to generate the required number of starting positions. All plausible samples in `init_walkers` are used as the start of every MCMC chain. Note that if the number of plausible samples in `init_walkers` is small, it is possible that the returned `p0_walkers` are not spread out properly over parameter space. """ # Make abbreviation for pipeline_obj pipe = pipeline_obj # Check if provided pipeline_obj is an instance of the Pipeline class if not isinstance(pipe, Pipeline): raise TypeError("Input argument 'pipeline_obj' must be an instance of " "the Pipeline class!") # Check if the provided pipeline_obj uses a default emulator if (pipe._emulator._emul_type != 'default'): raise InputError("Input argument 'pipeline_obj' does not use a default" " emulator!") # Get emulator iteration emul_i = pipe._emulator._get_emul_i(emul_i) # If req_n_walkers is not None, check if it is an integer if req_n_walkers is not None: req_n_walkers = check_vals(req_n_walkers, 'req_n_walkers', 'int', 'pos') # Check if unit_space is a bool unit_space = check_vals(unit_space, 'unit_space', 'bool') # Assume that walkers are not to be returned as a dict walker_dict = False # Check if lnpost_fn is None and try to get hybrid_lnpost function if not if lnpost_fn is not None: try: hybrid_lnpost =\ get_hybrid_lnpost_fn(lnpost_fn, pipe, emul_i=emul_i, unit_space=unit_space, **kwargs) except InputError: raise InputError("Input argument 'lnpost_fn' is invalid!") # If init_walkers is None, use impl_sam of emul_i if init_walkers is None: # Controller checking if emul_i has already been analyzed if pipe._is_controller: # If iteration has not been analyzed, raise error if not pipe._n_eval_sam[emul_i]: raise RequestError("Emulator iteration %i has not been " "analyzed yet!" % (emul_i)) # If iteration is last iteration, init_walkers is current impl_sam elif (emul_i == pipe._emulator._emul_i): init_walkers = pipe._impl_sam # If iteration is not last, init_walkers is previous impl_sam else: init_walkers = pipe._emulator._sam_set[emul_i + 1] # Make sure to make a copy of init_walkers to avoid modifications init_walkers = init_walkers.copy() # Broadcast init_walkers to workers as p0_walkers p0_walkers = pipe._comm.bcast(init_walkers, 0) # If init_walkers is not None, use provided samples or LHD size else: # Controller checking if init_walkers is valid if pipe._is_controller: # If init_walkers is an int, create LHD of provided size if isinstance(init_walkers, int): # Check if provided integer is positive n_sam = check_vals(init_walkers, 'init_walkers', 'pos') # Create LHD of provided size init_walkers = lhd(n_sam, pipe._modellink._n_par, pipe._modellink._par_rng, 'center', pipe._criterion, 100) # If init_walkers is not an int, it must be array_like or dict else: # If init_walkers is provided as a dict, convert it if isinstance(init_walkers, dict): # Make sure that init_walkers is a SortedDict init_walkers = sdict(init_walkers) # Convert it to normal init_walkers = np_array(init_walkers.values()).T # Return p0_walkers as a dict walker_dict = True # Make sure that init_walkers is a NumPy array init_walkers = np_array(init_walkers, ndmin=2) # If unit_space is True, convert init_walkers to par_space if unit_space: init_walkers = pipe._modellink._to_par_space(init_walkers) # Check if init_walkers is valid init_walkers = pipe._modellink._check_sam_set( init_walkers, 'init_walkers') # Broadcast init_walkers to workers init_walkers = pipe._comm.bcast(init_walkers, 0) # Analyze init_walkers and save them as p0_walkers p0_walkers = pipe._evaluate_sam_set(emul_i, init_walkers, 'analyze') # Check if init_walkers is not empty and raise error if it is if not p0_walkers.shape[0]: raise InputError("Input argument 'init_walkers' contains no plausible " "samples!") # If req_n_walkers is not None, use MH MCMC to find all required walkers if req_n_walkers is not None: n_walkers, p0_walkers = _do_mh_walkers(pipe, p0_walkers, req_n_walkers) else: p0_walkers = np.unique(p0_walkers, axis=0) n_walkers = p0_walkers.shape[0] # Check if p0_walkers needs to be converted if unit_space: p0_walkers = pipe._modellink._to_unit_space(p0_walkers) # Check if p0_walkers needs to be returned as a dict if walker_dict: p0_walkers = sdict(zip(pipe._modellink._par_name, p0_walkers.T)) # Check if hybrid_lnpost was requested and return it as well if so if lnpost_fn is not None: return (n_walkers, p0_walkers, hybrid_lnpost) else: return (n_walkers, p0_walkers)
def test_init_walkers_set(self, pipe): get_walkers(pipe, init_walkers=lhd(10, pipe._modellink._n_par, None, 'center', pipe._criterion, 100), unit_space=True)