def __init_spline(self, knots, coeffs, deg, units=None): if not isinstance(knots, ureg.Quantity): if units is None: knots = knots * ureg.dimensionless else: knots = ureg.Quantity(np.asarray(knots), units) self._state_attrs.extend(['knots', 'coeffs', 'deg', 'units']) self.kind = 'spline' if isinstance(knots, ureg.Quantity): self.units = str(knots.units) self.knots = knots self.coeffs = coeffs self.deg = deg def llh(x): x = self.__strip(self.__convert(x)) return splev(x, tck=(self.__strip(self.knots), coeffs, deg), ext=2) self.llh = llh self.max_at = fminbound( func=self.__attach_units_to_args(self.chi2), x1=np.min(self.__strip(self.knots)), x2=np.max(self.__strip(self.knots)), ) if self.units is not None: self.max_at = self.max_at * ureg(self.units) self.max_at_str = self.__stringify(self.max_at) self.valid_range = (np.min(self.knots) * ureg(self.units), np.max(self.knots) * ureg(self.units)) self._str = lambda s: 'spline prior: deg=%d, valid in [%s, %s]%s; max at %s%s' \ %(self.deg, self.__stringify(np.min(self.knots)), self.__stringify(np.max(self.knots)), self.units_str, self.max_at_str, self.units_str)
def __init_spline(self, knots, coeffs, deg, units=None): knots = interpret_quantity(knots, expect_sequence=True) self._state_attrs.extend(['knots', 'coeffs', 'deg']) self.kind = 'spline' if isunitless(knots): knots = ureg.Quantity(knots, units) elif units is not None: units = ureg.Unit(units) assert knots.dimensionality == units.dimensionality knots = knots.to(units) self.units = str(knots.units) self.knots = knots self.coeffs = coeffs self.deg = deg def llh(x): x = self.__strip(self.__convert(x)) return splev(x, tck=(self.__strip(self.knots), coeffs, deg), ext=2) self.llh = llh self.max_at = fminbound( func=self.__attach_units_to_args(self.chi2), x1=np.min(self.__strip(self.knots)), x2=np.max(self.__strip(self.knots)), ) if self.units is not None: self.max_at = self.max_at * ureg(self.units) self.max_at_str = self.__stringify(self.max_at) self.valid_range = (np.min(self.knots) * ureg(self.units), np.max(self.knots) * ureg(self.units)) self._str = lambda s: 'spline prior: deg=%d, valid in [%s, %s]%s; max at %s%s' \ %(self.deg, self.__stringify(np.min(self.knots)), self.__stringify(np.max(self.knots)), self.units_str, self.max_at_str, self.units_str)
def parse_param(config, section, selector, fullname, pname, value): """Parse a param specification from a PISA config file. Note that if the param sepcification does not include ``fixed``, ``prior``, and/or ``range``, the defaults for these are: ``fixed = True``, ``prior = None``, and ``range = None``. If a prior is specified explicitly via ``.prior``, this takes precendence, but if no ``.prior`` is specified and the param's value is parsed to be a :class:`uncertainties.AffineScalarFunc` (i.e. have `std_dev` attribute), a Gaussian prior is constructed from that and then the AffineScalarFunc is stripped out of the param's value (such that it is just a :class:`~pint.quantity.Quantity`). Parameters ---------- config : pisa.utils.config_parser.PISAConfigParser section : string selector : string or None fullname : string pname : string value : string Returns ------- param : pisa.core.param.Param """ # Note: imports placed here to avoid circular imports from pisa.core.param import Param from pisa.core.prior import Prior kwargs = dict(name=pname, is_fixed=True, prior=None, range=None) try: value = parse_quantity(value) kwargs['value'] = value.nominal_value * value.units except ValueError: value = parse_string_literal(value) kwargs['value'] = value # Search for explicit attr specifications if config.has_option(section, fullname + '.fixed'): kwargs['is_fixed'] = config.getboolean(section, fullname + '.fixed') if config.has_option(section, fullname + '.unique_id'): kwargs['unique_id'] = config.get(section, fullname + '.unique_id') if config.has_option(section, fullname + '.range'): range_ = config.get(section, fullname + '.range') # Note: `nominal` and `sigma` are called out in the `range_` string if 'nominal' in range_: nominal = value.n * value.units # pylint: disable=unused-variable if 'sigma' in range_: sigma = value.s * value.units # pylint: disable=unused-variable range_ = range_.replace('[', 'np.array([') range_ = range_.replace(']', '])') # Strip out uncertainties from value itself (as we will rely on the # prior from here on out) kwargs['range'] = eval(range_).to(value.units) # pylint: disable=eval-used if config.has_option(section, fullname + '.prior'): prior = str(config.get(section, fullname + '.prior')).strip().lower() if prior == 'uniform': kwargs['prior'] = Prior(kind='uniform') elif prior == 'jeffreys': kwargs['prior'] = Prior(kind='jeffreys', A=kwargs['range'][0], B=kwargs['range'][1]) elif prior == 'spline': priorname = pname if selector is not None: priorname += '_' + selector data = config.get(section, fullname + '.prior.data') data = from_file(data) data = data[priorname] knots = ureg.Quantity(np.asarray(data['knots']), data['units']) knots = knots.to(value.units) coeffs = np.asarray(data['coeffs']) deg = data['deg'] kwargs['prior'] = Prior(kind='spline', knots=knots, coeffs=coeffs, deg=deg) elif prior == 'none': kwargs['prior'] = None elif 'gauss' in prior: raise Exception('Please use new style +/- notation for gaussian' ' priors in config') else: raise Exception('Prior type unknown') elif hasattr(value, 'std_dev') and value.std_dev != 0: kwargs['prior'] = Prior(kind='gaussian', mean=value.nominal_value * value.units, stddev=value.std_dev * value.units) # Strip out any uncertainties from value itself (an explicit ``.prior`` # specification takes precedence over this) if hasattr(value, 'std_dev'): value = value.nominal_value * value.units try: param = Param(**kwargs) except: logging.error('Failed to instantiate new Param object with kwargs %s', kwargs) raise return param
def get_prior_bounds(obj, param=None, stddev=1.0): """Obtain confidence regions for CL corresponding to given number of stddevs from parameter prior. Parameters ---------- obj : string or Mapping if str, interpret as path from which to load a dict if dict, can be: template settings dict; must supply `param` to choose which to plot params dict; must supply `param` to choose which to plot prior dict param : Param Name of param for which to get bounds; necessary if obj is either template settings or params stddev : float or Iterable of floats number of stddevs Returns ------- bounds : OrderedDict A dictionary mapping the passed `stddev` values to the corresponding bounds """ if isbarenumeric(stddev): stddev = [stddev] elif isinstance(stddev, Iterable): stddev = list(stddev) bounds = OrderedDict() for s in stddev: bounds[s] = [] if isinstance(obj, basestring): obj = from_file(obj) if 'params' in obj: obj = obj['params'] if param is not None and param in obj: obj = obj[param] if 'prior' in obj: obj = obj['prior'] prior = Prior(**obj) logging.debug('Getting confidence region from prior: %s', prior) x0 = prior.valid_range[0] x1 = prior.valid_range[1] x = ureg.Quantity(np.linspace(x0, x1, 10000), prior.units) chi2 = prior.chi2(x) for (i, xval) in enumerate(x[:-1]): for s in stddev: chi2_level = s**2 if chi2[i] > chi2_level and chi2[i + 1] < chi2_level: bounds[s].append(xval) elif chi2[i] < chi2_level and chi2[i + 1] > chi2_level: bounds[s].append(x[i + 1]) return bounds
def plot_prior(obj, param=None, x_xform=None, ax1=None, ax2=None, **plt_kwargs): """Plot prior for param from template settings, params, or prior filename or dict. Arguments --------- obj : str or dict if str, interpret as path from which to load a dict if (nested) dict, (innermost) must be dict of prior properties : either supply `param` to choose which parameter's prior in `obj` to plot, or prior dict, in which case `param` need not be specified param Param name to plot; necessary if obj is either pipeline settings or params dict x_xform Transform to apply to x-values. E.g., to plot against sin^2 theta, use x_xform = lambda x: np.sin(x)**2 ax1, ax2 Axes onto which to plot LLH and chi-squared, respectively. If none are provided, new figures & axes will be created. plt_kwargs Keyword arguments to pass on to the plot function Returns ------- ax1, ax2 The axes onto which plots were drawn (ax1 = LLH, ax2 = chi^2) """ import matplotlib as mpl mpl.use('pdf') import matplotlib.pyplot as plt if isinstance(obj, basestring): obj = from_file(obj) if param is not None and param in obj: obj = obj[param] if 'prior' in obj: obj = obj['prior'] prior = Prior(**obj) logging.info('Plotting Prior: %s', prior) x0 = prior.valid_range[0] x1 = prior.valid_range[1] if prior.kind == 'gaussian': x0 = max(x0, prior.max_at - 5 * prior.stddev) x1 = min(x1, prior.max_at + 5 * prior.stddev) if np.isinf(x0): x0 = -1 if np.isinf(x1): x1 = +1 # if prior.units is None, will result in dimensionless quantity x = ureg.Quantity(np.linspace(x0, x1, 5000), prior.units) llh = prior.llh(x) chi2 = prior.chi2(x) if x_xform is not None: x = x_xform(x) if ax1 is None: f = plt.figure() ax1 = f.add_subplot(111) if ax2 is None: f = plt.figure() ax2 = f.add_subplot(111) ax1.plot(x, llh, **plt_kwargs) ax2.plot(x, chi2, **plt_kwargs) ax1.set_title(str(prior), fontsize=8, y=1.02) ax2.set_title(str(prior), fontsize=8, y=1.02) ax1.set_xlabel(param) ax2.set_xlabel(param) ax1.set_ylabel('LLH') ax2.set_ylabel(r'$\Delta\chi^2$') return ax1, ax2