def legendre_integral(limits: ztyping.SpaceType, norm_range: ztyping.SpaceType, params: List["zfit.Parameter"], model: RecursivePolynomial): """Recursive integral of Legendre polynomials""" lower, upper = limits.limit1d lower_rescaled = model._polynomials_rescale(lower) upper_rescaled = model._polynomials_rescale(upper) # if np.allclose((lower_rescaled, upper_rescaled), (-1, 1)): # return ztf.constant(2.) # lower = ztf.convert_to_tensor(lower_rescaled) upper = ztf.convert_to_tensor(upper_rescaled) integral_0 = model.params[f"c_0"] * (upper - lower) # if polynomial 0 is 1 if model.degree == 0: integral = integral_0 else: def indefinite_integral(limits): max_degree = model.degree + 1 # needed +1 for integral, max poly in term for n is n+1 polys = do_recurrence(x=limits, polys=legendre_polys, degree=max_degree, recurrence=legendre_recurrence) one_limit_integrals = [] for degree in range(1, max_degree): coeff = model.params[f"c_{degree}"] one_limit_integrals.append( coeff * (polys[degree + 1] - polys[degree - 1]) / (2. * (ztf.convert_to_tensor(degree)) + 1)) return ztf.reduce_sum(one_limit_integrals, axis=0) integral = indefinite_integral(upper) - indefinite_integral( lower) + integral_0 integral = tf.reshape(integral, shape=()) integral *= 0.5 * model.space.area() # rescale back to whole width return integral
def __call__(self, n_to_produce: Union[int, tf.Tensor], limits: Space, dtype): rnd_samples = [] thresholds_unscaled_list = [] weights = tf.broadcast_to(ztf.constant(1., shape=(1, )), shape=(n_to_produce, )) for (lower, upper), area in zip(limits.iter_limits(as_tuple=True), limits.iter_areas(rel=True)): n_partial_to_produce = tf.cast( ztf.to_real(n_to_produce) * ztf.to_real(area), dtype=tf.int32) # TODO(Mayou36): split right! lower = ztf.convert_to_tensor(lower, dtype=dtype) upper = ztf.convert_to_tensor(upper, dtype=dtype) if isinstance(limits, EventSpace): lower = tf.transpose(a=lower) upper = tf.transpose(a=upper) sample_drawn = tf.random.uniform( shape=(n_partial_to_produce, limits.n_obs + 1), # + 1 dim for the function value dtype=ztypes.float) rnd_sample = sample_drawn[:, :-1] * ( upper - lower) + lower # -1: all except func value thresholds_unscaled = sample_drawn[:, -1] # if not multiple_limits: # return rnd_sample, thresholds_unscaled rnd_samples.append(rnd_sample) thresholds_unscaled_list.append(thresholds_unscaled) rnd_sample = tf.concat(rnd_samples, axis=0) thresholds_unscaled = tf.concat(thresholds_unscaled_list, axis=0) n_drawn = n_to_produce return rnd_sample, thresholds_unscaled, weights, weights, n_drawn
def func_integral_laguerre(limits, norm_range, params: Dict, model): """The integral of the simple laguerre polynomials. Defined as :math:`\int L_{n} = (-1) L_{n+1}^{(-1)}` with :math:`L^{(\alpha)}` the generalized Laguerre polynom. Args: limits: norm_range: params: model: Returns: """ lower, upper = limits.limit1d lower_rescaled = model._polynomials_rescale(lower) upper_rescaled = model._polynomials_rescale(upper) lower = ztf.convert_to_tensor(lower_rescaled) upper = ztf.convert_to_tensor(upper_rescaled) # The laguerre shape makes the sum for us. setting the 0th coeff to 0, since no -1 term exists. coeffs_laguerre_nup = { f'c_{int(n.split("_", 1)[-1]) + 1}': c for i, (n, c) in enumerate(params.items()) } # increase n -> n+1 of naming coeffs_laguerre_nup['c_0'] = tf.constant(0., dtype=model.dtype) coeffs_laguerre_nup = convert_coeffs_dict_to_list(coeffs_laguerre_nup) def indefinite_integral(limits): return -1 * laguerre_shape_alpha_minusone(x=limits, coeffs=coeffs_laguerre_nup) integral = indefinite_integral(upper) - indefinite_integral(lower) integral = tf.reshape(integral, shape=()) integral *= 0.5 * model.space.area() # rescale back to whole width return integral
def indefinite_integral(limits): max_degree = model.degree + 1 polys = do_recurrence(x=limits, polys=chebyshev_polys, degree=max_degree, recurrence=chebyshev_recurrence) one_limit_integrals = [] for degree in range(2, max_degree): coeff = model.params[f"c_{degree}"] n_float = ztf.convert_to_tensor(degree) integral = (n_float * polys[degree + 1] / (ztf.square(n_float) - 1) - limits * polys[degree] / (n_float - 1)) one_limit_integrals.append(coeff * integral) return ztf.reduce_sum(one_limit_integrals, axis=0)
def test_analytic_integral(): class DistFunc3(zbasepdf.BasePDF): def _unnormalized_pdf(self, x, norm_range=False): return func3_2deps(x) mu_true = 1.4 sigma_true = 1.8 limits = -4.3, 1.9 mu = Parameter("mu_1414", mu_true, mu_true - 2., mu_true + 7.) sigma = Parameter("sigma_1414", sigma_true, sigma_true - 10., sigma_true + 5.) gauss_params1 = CustomGaussOLD(mu=mu, sigma=sigma, obs=obs1, name="gauss_params1") normal_params1 = Gauss(mu=mu, sigma=sigma, obs=obs1, name="gauss_params1") try: infinity = mt.inf except AttributeError: # py34 infinity = float('inf') gauss_integral_infs = gauss_params1.integrate(limits=(-infinity, infinity), norm_range=False) normal_integral_infs = normal_params1.integrate(limits=(-infinity, infinity), norm_range=False) DistFunc3.register_analytic_integral(func=func3_2deps_fully_integrated, limits=Space.from_axes(limits=limits3, axes=(0, 1))) dist_func3 = DistFunc3(obs=['obs1', 'obs2']) gauss_integral_infs = zfit.run(gauss_integral_infs) normal_integral_infs = zfit.run(normal_integral_infs) func3_integrated = zfit.run( ztf.convert_to_tensor(dist_func3.integrate(limits=Space.from_axes( limits=limits3, axes=(0, 1)), norm_range=False), dtype=tf.float64)) assert func3_integrated == func3_2deps_fully_integrated( limits=Space.from_axes(limits=limits3, axes=(0, 1))) assert gauss_integral_infs == pytest.approx(np.sqrt(np.pi * 2.) * sigma_true, rel=0.0001) assert normal_integral_infs == pytest.approx(1, rel=0.0001)
def __init__(self, params: Dict[str, ZfitParameter], distribution: tfd.Distribution, dist_params, dist_kwargs=None, name: str = "DistributionConstraint", dtype=ztypes.float, **kwargs): """ Base class for constraints using a probability density function. Args: distribution (`tensorflow_probability.distributions.Distribution`): The probability density function used to constraint the parameters """ super().__init__(params=params, name=name, dtype=dtype, **kwargs) self._distribution = distribution self.dist_params = dist_params self.dist_kwargs = dist_kwargs if dist_kwargs is not None else {} self._tparams = ztf.convert_to_tensor(self.get_params())
def set_weights(self, weights: ztyping.WeightsInputType): """Set (temporarily) the weights of the dataset. Args: weights (`tf.Tensor`, np.ndarray, None): """ if weights is not None: weights = ztf.convert_to_tensor(weights) weights = ztf.to_real(weights) if weights.shape.ndims != 1: raise ShapeIncompatibleError( "Weights have to be 1-Dim objects.") def setter(value): self._weights = value def getter(): return self.weights return TemporarilySet(value=weights, getter=getter, setter=setter)
def step_size(self, value): if value is not None: value = ztf.convert_to_tensor(value) self._step_size = value
def loss_func(): probs = ztf.convert_to_tensor((a_param - true_a) ** 2 + (b_param - true_b) ** 2 + (c_param - true_c) ** 4) + 0.42 return tf.reduce_sum(tf.log(probs))
def __init__(self, tensor): if not isinstance(tensor, tf.Tensor): tensor = ztf.convert_to_tensor(tensor) self.tensor = tensor
def mc_integrate(func: Callable, limits: ztyping.LimitsType, axes: Optional[ztyping.AxesTypeInput] = None, x: Optional[ztyping.XType] = None, n_axes: Optional[int] = None, draws_per_dim: int = 20000, method: str = None, dtype: Type = ztypes.float, mc_sampler: Callable = tfp.mcmc.sample_halton_sequence, importance_sampling: Optional[Callable] = None) -> tf.Tensor: """Monte Carlo integration of `func` over `limits`. Args: func (callable): The function to be integrated over limits (:py:class:`~zfit.Space`): The limits of the integral axes (tuple(int)): The row to integrate over. None means integration over all value x (numeric): If a partial integration is performed, this are the value where x will be evaluated. n_axes (int): the number of total dimensions (old?) draws_per_dim (int): How many random points to draw per dimensions method (str): Which integration method to use dtype (dtype): |dtype_arg_descr| mc_sampler (callable): A function that takes one argument (`n_draws` or similar) and returns random value between 0 and 1. importance_sampling (): Returns: numerical: the integral """ if axes is not None and n_axes is not None: raise ValueError("Either specify axes or n_axes") limits = convert_to_space(limits) axes = limits.axes partial = (axes is not None) and (x is not None ) # axes, value can be tensors if axes is not None and n_axes is None: n_axes = len(axes) if n_axes is not None and axes is None: axes = tuple(range(n_axes)) lower, upper = limits.limits if np.infty in upper[0] or -np.infty in lower[0]: raise ValueError( "MC integration does (currently) not support unbound limits (np.infty) as given here:" "\nlower: {}, upper: {}".format(lower, upper)) lower = ztf.convert_to_tensor(lower, dtype=dtype) upper = ztf.convert_to_tensor(upper, dtype=dtype) n_samples = draws_per_dim chunked_normalization = zfit.run.chunksize < n_samples # chunked_normalization = True if chunked_normalization: if partial: raise DueToLazynessNotImplementedError( "This feature is not yet implemented: needs new Datasets") n_chunks = int(np.ceil(n_samples / zfit.run.chunksize)) chunksize = int(np.ceil(n_samples / n_chunks)) print("starting normalization with {} chunks and a chunksize of {}". format(n_chunks, chunksize)) avg = normalization_chunked(func=func, n_axes=n_axes, dtype=dtype, x=x, num_batches=n_chunks, batch_size=chunksize, space=limits) else: # TODO: deal with n_obs properly? samples_normed = mc_sampler(dim=n_axes, num_results=n_samples, dtype=dtype) # samples_normed = tf.reshape(samples_normed, shape=(n_vals, int(n_samples / n_vals), n_axes)) # samples_normed = tf.expand_dims(samples_normed, axis=0) samples = samples_normed * ( upper - lower) + lower # samples is [0, 1], stretch it # samples = tf.transpose(samples, perm=[2, 0, 1]) if partial: # TODO(Mayou36): shape of partial integral? data_obs = x.obs new_obs = [] x = x.value() value_list = [] index_samples = 0 index_values = 0 if len(x.shape) == 1: x = tf.expand_dims(x, axis=1) for i in range(n_axes + x.shape[-1].value): if i in axes: new_obs.append(limits.obs[index_samples]) value_list.append(samples[:, index_samples]) index_samples += 1 else: new_obs.append(data_obs[index_values]) value_list.append( tf.expand_dims(x[:, index_values], axis=1)) index_values += 1 value_list = [tf.cast(val, dtype=dtype) for val in value_list] x = value_list x = PartialIntegralSampleData(sample=value_list, space=Space(obs=new_obs)) else: x = samples # convert rnd samples with value to feedable vector reduce_axis = 1 if partial else None avg = tf.reduce_mean(func(x), axis=reduce_axis) # avg = tfp.monte_carlo.expectation(f=func, samples=x, axis=reduce_axis) # TODO: importance sampling? # avg = tfb.monte_carlo.expectation_importance_sampler(f=func, samples=value,axis=reduce_axis) integral = avg * tf.cast(ztf.convert_to_tensor(limits.area()), dtype=avg.dtype) return integral
def __init__(self, data: tf.Tensor, bandwidth: ztyping.ParamTypeInput, obs: ztyping.ObsTypeInput, name: str = "GaussianKDE"): """Gaussian Kernel Density Estimation using Silverman's rule of thumb Args: data: Data points to build a kernel around bandwidth: sigmas for the covariance matrix of the multivariate gaussian obs: name: Name of the PDF """ dtype = zfit.settings.ztypes.float if isinstance(data, zfit.core.interfaces.ZfitData): raise WorkInProgressError("Currently, no dataset supported yet") # size = data.nevents # dims = data.n_obs # with data. # data = data.value() # if data.weights is not None: else: if not isinstance(data, tf.Tensor): data = ztf.convert_to_tensor(value=data) data = ztf.to_real(data) shape_data = tf.shape(data) size = tf.cast(shape_data[0], dtype=dtype) dims = tf.cast(shape_data[-1], dtype=dtype) bandwidth = convert_to_container(bandwidth) # Bandwidth definition, use silverman's rule of thumb for nd def reshaped_kerner_factory(): cov = tf.linalg.diag([ tf.square((4. / (dims + 2.))**(1 / (dims + 4)) * size**(-1 / (dims + 4)) * s) for s in bandwidth ]) # kernel prob output shape: (n,) kernel = tfd.MultivariateNormalFullCovariance( loc=data, covariance_matrix=cov) return tfd.Independent(kernel) # reshaped_kernel = kernel probs = tf.broadcast_to(1 / size, shape=(tf.cast(size, tf.int32), )) categorical = tfd.Categorical( probs=probs) # no grad -> no need to recreate dist_kwargs = lambda: dict(mixture_distribution=categorical, components_distribution= reshaped_kerner_factory()) distribution = tfd.MixtureSameFamily # TODO lambda for params params = OrderedDict( (f"bandwidth_{i}", h) for i, h in enumerate(bandwidth)) super().__init__(distribution=distribution, dist_params={}, dist_kwargs=dist_kwargs, params=params, obs=obs, name=name)
def normalization_nograd(func, n_axes, batch_size, num_batches, dtype, space, x=None, shape_after=()): upper, lower = space.limits lower = ztf.convert_to_tensor(lower, dtype=dtype) upper = ztf.convert_to_tensor(upper, dtype=dtype) def body(batch_num, mean): start_idx = batch_num * batch_size end_idx = start_idx + batch_size indices = tf.range(start_idx, end_idx, dtype=tf.int32) samples_normed = tfp.mcmc.sample_halton_sequence( n_axes, # num_results=batch_size, sequence_indices=indices, dtype=dtype, randomized=False) # halton_sample = tf.random_uniform(shape=(n_axes, batch_size), dtype=dtype) samples_normed.set_shape((batch_size, n_axes)) samples_normed = tf.expand_dims(samples_normed, axis=0) samples = samples_normed * (upper - lower) + lower func_vals = func(samples) if shape_after == (): reduce_axis = None else: reduce_axis = 1 if len(func_vals.shape) == 1: func_vals = tf.expand_dims(func_vals, -1) batch_mean = tf.reduce_mean(func_vals, axis=reduce_axis) # if there are gradients # batch_mean = tf.reduce_mean(sample) # batch_mean = tf.guarantee_const(batch_mean) # with tf.control_dependencies([batch_mean]): err_weight = 1 / tf.to_double(batch_num + 1) # err_weight /= err_weight + 1 # print_op = tf.print(batch_mean) do_print = False if do_print: deps = [tf.print(batch_num + 1)] else: deps = [] with tf.control_dependencies(deps): return batch_num + 1, mean + err_weight * (batch_mean - mean) cond = lambda batch_num, _: batch_num < num_batches initial_mean = tf.constant(0, shape=shape_after, dtype=dtype) initial_body_args = (0, initial_mean) _, final_mean = tf.while_loop(cond, body, initial_body_args, parallel_iterations=1, swap_memory=False, back_prop=True) # def normalization_grad(x): return final_mean
def step_size(self, value): if value is not None: value = ztf.convert_to_tensor(value, preferred_dtype=ztypes.float) value = tf.cast(value, dtype=ztypes.float) self._step_size = value
return mu2, sigma2 def create_params3(nameadd=""): mu3 = zfit.Parameter("mu35" + nameadd, ztf.to_real(mu_true) - 0.2, mu_true - 1., mu_true + 1.) sigma3 = zfit.Parameter("sigma35" + nameadd, ztf.to_real(sigma_true) - 0.3, sigma_true - 2., sigma_true + 2.) yield3 = zfit.Parameter("yield35" + nameadd, yield_true + 300, 0, yield_true + 20000) return mu3, sigma3, yield3 obs1 = 'obs1' mu_constr = [1.6, 0.2] # mu, sigma sigma_constr = [3.8, 0.2] constr = lambda: [mu_constr[1], sigma_constr[1]] constr_tf = lambda: ztf.convert_to_tensor(constr()) covariance = lambda: np.array([[mu_constr[1] ** 0.5, -0.05], [-0.05, sigma_constr[1] ** 0.5]]) covariance_tf = lambda: ztf.convert_to_tensor(covariance()) def create_gauss1(): mu, sigma = create_params1() return Gauss(mu, sigma, obs=obs1, name="gaussian1"), mu, sigma def create_gauss2(): mu, sigma = create_params2() return Gauss(mu, sigma, obs=obs1, name="gaussian2"), mu, sigma def create_gauss3ext():