Source code for n3fit.layers.preprocessing

from typing import List, Optional

from n3fit.backends import MetaLayer, MultiInitializer, constraints
from n3fit.backends import operations as op


[docs]class Preprocessing(MetaLayer): """ Computes preprocessing factor for the PDF. This layer generates a factor (1-x)^beta*x^(1-alpha) where both beta and alpha are model paramters that can be trained. If feature scaling is used, the preprocessing factor is x^(1-alpha). Alpha is initialized uniformly within the ranges allowed in the runcard and then it is only allowed to move between those two values (with a hard wall in each side) Alpha and, unless feature scaling is used, beta are initialized uniformly within the ranges allowed in the runcard and then they are only allowed to move between those two values (with a hard wall in each side) Parameters ---------- replica_seeds: List[int] list of pre replica seeds for the initializer of the random alpha and beta values flav_info: list list of dicts containing the information about the fitting of the preprocessing factor This corresponds to the `fitting::basis` parameter in the nnpdf runcard. The dicts can contain the following fields: `smallx`: range of alpha `largex`: range of beta `trainable`: whether these alpha-beta should be trained during the fit (defaults to true) large_x: bool Whether large x preprocessing factor should be active """ def __init__( self, replica_seeds: List[int], flav_info: Optional[list] = None, large_x: bool = True, **kwargs, ): if flav_info is None: raise ValueError( "Trying to instantiate a preprocessing factor with no basis information" ) self.flav_info = flav_info # This variable contains the next seed to be used to generate weights self._replica_seeds = replica_seeds self.large_x = large_x self.num_replicas = len(replica_seeds) self.alphas = [] self.betas = [] super().__init__(**kwargs)
[docs] def generate_weight(self, name: str, kind: str, dictionary: dict, set_to_zero: bool = False): """ Generates weights according to the flavour dictionary Parameters ---------- name: str name to be given to the generated weight kind: str where to find the limits of the weight in the dictionary dictionary: dict dictionary defining the weight, usually one entry from `flav_info` set_to_zero: bool set the weight to constant 0 """ constraint = None if set_to_zero: single_replica_initializer = MetaLayer.init_constant(0.0) trainable = False else: minval, maxval = dictionary[kind] trainable = dictionary.get("trainable", True) # Seeds will be set in the wrapper MultiInitializer single_replica_initializer = MetaLayer.select_initializer( "random_uniform", minval=minval, maxval=maxval ) # If we are training, constrain the weights to be within the limits if trainable: constraint = constraints.MinMaxWeight(minval, maxval) initializer = MultiInitializer(single_replica_initializer, self._replica_seeds, base_seed=0) # increment seeds for the next coefficient self._replica_seeds = [seed + 1 for seed in self._replica_seeds] # Generate the new trainable (or not) parameter newpar = self.builder_helper( name=name, kernel_shape=(self.num_replicas, 1), initializer=initializer, trainable=trainable, constraint=constraint, ) return newpar
[docs] def build(self, input_shape): # Run through the whole basis for flav_dict in self.flav_info: flav_name = flav_dict["fl"] alpha_name = f"alpha_{flav_name}" self.alphas.append(self.generate_weight(alpha_name, "smallx", flav_dict)) beta_name = f"beta_{flav_name}" self.betas.append( self.generate_weight(beta_name, "largex", flav_dict, set_to_zero=not self.large_x) ) super(Preprocessing, self).build(input_shape)
[docs] def call(self, x): """ Compute preprocessing prefactor. Parameters ---------- x: tensor(shape=[1,N,1]) Returns ------- prefactor: tensor(shape=[1,R,N,F]) """ # weight tensors of shape (R, 1, F) alphas = op.stack(self.alphas, axis=-1) betas = op.stack(self.betas, axis=-1) x = op.batchit(x, batch_dimension=0) return x ** (1 - alphas) * (1 - x) ** betas