def _initialize_assembias_param_dict(self, assembias_strength=0.5, **kwargs): """ """ if not hasattr(self, 'param_dict'): self.param_dict = {} # Make sure the code behaves properly whether or not we were passed an iterable strength = assembias_strength try: iterator = iter(strength) strength = list(strength) except TypeError: strength = [strength] if 'assembias_strength_abscissa' in kwargs: abscissa = kwargs['assembias_strength_abscissa'] try: iterator = iter(abscissa) abscissa = list(abscissa) except TypeError: abscissa = [abscissa] else: abscissa = [2] if custom_len(abscissa) != custom_len(strength): raise HalotoolsError("``assembias_strength`` and ``assembias_strength_abscissa`` " "must have the same length") self._assembias_strength_abscissa = abscissa for ipar, val in enumerate(strength): self.param_dict[self._get_assembias_param_dict_key(ipar)] = val
def _set_percentile_splitting(self, split=0.5, **kwargs): """ Method interprets the arguments passed to the constructor and sets up the interpolation scheme for how halos will be divided into two types as a function of the primary halo property. """ if 'splitting_model' in kwargs: self.splitting_model = kwargs['splitting_model'] func = getattr(self.splitting_model, kwargs['splitting_method_name']) if isinstance(func, collections.Callable): self._input_split_func = func else: raise HalotoolsError("Input ``splitting_model`` must have a callable function " "named ``%s``" % kwargs['splitting_method_name']) elif 'split_abscissa' in list(kwargs.keys()): if custom_len(kwargs['split_abscissa']) != custom_len(split): raise HalotoolsError("``split`` and ``split_abscissa`` must have the same length") self._split_abscissa = kwargs['split_abscissa'] self._split_ordinates = split else: try: self._split_abscissa = [2] self._split_ordinates = [split] except KeyError: msg = ("The _set_percentile_splitting method must at least be called with a ``split``" "keyword argument, or alternatively ``split`` and ``split_abscissa`` arguments.") raise HalotoolsError(msg)
def _initialize_assembias_param_dict(self, assembias_strength=0.5, assembias_slope=1.0, **kwargs): r""" For full documentation, see the Heaviside Assembias Declaration in Halotools. This function calls the superclass's version. Then, it adds the parameters from disp_func to the dict as well. Parameters ---------- assembias_strength : float, optional Strength of assembias. Default is 1.0 for maximum strength assembias_slope : float, optional Effective slopes of disp_func. Can be an iterator of float. Uses the same abscissa as assembias_strength """ super(ContinuousAssembias, self)._initialize_assembias_param_dict( assembias_strength=assembias_strength, **kwargs) # Accept float or iterable slope = assembias_slope try: iterator = iter(slope) slope = list(slope) except TypeError: slope = [slope] # assert it has the proper length if custom_len(self._assembias_strength_abscissa) != custom_len(slope): raise HalotoolsError("``assembias_strength`` and ``assembias_slope`` " "must have the same length") for ipar, val in enumerate(slope): self.param_dict[self._get_continuous_assembias_param_dict_key(ipar)] = val
def _set_percentile_splitting(self, split=0.5, **kwargs): """ Interpret constructor arguments and set up interpolation scheme In this subclass, add "split" as a free parameter, similar to assembias. """ if not hasattr(self, 'param_dict'): self.param_dict = {} if 'splitting_model' in kwargs: #self.splitting_model = kwargs['splitting_model'] #func = getattr(self.splitting_model, kwargs['splitting_method_name']) #if isinstance(func, collections.Callable): # self._input_split_func = func #else: raise HalotoolsError( "Input ``splitting_model`` has not yet been implemented for the " "FreeSplitAssembias subclass.") # Make sure the code behaves properly whether or not we were passed an iterable try: iterator = iter(split) split = list(split) except TypeError: split = [split] if 'assembias_strength_abscissa' in kwargs: abscissa = kwargs['assembias_strength_abscissa'] try: iterator = iter(abscissa) abscissa = list(abscissa) except TypeError: abscissa = [abscissa] else: abscissa = [2] if custom_len(abscissa) != custom_len(split): raise HalotoolsError( "``assembias_strength`` and ``assembias_strength_abscissa`` " "must have the same length") self._split_abscissa = abscissa for ipar, val in enumerate(split): self.param_dict[self._get_free_split_assembias_param_dict_key( ipar)] = val
def assembias_mc_occupation(self, seed=None, **kwargs): first_occupation_moment_orig = self.mean_occupation_orig(**kwargs) first_occupation_moment = self.mean_occupation(**kwargs) if self._upper_occupation_bound == 1: with NumpyRNGContext(seed): score = np.random.rand( custom_len(first_occupation_moment_orig)) total = np.count_nonzero(first_occupation_moment_orig > score) result = np.where(first_occupation_moment > score, 1, 0) diff = result.sum() - total if diff < 0: x = (first_occupation_moment / score) result.fill(0) result[x.argsort()[-total:]] = 1 elif diff > 0: x = (1.0 - first_occupation_moment) / (1.0 - score) result.fill(0) result[x.argsort()[:total]] = 1 elif self._upper_occupation_bound == float("inf"): total = self._poisson_distribution( first_occupation_moment_orig.sum(), seed=seed) if seed is not None: seed += 1 with NumpyRNGContext(seed): score = np.random.rand(total) score.sort() x = first_occupation_moment.cumsum(dtype=np.float64) x /= x[-1] result = np.ediff1d(np.insert(np.searchsorted(score, x), 0, 0)) else: msg = ( "\nYou have chosen to set ``_upper_occupation_bound`` to some value \n" "besides 1 or infinity. In such cases, you must also \n" "write your own ``mc_occupation`` method that overrides the method in the \n" "OccupationComponent super-class\n") raise HalotoolsError(msg) if 'table' in kwargs: kwargs['table']['halo_num_' + self.gal_type] = result return result
def wrapper(*args, **kwargs): ################################################################################# # Retrieve the arrays storing prim_haloprop and sec_haloprop # The control flow below is what permits accepting an input # table or a directly inputting prim_haloprop and sec_haloprop arrays _HAS_table = False if 'table' in kwargs: try: table = kwargs['table'] prim_haloprop = table[self.prim_haloprop_key] sec_haloprop = table[self.sec_haloprop_key] _HAS_table = True except KeyError: msg = ("When passing an input ``table`` to the " " ``assembias_decorator`` method,\n" "the input table must have a column with name ``%s``" "and a column with name ``%s``.\n") raise HalotoolsError(msg % (self.prim_haloprop_key), self.sec_haloprop_key) else: try: prim_haloprop = np.atleast_1d(kwargs['prim_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``prim_haloprop`` argument.\n") raise HalotoolsError(msg) try: sec_haloprop = np.atleast_1d(kwargs['sec_haloprop']) except KeyError: if 'sec_haloprop_percentile' not in kwargs: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass either a ``sec_haloprop`` or " "``sec_haloprop_percentile`` argument.\n") raise HalotoolsError(msg) ################################################################################# # Compute the fraction of type-2 halos as a function of the input prim_haloprop split = self.percentile_splitting_function(prim_haloprop) # Compute the baseline, undecorated result result = func(*args, **kwargs) # We will only decorate values that are not edge cases, # so first compute the mask for non-edge cases no_edge_mask = ( (split > 0) & (split < 1) & (result > baseline_lower_bound) & (result < baseline_upper_bound) ) # Now create convenient references to the non-edge-case sub-arrays no_edge_result = result[no_edge_mask] no_edge_split = split[no_edge_mask] ################################################################################# # Compute the array type1_mask # This array will serve as a boolean mask that divides the halo sample into two subsamples # There are several possible ways that the type1_mask can be computed, depending on # what the decorator was passed as input if _HAS_table is True: # we were passed halo_type_tuple: if hasattr(self, 'halo_type_tuple'): halo_type_key = self.halo_type_tuple[0] halo_type1_val = self.halo_type_tuple[1] type1_mask = table[halo_type_key][no_edge_mask] == halo_type1_val # the value of sec_haloprop_percentile is already stored as a column of the table elif self.sec_haloprop_key + '_percentile' in list(table.keys()): no_edge_percentiles = table[self.sec_haloprop_key + '_percentile'][no_edge_mask] type1_mask = no_edge_percentiles > no_edge_split else: # the value of sec_haloprop_percentile will be computed from scratch percentiles = compute_conditional_percentiles( prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop ) no_edge_percentiles = percentiles[no_edge_mask] type1_mask = no_edge_percentiles > no_edge_split else: try: percentiles = kwargs['sec_haloprop_percentile'] if custom_len(percentiles) == 1: percentiles = np.zeros(custom_len(prim_haloprop)) + percentiles except KeyError: percentiles = compute_conditional_percentiles( prim_haloprop=prim_haloprop, sec_haloprop=sec_haloprop ) no_edge_percentiles = percentiles[no_edge_mask] type1_mask = no_edge_percentiles > no_edge_split # type1_mask has now been computed for all possible branchings ################################################################################# perturbation = self._galprop_perturbation( prim_haloprop=prim_haloprop[no_edge_mask], baseline_result=no_edge_result, splitting_result=no_edge_split) frac_type1 = 1 - no_edge_split frac_type2 = 1 - frac_type1 perturbation[~type1_mask] *= (-frac_type1[~type1_mask] / (frac_type2[~type1_mask])) no_edge_result += perturbation result[no_edge_mask] = no_edge_result return result
def wrapper(*args, **kwargs): ################################################################################# # Retrieve the arrays storing prim_haloprop and sec_haloprop # The control flow below is what permits accepting an input # table or a directly inputting prim_haloprop and sec_haloprop arrays _HAS_table = False if 'table' in kwargs: try: table = kwargs['table'] prim_haloprop = table[self.prim_haloprop_key] sec_haloprop = table[self.sec_haloprop_key] _HAS_table = True except KeyError: msg = ("When passing an input ``table`` to the " " ``assembias_decorator`` method,\n" "the input table must have a column with name ``%s``" "and a column with name ``%s``.\n") raise HalotoolsError(msg % (self.prim_haloprop_key), self.sec_haloprop_key) else: try: prim_haloprop = np.atleast_1d(kwargs['prim_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``prim_haloprop`` argument.\n") raise HalotoolsError(msg) try: sec_haloprop = np.atleast_1d(kwargs['sec_haloprop']) except KeyError: msg = ("\nIf not passing an input ``table`` to the " "``assembias_decorator`` method,\n" "you must pass ``sec_haloprop`` argument") raise HalotoolsError(msg) ################################################################################# # Compute the percentile to split on as a function of the input prim_haloprop split = self.percentile_splitting_function(prim_haloprop) # Compute the baseline, undecorated result result = func(*args, **kwargs) # We will only decorate values that are not edge cases, # so first compute the mask for non-edge cases no_edge_mask = ( (split > 0) & (split < 1) & (result > baseline_lower_bound) & (result < baseline_upper_bound) ) # Now create convenient references to the non-edge-case sub-arrays no_edge_result = result[no_edge_mask] no_edge_split = split[no_edge_mask] # Retrieve percentile values (medians) if they've been precomputed. Else, compute them. if _HAS_table is True: if self.sec_haloprop_key + '_percentile_values' in table.keys(): no_edge_percentile_values = table[self.sec_haloprop_key + '_percentile_value'][no_edge_mask] else: # the value of sec_haloprop_percentile will be computed from scratch no_edge_percentile_values = compute_conditional_percentile_values( p=no_edge_split, prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask] ) else: try: percentiles = kwargs['sec_haloprop_percentile_values'] if custom_len(percentiles) == 1: percentiles = np.zeros(custom_len(prim_haloprop)) + percentiles no_edge_percentile_values = percentiles[no_edge_mask] except KeyError: no_edge_percentile_values = compute_conditional_percentile_values(p=no_edge_split, prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask] ) # NOTE I've removed the type 1 mask as it is not well-defined in this implementation # this has all been rolled into the galprop_perturbation function # normalize by max value away from percentile # This ensures that the "slope" definition and boundaries are universal pv_sub_sec_haloprop = sec_haloprop[no_edge_mask] - no_edge_percentile_values if prim_haloprop[no_edge_mask].shape[0] == 0: perturbation = np.zeros_like(no_edge_result) else: perturbation = self._galprop_perturbation( prim_haloprop=prim_haloprop[no_edge_mask], sec_haloprop=pv_sub_sec_haloprop/np.max(np.abs(pv_sub_sec_haloprop)), #sec_haloprop = compute_conditional_percentiles(prim_haloprop = prim_haloprop[no_edge_mask], sec_haloprop=sec_haloprop[no_edge_mask])-no_edge_split, baseline_result=no_edge_result) no_edge_result += perturbation result[no_edge_mask] = no_edge_result return result