From fc6bb946c4ada48e143bfd4ba638cfaf7a5a8c40 Mon Sep 17 00:00:00 2001 From: wilsonm Date: Wed, 3 Feb 2021 16:28:49 +0000 Subject: [PATCH 01/16] duplicate the parts of n3fit.io.reader which get called by fit, but transform into validphys style providers. --- n3fit/src/n3fit/io/reader_providers.py | 279 +++++++++++++++++++++++++ n3fit/src/n3fit/performfit.py | 56 ++--- n3fit/src/n3fit/scripts/n3fit_exec.py | 21 +- 3 files changed, 308 insertions(+), 48 deletions(-) create mode 100644 n3fit/src/n3fit/io/reader_providers.py diff --git a/n3fit/src/n3fit/io/reader_providers.py b/n3fit/src/n3fit/io/reader_providers.py new file mode 100644 index 0000000000..9d928395eb --- /dev/null +++ b/n3fit/src/n3fit/io/reader_providers.py @@ -0,0 +1,279 @@ +""" +Bridges between NNPDF objects and providers which can return python +objects required in ``n3fit.performfit`` + +""" +from collections import defaultdict, namedtuple +from copy import deepcopy +import hashlib +import logging + +import numpy as np + +from NNPDF import RandomGenerator +from reportengine import collect + +from n3fit.io.reader import common_data_reader_experiment + +log = logging.getLogger() + +def replica_trvlseed(replica, trvlseed): + """Generates the ``trvlseed`` for a ``replica``.""" + # TODO: move to the new infrastructure + # https://numpy.org/doc/stable/reference/random/index.html#introduction + np.random.seed(seed=trvlseed) + for _ in range(replica): + res = np.random.randint(0, pow(2, 31)) + return res + +def replica_nnseed(replica, nnseed): + """Generates the ``nnseed`` for a ``replica``.""" + np.random.seed(seed=nnseed) + for _ in range(replica): + res = np.random.randint(0, pow(2, 31)) + return res + +def replica_mcseed(replica, mcseed, genrep): + """Generates the ``mcseed`` for a ``replica``.""" + if not genrep: + return None + np.random.seed(seed=mcseed) + for _ in range(replica): + res = np.random.randint(0, pow(2, 31)) + return res + +replicas_nnseed = collect("replica_nnseed", ("replicas",)) + +TrVlMasks = namedtuple( + "TrVlMasks", + ( + "dataset_inputs_tr_mask", + "dataset_inputs_vl_mask" + ) +) + +def tr_vl_masks(data, replica_trvlseed): + """Generate the boolean masks used to split data into training and + validation points. + + """ + nameseed = int(hashlib.sha256(str(data).encode()).hexdigest(), 16) % 10 ** 8 + nameseed += replica_trvlseed + # TODO: update this to new random infrastructure. + np.random.seed(nameseed) + trmask_partial = [] + vlmask_partial = [] + for dataset in data.datasets: + # TODO: python commondata will not require this rubbish. + # all data if cuts are None + cuts = dataset.cuts + ndata = len(cuts.load()) if cuts else dataset.commondata.ndata + frac = dataset.frac + trmax = int(frac * ndata) + mask = np.concatenate( + [np.ones(trmax, dtype=np.bool), np.zeros(ndata - trmax, dtype=np.bool)] + ) + np.random.shuffle(mask) + vl_mask = ~mask + trmask_partial.append(mask) + vlmask_partial.append(vl_mask) + return TrVlMasks(trmask_partial, vlmask_partial) + +def kfold_masks(kpartitions, data): + """Collect the masks (if any) due to kfolding for this data. + These will be applied to the experimental data before starting + the training of each fold. + """ + list_folds = [] + if kpartitions is not None: + for partition in kpartitions: + data_fold = partition.get("datasets", []) + mask = [] + for dataset in data.datasets: + # TODO: python commondata will not require this rubbish. + # all data if cuts are None + cuts = dataset.cuts + ndata = len(cuts.load()) if cuts else dataset.commondata.ndata + # If the dataset is in the fold, its mask is full of 0s + if str(dataset) in data_fold: + mask.append(np.zeros(ndata, dtype=np.bool)) + # otherwise of ones + else: + mask.append(np.ones(ndata, dtype=np.bool)) + list_folds.append(np.concatenate(mask)) + return list_folds + + +def _mask_fk_tables(dataset_dicts, tr_vl_masks): + """ + Internal function which masks the fktables for a group of datasets. + + Parameters + ---------- + dataset_dicts: list[dict] + list of datasets dictionaries returned by + :py:func:`n3fit.io.reader.common_data_reader_experiment`. + tr_vl_masks: tuple[list] + a tuple containing the lists of training and validation masks for + each dataset. + + Return + ------ + trmask: np.array + boolean array resulting from concatenating the training masks of + each dataset + vlmask: np.array + boolean array resulting from concatenating the validation masks of + each dataset + + Note: the returned masks are only used in order to mask the covmat + """ + trmask_partial, vlmask_partial = tr_vl_masks + for dataset_dict, tr_mask, vl_mask in zip(dataset_dicts, trmask_partial, vlmask_partial): + # Generate the training and validation fktables + tr_fks = [] + vl_fks = [] + ex_fks = [] + for fktable_dict in dataset_dict["fktables"]: + tr_fks.append(fktable_dict["fktable"][tr_mask]) + vl_fks.append(fktable_dict["fktable"][vl_mask]) + ex_fks.append(fktable_dict.get("fktable")) + dataset_dict["tr_fktables"] = tr_fks + dataset_dict["vl_fktables"] = vl_fks + dataset_dict["ex_fktables"] = ex_fks + + return np.concatenate(trmask_partial), np.concatenate(vlmask_partial) + + +def fitting_data_dict( + data, + replica_mcseed, + tr_vl_masks, + kfold_masks, + t0set=None, + diagonal_basis=None, +): + """ + Provider which takes the information from validphys ``data``. + + # Returns: + - `all_dict_out`: a dictionary containing all the information of the experiment/dataset + for training, validation and experimental + 'datasets' : list of dictionaries for each of the datasets + contained in ``data`` + 'name' : name of the ``data`` - typically experiment/group name + 'expdata_true' : non-replica data + 'invcovmat_true' : inverse of the covmat (non-replica) + + 'trmask' : mask for the training data + 'invcovmat' : inverse of the covmat for the training data + 'ndata' : number of datapoints for the training data + 'expdata' : experimental data (replica'd) for training + + 'vlmask' : (same as above for validation) + 'invcovmat_vl' : (same as above for validation) + 'ndata_vl' : (same as above for validation) + 'expdata_vl' : (same as above for validation) + + 'positivity' : bool - is this a positivity set? + 'count_chi2' : should this be counted towards the chi2 + """ + # TODO: Plug in the python data loading when available. Including but not + # limited to: central values, ndata, replica generation, covmat construction + spec_c = data.load() + ndata = spec_c.GetNData() + expdata_true = spec_c.get_cv().reshape(1, ndata) + if t0set: + t0pdfset = t0set.load_t0() + spec_c.SetT0(t0pdfset) + + base_mcseed = int(hashlib.sha256(str(data).encode()).hexdigest(), 16) % 10 ** 8 + + spec_replica_c = type(spec_c)(spec_c) # I might need the t0 set here as well + + # Replica generation + if replica_mcseed is not None: + mcseed = base_mcseed + replica_mcseed + RandomGenerator.InitRNG(0, mcseed) + spec_replica_c.MakeReplica() + expdata = spec_replica_c.get_cv() + + datasets = common_data_reader_experiment(spec_c, data) + + # t0 covmat + covmat = spec_c.get_covmat() + inv_true = np.linalg.inv(covmat) + + if diagonal_basis: + log.info("working in diagonal basis.") + eig, v = np.linalg.eigh(covmat) + dt_trans = v.T + else: + dt_trans = None + dt_trans_tr = None + dt_trans_vl = None + + + # Copy dataset dict because we mutate it. + datasets_copy = deepcopy(datasets) + + tr_mask, vl_mask = _mask_fk_tables(datasets_copy, tr_vl_masks) + + if diagonal_basis: + expdata = np.matmul(dt_trans, expdata) + # make a 1d array of the diagonal + covmat_tr = eig[tr_mask] + invcovmat_tr = 1./covmat_tr + + covmat_vl = eig[vl_mask] + invcovmat_vl = 1./covmat_vl + + # prepare a masking rotation + dt_trans_tr = dt_trans[tr_mask] + dt_trans_vl = dt_trans[vl_mask] + else: + covmat_tr = covmat[tr_mask].T[tr_mask] + invcovmat_tr = np.linalg.inv(covmat_tr) + + covmat_vl = covmat[vl_mask].T[vl_mask] + invcovmat_vl = np.linalg.inv(covmat_vl) + + ndata_tr = np.count_nonzero(tr_mask) + expdata_tr = expdata[tr_mask].reshape(1, ndata_tr) + + ndata_vl = np.count_nonzero(vl_mask) + expdata_vl = expdata[vl_mask].reshape(1, ndata_vl) + + # Now save a dictionary of training/validation/experimental folds + # for training and validation we need to apply the tr/vl masks + # for experimental we need to negate the mask + folds = defaultdict(list) + for fold in kfold_masks: + folds["training"].append(fold[tr_mask]) + folds["validation"].append(fold[vl_mask]) + folds["experimental"].append(~fold) + + dict_out = { + "datasets": datasets_copy, + "name": str(data), + "expdata_true": expdata_true, + "invcovmat_true": inv_true, + "trmask": tr_mask, + "invcovmat": invcovmat_tr, + "ndata": ndata_tr, + "expdata": expdata_tr, + "vlmask": vl_mask, + "invcovmat_vl": invcovmat_vl, + "ndata_vl": ndata_vl, + "expdata_vl": expdata_vl, + "positivity": False, + "count_chi2": True, + "folds" : folds, + "data_transformation": dt_trans_tr, + "data_transformation_vl": dt_trans_vl, + } + return dict_out + +replicas_fitting_data_dict = collect("fitting_data_dict", ("replicas",)) +exps_replicas_fitting_data_dict = collect( + "replicas_fitting_data_dict", ("group_dataset_inputs_by_experiment",)) diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index b12e6359ea..02d6d6ece5 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -13,7 +13,7 @@ log = logging.getLogger(__name__) -def initialize_seeds(replica: list, trvlseed: int, nnseed: int, mcseed: int, genrep: bool): +def initialize_seeds(replicas: list, trvlseed: int, nnseed: int, mcseed: int, genrep: bool): """Action to initialize seeds for random number generation. We initialize three different seeds. The first is the seed used for training/validation splits, the second is used for @@ -27,7 +27,7 @@ def initialize_seeds(replica: list, trvlseed: int, nnseed: int, mcseed: int, gen Parameters ---------- - replica: list + replicas: list A list of replica numbers to run over typically of size one trvlseed: int Seed initialization for training/validation split @@ -50,7 +50,7 @@ def initialize_seeds(replica: list, trvlseed: int, nnseed: int, mcseed: int, gen trvalseeds = [] nnseeds = [] mcseeds = [] - for replica_number in replica: + for replica_number in replicas: np.random.seed(trvlseed) for _ in range(replica_number): trvalseed = np.random.randint(0, pow(2, 31)) @@ -80,13 +80,15 @@ def initialize_seeds(replica: list, trvlseed: int, nnseed: int, mcseed: int, gen @n3fit.checks.wrapper_hyperopt def performfit( fitting, + exps_replicas_fitting_data_dict, experiments_data, - t0set, - replica, + replicas_nnseed, + replicas, replica_path, output_path, theoryid, posdatasets, + kfold_parameters, integdatasets=None, hyperscan=None, hyperopt=None, @@ -94,7 +96,8 @@ def performfit( maxcores=None, ): """ - This action will (upon having read a validcard) process a full PDF fit for a given replica. + This action will (upon having read a validcard) process a full PDF fit + for a set of replicas. The input to this function is provided by validphys and/or defined in the runcards or commandline arguments. @@ -121,7 +124,7 @@ def performfit( the fit t0set: str t0set name - replica: list + replicas: list a list of replica numbers to run over (typically just one) replica_path: pathlib.Path path to the output of this run @@ -158,19 +161,6 @@ def performfit( from n3fit.backends import MetaModel, operations import n3fit.io.reader as reader - # Loading t0set from LHAPDF - if t0set is not None: - t0pdfset = t0set.load_t0() - else: - t0pdfset = None - - trvlseed, nnseed, mcseed, genrep = [ - fitting.get(i) for i in ["trvlseed", "nnseed", "mcseed", "genrep"] - ] - - seeds = initialize_seeds(replica, trvlseed, nnseed, mcseed, genrep) - trvalseeds, nnseeds, mcseeds = seeds.trvlseeds, seeds.nnseeds, seeds.mcseeds - ############################################################################## # ### Read files # Loop over all the experiment and positivity datasets @@ -180,28 +170,10 @@ def performfit( # we are just creating dictionaries with all the necessary information # (experimental data, covariance matrix, replicas, etc, tr/val split) ############################################################################## - all_exp_infos = [[] for _ in replica] - if fitting.get("diagonal_basis"): - log.info("working in diagonal basis") + all_exp_infos = [[] for _ in replicas] - if hyperscan and hyperopt: - kfold_parameters = hyperscan["kfold"] - kpartitions = kfold_parameters["partitions"] - else: - kfold_parameters = None - kpartitions = None - - # First loop over the experiments - for exp in experiments_data: - log.info("Loading experiment: {0}".format(exp)) - all_exp_dicts = reader.common_data_reader( - exp, - t0pdfset, - replica_seeds=mcseeds, - trval_seeds=trvalseeds, - kpartitions=kpartitions, - rotate_diagonal=fitting.get("diagonal_basis"), - ) + # First loop over the experiments, then replica + for all_exp_dicts in exps_replicas_fitting_data_dict: for i, exp_dict in enumerate(all_exp_dicts): all_exp_infos[i].append(exp_dict) @@ -225,7 +197,7 @@ def performfit( # Note: In the basic scenario we are only running for one replica and thus this loop is only # run once and all_exp_infos is a list of just than one element stopwatch.register_times("data_loaded") - for replica_number, exp_info, nnseed in zip(replica, all_exp_infos, nnseeds): + for replica_number, exp_info, nnseed in zip(replicas, all_exp_infos, replicas_nnseed): replica_path_set = replica_path / f"replica_{replica_number}" log.info("Starting replica fit %s", replica_number) diff --git a/n3fit/src/n3fit/scripts/n3fit_exec.py b/n3fit/src/n3fit/scripts/n3fit_exec.py index 7609999a68..c539605418 100755 --- a/n3fit/src/n3fit/scripts/n3fit_exec.py +++ b/n3fit/src/n3fit/scripts/n3fit_exec.py @@ -17,6 +17,7 @@ from validphys.core import FitSpec from reportengine import colors from reportengine.compat import yaml +from reportengine.namespaces import NSList N3FIT_FIXED_CONFIG = dict( @@ -25,7 +26,7 @@ actions_ = [] ) -N3FIT_PROVIDERS = ["n3fit.performfit", "validphys.results"] +N3FIT_PROVIDERS = ["n3fit.performfit", "validphys.results", "n3fit.io.reader_providers"] log = logging.getLogger(__name__) @@ -68,7 +69,7 @@ def init_output(self): # create output folder for the fit self.replica_path = self.output_path / "nnfit" - for replica in self.replica: + for replica in self.replicas: path = self.replica_path / "replica_{0}".format(replica) log.info("Creating replica output folder in {0}".format(path)) try: @@ -83,7 +84,7 @@ def init_output(self): @classmethod def ns_dump_description(cls): return { - "replica": "The MC replica number", + "replicas": "The MC replica number/s", "replica_path": "The replica output path", "output_path": "The runcard name", "hyperopt": "The hyperopt flag", @@ -112,10 +113,10 @@ def from_yaml(cls, o, *args, **kwargs): if file_content.get('closuretest') is not None: N3FIT_FIXED_CONFIG['actions_'].append( - 'datacuts::theory::closuretest performfit') + 'datacuts::theory::closuretest::fitting performfit') else: N3FIT_FIXED_CONFIG['actions_'].append( - 'datacuts::theory performfit') + 'datacuts::theory::fitting performfit') file_content.update(N3FIT_FIXED_CONFIG) return cls(file_content, *args, **kwargs) @@ -147,7 +148,15 @@ def produce_use_fitcommondata(self, fakedata): """ return fakedata + def produce_kfold_parameters(self, hyperscan=None, hyperopt=None): + if hyperscan and hyperopt: + return hyperscan["kfold"] + return None + def produce_kpartitions(self, kfold_parameters): + if kfold_parameters: + return kfold_parameters["partitions"] + return None class N3FitApp(App): @@ -191,7 +200,7 @@ def run(self): replicas = list(range(replica, self.args["replica_range"] + 1)) else: replicas = [replica] - self.environment.replica = replicas + self.environment.replicas = NSList(replicas, nskey="replica") self.environment.hyperopt = self.args["hyperopt"] super().run() except N3FitError as e: From 84f578d793de299dd490c413216b067e8b8c060e Mon Sep 17 00:00:00 2001 From: wilsonm Date: Thu, 4 Feb 2021 11:45:08 +0000 Subject: [PATCH 02/16] make use of fitting being expanded as namespace, update checks to reflect. Add integrability and positivity actions --- n3fit/src/n3fit/checks.py | 60 ++++++++---------- n3fit/src/n3fit/io/reader_providers.py | 33 ++++++++-- n3fit/src/n3fit/performfit.py | 88 ++++++++------------------ 3 files changed, 84 insertions(+), 97 deletions(-) diff --git a/n3fit/src/n3fit/checks.py b/n3fit/src/n3fit/checks.py index 7f90a0d292..edab10031f 100644 --- a/n3fit/src/n3fit/checks.py +++ b/n3fit/src/n3fit/checks.py @@ -67,9 +67,9 @@ def check_stopping(parameters): raise CheckError(f"Needs to run at least 1 epoch, got: {epochs}") -def check_basis_with_layers(fitting, parameters): +def check_basis_with_layers(basis, parameters): """ Check that the last layer matches the number of flavours defined in the runcard""" - number_of_flavours = len(fitting["basis"]) + number_of_flavours = len(basis) last_layer = parameters["nodes_per_layer"][-1] if number_of_flavours != last_layer: raise CheckError( @@ -137,34 +137,31 @@ def check_lagrange_multipliers(parameters, key): raise CheckError(f"The {key}::threshold must be a number, received: {threshold}") -def check_model_file(fitting): +def check_model_file(save, load): """ Checks whether the model_files given in the runcard are acceptable """ - save_file = fitting.get("save") - load_file = fitting.get("load") - if save_file: - if not isinstance(save_file, str): - raise CheckError(f"Model file to save to: {save_file} not understood") + if save: + if not isinstance(save, str): + raise CheckError(f"Model file to save to: {save} not understood") # Since the file to save to will be found inside the replica folder, it should writable as all the others - if load_file: - if not isinstance(load_file, str): - raise CheckError(f"Model file to load: {load_file} not understood, str expected") - if not os.path.isfile(load_file): - raise CheckError(f"Model file to load: {load_file} can not be opened, does it exist?") - if not os.access(load_file, os.R_OK): - raise CheckError(f"Model file to load: {load_file} cannot be read, permission denied") - if os.stat(load_file).st_size == 0: - raise CheckError(f"Model file {load_file} seems to be empty") + if load: + if not isinstance(load, str): + raise CheckError(f"Model file to load: {load} not understood, str expected") + if not os.path.isfile(load): + raise CheckError(f"Model file to load: {load} can not be opened, does it exist?") + if not os.access(load, os.R_OK): + raise CheckError(f"Model file to load: {load} cannot be read, permission denied") + if os.stat(load).st_size == 0: + raise CheckError(f"Model file {load} seems to be empty") @make_argcheck -def wrapper_check_NN(fitting): +def wrapper_check_NN(basis, tensorboard, save, load, parameters): """ Wrapper function for all NN-related checks """ - check_tensorboard(fitting.get("tensorboard")) - parameters = fitting["parameters"] - check_model_file(fitting) + check_tensorboard(tensorboard) + check_model_file(save, load) check_existing_parameters(parameters) check_consistent_layers(parameters) - check_basis_with_layers(fitting, parameters) + check_basis_with_layers(basis, parameters) check_stopping(parameters) check_dropout(parameters) check_lagrange_multipliers(parameters, "integrability") @@ -244,13 +241,11 @@ def check_kfold_options(kfold): ) -def check_correct_partitions(kfold, experiments_data): +def check_correct_partitions(kfold, data): """Ensures that all experimennts in all partitions are included in the fit definition""" # Get all datasets - datasets = [] - for exp in experiments_data: - datasets += [i.name for i in exp.datasets] + datasets = list(map(str, data)) for partition in kfold["partitions"]: fold_sets = partition["datasets"] for dset in fold_sets: @@ -290,13 +285,13 @@ def check_hyperopt_stopping(stopping_dict): @make_argcheck -def wrapper_hyperopt(hyperopt, hyperscan, fitting, experiments_data): +def wrapper_hyperopt(hyperopt, hyperscan, genrep, data): """Wrapper function for all hyperopt-related checks No check is performed if hyperopt is not active """ if not hyperopt: return None - if fitting["genrep"]: + if genrep: raise CheckError("Generation of replicas is not accepted during hyperoptimization") if hyperscan is None: raise CheckError("Can't perform hyperoptimization without the hyperscan key") @@ -306,7 +301,7 @@ def wrapper_hyperopt(hyperopt, hyperscan, fitting, experiments_data): check_hyperopt_architecture(hyperscan.get("architecture")) check_hyperopt_positivity(hyperscan.get("positivity")) check_kfold_options(hyperscan["kfold"]) - check_correct_partitions(hyperscan["kfold"], experiments_data) + check_correct_partitions(hyperscan["kfold"], data) def check_sumrules(sum_rules): @@ -322,17 +317,16 @@ def check_sumrules(sum_rules): # Checks on the physics @make_argcheck -def check_consistent_basis(fitting, theoryid): +def check_consistent_basis(sum_rules, fitbasis, basis, theoryid): """Checks the fitbasis setup for inconsistencies - Checks the sum rules can be imposed - Correct flavours for the selected basis - Correct ranges (min < max) for the small and large-x exponents """ - check_sumrules(fitting.get("sum_rules", True)) - fitbasis = fitting["fitbasis"] + check_sumrules(sum_rules) # Check that there are no duplicate flavours and that parameters are sane flavs = [] - for flavour_dict in fitting["basis"]: + for flavour_dict in basis: name = flavour_dict["fl"] smallx = flavour_dict["smallx"] if smallx[0] > smallx[1]: diff --git a/n3fit/src/n3fit/io/reader_providers.py b/n3fit/src/n3fit/io/reader_providers.py index 9d928395eb..dcd97fe276 100644 --- a/n3fit/src/n3fit/io/reader_providers.py +++ b/n3fit/src/n3fit/io/reader_providers.py @@ -13,7 +13,7 @@ from NNPDF import RandomGenerator from reportengine import collect -from n3fit.io.reader import common_data_reader_experiment +from n3fit.io.reader import common_data_reader_experiment, positivity_reader log = logging.getLogger() @@ -274,6 +274,31 @@ def fitting_data_dict( } return dict_out -replicas_fitting_data_dict = collect("fitting_data_dict", ("replicas",)) -exps_replicas_fitting_data_dict = collect( - "replicas_fitting_data_dict", ("group_dataset_inputs_by_experiment",)) +exps_fitting_data_dict = collect("fitting_data_dict", ("group_dataset_inputs_by_experiment",)) + +def replica_nnseed_fitting_data_dict(replica, exps_fitting_data_dict, replica_nnseed): + return (replica, exps_fitting_data_dict, replica_nnseed) + +replicas_nnseed_fitting_data_dict = collect("replica_nnseed_fitting_data_dict", ("replicas",)) + + +def fitting_pos_dict(posdataset): + """Loads a positivity dataset""" + log.info("Loading positivity dataset %s", posdataset) + return positivity_reader(posdataset) + +posdatasets_fitting_pos_dict = collect("fitting_pos_dict", ("posdatasets",)) + + +#can't use collect here because integdatasets might not exist. +def integdatasets_fitting_integ_dict(integdatasets=None): + """Loads a integrability dataset""" + if integdatasets is not None: + integ_info = [] + for integ_set in integdatasets: + log.info("Loading integrability dataset %s", integ_set) + # Use the same reader as positivity observables + integ_dict = positivity_reader(integ_set) + integ_info.append(integ_dict) + else: + integ_info = None diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index 02d6d6ece5..350e2cf759 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -5,7 +5,6 @@ # Backend-independent imports from collections import namedtuple import logging -import os.path import numpy as np import n3fit.checks from n3fit.vpinterface import N3PDF @@ -79,21 +78,27 @@ def initialize_seeds(replicas: list, trvlseed: int, nnseed: int, mcseed: int, ge @n3fit.checks.wrapper_check_NN @n3fit.checks.wrapper_hyperopt def performfit( - fitting, - exps_replicas_fitting_data_dict, - experiments_data, - replicas_nnseed, - replicas, + genrep, + data, + replicas_nnseed_fitting_data_dict, + posdatasets_fitting_pos_dict, + integdatasets_fitting_integ_dict, replica_path, output_path, theoryid, - posdatasets, kfold_parameters, - integdatasets=None, + basis, + fitbasis, + parameters, hyperscan=None, hyperopt=None, debug=False, maxcores=None, + save_weights_each=None, + load=None, + sum_rules=True, + tensorboard=None, + save=None, ): """ This action will (upon having read a validcard) process a full PDF fit @@ -158,46 +163,11 @@ def performfit( # so they can eventually be set from the runcard from n3fit.ModelTrainer import ModelTrainer from n3fit.io.writer import WriterWrapper - from n3fit.backends import MetaModel, operations - import n3fit.io.reader as reader - - ############################################################################## - # ### Read files - # Loop over all the experiment and positivity datasets - # and save them into two list of dictionaries: exp_info and pos_info - # later on we will loop over these two lists and generate the actual layers - # i.e., at this point there is no information about the NN - # we are just creating dictionaries with all the necessary information - # (experimental data, covariance matrix, replicas, etc, tr/val split) - ############################################################################## - all_exp_infos = [[] for _ in replicas] - - # First loop over the experiments, then replica - for all_exp_dicts in exps_replicas_fitting_data_dict: - for i, exp_dict in enumerate(all_exp_dicts): - all_exp_infos[i].append(exp_dict) - - # Now read all the positivity datasets - pos_info = [] - for pos_set in posdatasets: - log.info("Loading positivity dataset %s", pos_set) - pos_dict = reader.positivity_reader(pos_set) - pos_info.append(pos_dict) - - if integdatasets is not None: - integ_info = [] - for integ_set in integdatasets: - log.info("Loading integrability dataset %s", integ_set) - # Use the same reader as positivity observables - integ_dict = reader.positivity_reader(integ_set) - integ_info.append(integ_dict) - else: - integ_info = None # Note: In the basic scenario we are only running for one replica and thus this loop is only - # run once and all_exp_infos is a list of just than one element + # run once and replicas_nnseed_fitting_data_dict is a list of just than one element stopwatch.register_times("data_loaded") - for replica_number, exp_info, nnseed in zip(replicas, all_exp_infos, replicas_nnseed): + for replica_number, exp_info, nnseed in replicas_nnseed_fitting_data_dict: replica_path_set = replica_path / f"replica_{replica_number}" log.info("Starting replica fit %s", replica_number) @@ -205,24 +175,23 @@ def performfit( # this object holds all necessary information to train a PDF (up to the NN definition) the_model_trainer = ModelTrainer( exp_info, - pos_info, - integ_info, - fitting["basis"], - fitting["fitbasis"], + posdatasets_fitting_pos_dict, + integdatasets_fitting_integ_dict, + basis, + fitbasis, nnseed, debug=debug, - save_weights_each=fitting.get("save_weights_each"), + save_weights_each=save_weights_each, kfold_parameters=kfold_parameters, max_cores=maxcores, - model_file=fitting.get("load"), - sum_rules=fitting.get("sum_rules", True) + model_file=load, + sum_rules=sum_rules ) # This is just to give a descriptive name to the fit function pdf_gen_and_train_function = the_model_trainer.hyperparametrizable # Read up the parameters of the NN from the runcard - parameters = fitting.get("parameters") stopwatch.register_times("replica_set") ######################################################################## @@ -252,10 +221,9 @@ def performfit( the_model_trainer.set_hyperopt(False) # Enable the tensorboard callback - tboard = fitting.get("tensorboard") - if tboard is not None: - profiling = tboard.get("profiling", False) - weight_freq = tboard.get("weight_freq", 0) + if tensorboard is not None: + profiling = tensorboard.get("profiling", False) + weight_freq = tensorboard.get("weight_freq", 0) log_path = replica_path_set / "tboard" the_model_trainer.enable_tensorboard(log_path, weight_freq, profiling) @@ -290,7 +258,7 @@ def performfit( ) # Create a pdf instance - pdf_instance = N3PDF(pdf_model, fit_basis=fitting.get("basis")) + pdf_instance = N3PDF(pdf_model, fit_basis=basis) # Generate the writer wrapper writer_wrapper = WriterWrapper( @@ -308,7 +276,7 @@ def performfit( ) # Save the weights to some file for the given replica - model_file = fitting.get("save") + model_file = save if model_file: model_file_path = replica_path_set / model_file log.info(" > Saving the weights for future in %s", model_file_path) @@ -327,5 +295,5 @@ def performfit( # So every time we want to capture output_path.name and addd a history_step_X # parallel to the nnfit folder - if tboard is not None: + if tensorboard is not None: log.info("Tensorboard logging information is stored at %s", log_path) From 07577b829c64d2c118c189736c7de5f9e3935133 Mon Sep 17 00:00:00 2001 From: wilsonm Date: Thu, 4 Feb 2021 12:09:17 +0000 Subject: [PATCH 03/16] update performfit docstring to reflect new parameters --- n3fit/src/n3fit/performfit.py | 77 ++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index 350e2cf759..1b47588159 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -107,40 +107,57 @@ def performfit( The input to this function is provided by validphys and/or defined in the runcards or commandline arguments. + This controller is provided with: + 1. Seeds generated using the replica number and the seeds defined in the runcard. + 2. Loaded datasets with replicas generated. + 2.1 Loaded positivity/integrability sets. + The workflow of this controller is as follows: - 1. Generates seeds using the replica number and the seeds defined in the runcard, - 2. Read up all datasets from the given experiments and create the necessary replicas - 2.1 Read up also the positivity sets - 3. Generate a ModelTrainer object holding information to create the NN and perform a fit + 1. Generate a ModelTrainer object holding information to create the NN and perform a fit (at this point no NN object has been generated) - 3.1 (if hyperopt) generates the hyperopt scanning dictionary + 1.1 (if hyperopt) generates the hyperopt scanning dictionary taking as a base the fitting dictionary and the runcard's hyperscan dictionary - 4. Pass the dictionary of parameters to ModelTrainer + 2. Pass the dictionary of parameters to ModelTrainer for the NN to be generated and the fit performed - 4.1 (if hyperopt) Loop over point 4 for `hyperopt` number of times - 5. Once the fit is finished, output the PDF grid and accompanying files + 2.1 (if hyperopt) Loop over point 4 for `hyperopt` number of times + 3. Once the fit is finished, output the PDF grid and accompanying files Parameters ---------- - fitting: dict - dictionary with the hyperparameters of the fit - experiments_data: dict - vp list of datasets grouped into experiments to be included in - the fit - t0set: str - t0set name - replicas: list - a list of replica numbers to run over (typically just one) + genrep: bool + Whether or not to generate MC replicas. + data: validphys.core.DataGroupSpec + containing the datasets to be included in the fit. + replicas_nnseed_fitting_data_dict: list[tuple] + list with element for each replica (typically just one) to be + fitted. Each element + is a tuple containing the replica number, nnseed and + ``fitted_data_dict`` containing all of the data, metadata + for each group of datasets which is to be fitted. + posdatasets_fitting_pos_dict: list[dict] + list of dictionaries containing all data and metadata for each + positivity dataset + integdatasets_fitting_integ_dict: list[dict] + list of dictionaries containing all data and metadata for each + integrability dataset replica_path: pathlib.Path path to the output of this run output_path: str name of the fit - theorid: int - theory id number - posdatasets: list - list of positivity datasets - integdatasets: list - list of integrability datasets + theoryid: validphys.core.TheoryIDSpec + Theory which is used to generate theory predictions from model + during fit. Object also contains some metadata on the theory + settings. + kfold_parameters: None, dict + dictionary with kfold settings used in hyperopt. + basis: list[dict] + preprocessing information for each flavour to be fitted. + fitbasis: str + Valid basis which the fit is to be ran in. Available bases can + be found in :py:mod:`validphys.pdfbases`. + parameters: dict + Mapping containing parameters which define the network + architecture/fitting methodology. hyperscan: dict dictionary containing the details of the hyperscan hyperopt: int @@ -149,6 +166,20 @@ def performfit( activate some debug options maxcores: int maximum number of (logical) cores that the backend should be aware of + save_weights_each: None, int + if set, save the state of the fit every ``save_weights_each`` + epochs + load: None, str + model file from which to load weights from. + sum_rules: bool + Whether to impose sum rules in fit. By default set to True + tensorboard: None, dict + mapping containing tensorboard settings if it is to be used. By + default it is None and tensorboard is not enabled. + save: None, str + model file where weights will be save, used in conjunction with + ``load``. + """ from n3fit.backends import set_initial_state From 726986388dbcbf3b8bff6238735756f03ca8cae9 Mon Sep 17 00:00:00 2001 From: wilsonm Date: Mon, 8 Feb 2021 16:50:28 +0000 Subject: [PATCH 04/16] actually return the integrability info --- n3fit/src/n3fit/io/reader_providers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/n3fit/src/n3fit/io/reader_providers.py b/n3fit/src/n3fit/io/reader_providers.py index dcd97fe276..afa88e5887 100644 --- a/n3fit/src/n3fit/io/reader_providers.py +++ b/n3fit/src/n3fit/io/reader_providers.py @@ -300,5 +300,5 @@ def integdatasets_fitting_integ_dict(integdatasets=None): # Use the same reader as positivity observables integ_dict = positivity_reader(integ_set) integ_info.append(integ_dict) - else: - integ_info = None + return integ_info + return None From 2c1c19cb68c934def3568a50d7a11cd07483720e Mon Sep 17 00:00:00 2001 From: wilsonm Date: Tue, 2 Mar 2021 14:12:34 +0000 Subject: [PATCH 05/16] add pseudodata table and command line flag to dump data. --- n3fit/src/n3fit/io/reader_providers.py | 114 +++++++++++++++---------- n3fit/src/n3fit/scripts/n3fit_exec.py | 26 ++++-- 2 files changed, 92 insertions(+), 48 deletions(-) diff --git a/n3fit/src/n3fit/io/reader_providers.py b/n3fit/src/n3fit/io/reader_providers.py index afa88e5887..6e1a1b2185 100644 --- a/n3fit/src/n3fit/io/reader_providers.py +++ b/n3fit/src/n3fit/io/reader_providers.py @@ -1,17 +1,22 @@ """ -Bridges between NNPDF objects and providers which can return python -objects required in ``n3fit.performfit`` +reader_providers.py + +Providers which prepare the data ready for +:py:func:`n3fit.performfit.performfit`. Returns python objects but the underlying +functions make calls to libnnpdf C++ library. """ -from collections import defaultdict, namedtuple +from collections import defaultdict from copy import deepcopy import hashlib import logging import numpy as np +import pandas as pd from NNPDF import RandomGenerator from reportengine import collect +from reportengine.table import table from n3fit.io.reader import common_data_reader_experiment, positivity_reader @@ -42,19 +47,14 @@ def replica_mcseed(replica, mcseed, genrep): res = np.random.randint(0, pow(2, 31)) return res -replicas_nnseed = collect("replica_nnseed", ("replicas",)) - -TrVlMasks = namedtuple( - "TrVlMasks", - ( - "dataset_inputs_tr_mask", - "dataset_inputs_vl_mask" - ) -) -def tr_vl_masks(data, replica_trvlseed): +def tr_masks(data, replica_trvlseed): """Generate the boolean masks used to split data into training and - validation points. + validation points. Returns a list of 1-D boolean arrays, one for each + dataset. Each array has length equal to N_data, the datapoints which + will be included in the training are ``True`` such that + + tr_data = data[tr_mask] """ nameseed = int(hashlib.sha256(str(data).encode()).hexdigest(), 16) % 10 ** 8 @@ -62,7 +62,6 @@ def tr_vl_masks(data, replica_trvlseed): # TODO: update this to new random infrastructure. np.random.seed(nameseed) trmask_partial = [] - vlmask_partial = [] for dataset in data.datasets: # TODO: python commondata will not require this rubbish. # all data if cuts are None @@ -74,15 +73,14 @@ def tr_vl_masks(data, replica_trvlseed): [np.ones(trmax, dtype=np.bool), np.zeros(ndata - trmax, dtype=np.bool)] ) np.random.shuffle(mask) - vl_mask = ~mask trmask_partial.append(mask) - vlmask_partial.append(vl_mask) - return TrVlMasks(trmask_partial, vlmask_partial) + return trmask_partial def kfold_masks(kpartitions, data): """Collect the masks (if any) due to kfolding for this data. These will be applied to the experimental data before starting the training of each fold. + """ list_folds = [] if kpartitions is not None: @@ -104,7 +102,7 @@ def kfold_masks(kpartitions, data): return list_folds -def _mask_fk_tables(dataset_dicts, tr_vl_masks): +def _mask_fk_tables(dataset_dicts, tr_masks): """ Internal function which masks the fktables for a group of datasets. @@ -113,27 +111,24 @@ def _mask_fk_tables(dataset_dicts, tr_vl_masks): dataset_dicts: list[dict] list of datasets dictionaries returned by :py:func:`n3fit.io.reader.common_data_reader_experiment`. - tr_vl_masks: tuple[list] - a tuple containing the lists of training and validation masks for - each dataset. + tr_masks: list[np.array] + a tuple containing the lists of training masks for each dataset. Return ------ - trmask: np.array + data_trmask: np.array boolean array resulting from concatenating the training masks of - each dataset - vlmask: np.array - boolean array resulting from concatenating the validation masks of - each dataset + each dataset. Note: the returned masks are only used in order to mask the covmat """ - trmask_partial, vlmask_partial = tr_vl_masks - for dataset_dict, tr_mask, vl_mask in zip(dataset_dicts, trmask_partial, vlmask_partial): + trmask_partial = tr_masks + for dataset_dict, tr_mask in zip(dataset_dicts, trmask_partial): # Generate the training and validation fktables tr_fks = [] vl_fks = [] ex_fks = [] + vl_mask = ~tr_mask for fktable_dict in dataset_dict["fktables"]: tr_fks.append(fktable_dict["fktable"][tr_mask]) vl_fks.append(fktable_dict["fktable"][vl_mask]) @@ -142,13 +137,30 @@ def _mask_fk_tables(dataset_dicts, tr_vl_masks): dataset_dict["vl_fktables"] = vl_fks dataset_dict["ex_fktables"] = ex_fks - return np.concatenate(trmask_partial), np.concatenate(vlmask_partial) + return np.concatenate(trmask_partial) + + +def generate_data_replica(data, replica_mcseed): + """Generate a pseudodata replica for ``data`` given the ``replica_seed``""" + spec_c = data.load() + base_mcseed = int(hashlib.sha256(str(data).encode()).hexdigest(), 16) % 10 ** 8 + # copy C++ object to avoid mutation + # t0 not required for replica generation, since libnnpdf uses experimental + # covmat to generate replicas. + spec_replica_c = type(spec_c)(spec_c) + + # Replica generation + if replica_mcseed is not None: + mcseed = base_mcseed + replica_mcseed + RandomGenerator.InitRNG(0, mcseed) + spec_replica_c.MakeReplica() + return spec_replica_c.get_cv() def fitting_data_dict( data, - replica_mcseed, - tr_vl_masks, + generate_data_replica, + tr_masks, kfold_masks, t0set=None, diagonal_basis=None, @@ -187,16 +199,7 @@ def fitting_data_dict( t0pdfset = t0set.load_t0() spec_c.SetT0(t0pdfset) - base_mcseed = int(hashlib.sha256(str(data).encode()).hexdigest(), 16) % 10 ** 8 - - spec_replica_c = type(spec_c)(spec_c) # I might need the t0 set here as well - - # Replica generation - if replica_mcseed is not None: - mcseed = base_mcseed + replica_mcseed - RandomGenerator.InitRNG(0, mcseed) - spec_replica_c.MakeReplica() - expdata = spec_replica_c.get_cv() + expdata = generate_data_replica datasets = common_data_reader_experiment(spec_c, data) @@ -217,7 +220,8 @@ def fitting_data_dict( # Copy dataset dict because we mutate it. datasets_copy = deepcopy(datasets) - tr_mask, vl_mask = _mask_fk_tables(datasets_copy, tr_vl_masks) + tr_mask = _mask_fk_tables(datasets_copy, tr_masks) + vl_mask = ~tr_mask if diagonal_basis: expdata = np.matmul(dt_trans, expdata) @@ -281,6 +285,30 @@ def replica_nnseed_fitting_data_dict(replica, exps_fitting_data_dict, replica_nn replicas_nnseed_fitting_data_dict = collect("replica_nnseed_fitting_data_dict", ("replicas",)) +exps_pseudodata = collect("generate_data_replica", ("group_dataset_inputs_by_experiment",)) +replicas_exps_pseudodata = collect("exps_pseudodata", ("replicas",)) + +@table +def pseudodata_table(replicas_exps_pseudodata, replicas, experiments_index): + """Creates a pandas DataFrame containing the generated pseudodata. The + index is :py:func:`validphys.results.experiments_index` and the columns + are the replica numbers. + + Notes + ----- + The table folder where this table is saved is + created inside the final fitted replica + + """ + rep_dfs = [] + for rep_exps_pseudodata, rep in zip(replicas_exps_pseudodata, replicas): + all_pseudodata = np.concatenate(rep_exps_pseudodata) + rep_dfs.append(pd.DataFrame( + all_pseudodata[:, np.newaxis], + columns=[f"replica {rep}"], + index=experiments_index + )) + return pd.concat(rep_dfs, axis=1) def fitting_pos_dict(posdataset): """Loads a positivity dataset""" diff --git a/n3fit/src/n3fit/scripts/n3fit_exec.py b/n3fit/src/n3fit/scripts/n3fit_exec.py index c539605418..8cd0e6dd21 100755 --- a/n3fit/src/n3fit/scripts/n3fit_exec.py +++ b/n3fit/src/n3fit/scripts/n3fit_exec.py @@ -32,7 +32,7 @@ RUNCARD_COPY_FILENAME = "filter.yml" INPUT_FOLDER = "input" - +TAB_FOLDER = "tables" class N3FitError(Exception): """Exception raised when n3fit cannot succeed and knows why""" @@ -76,6 +76,11 @@ def init_output(self): path.mkdir(exist_ok=True) except OSError as e: raise EnvironmentError_(e) from e + + # place tables in last replica path + self.table_folder = path / TAB_FOLDER + self.table_folder.mkdir(exist_ok=True) + # make lockfile input inside of replica folder # avoid conflict with setupfit self.input_folder = self.replica_path / INPUT_FOLDER @@ -112,11 +117,15 @@ def from_yaml(cls, o, *args, **kwargs): raise ConfigError(f"Expecting input runcard to be a mapping, " f"not '{type(file_content)}'.") if file_content.get('closuretest') is not None: - N3FIT_FIXED_CONFIG['actions_'].append( - 'datacuts::theory::closuretest::fitting performfit') + fit_action = 'datacuts::theory::closuretest::fitting performfit' else: - N3FIT_FIXED_CONFIG['actions_'].append( - 'datacuts::theory::fitting performfit') + fit_action = 'datacuts::theory::fitting performfit' + N3FIT_FIXED_CONFIG['actions_'].append(fit_action) + + if kwargs["environment"].dump_pseudodata: + # take same namespace configuration on the pseudodata_table action. + table_action = fit_action.replace('performfit', 'pseudodata_table') + N3FIT_FIXED_CONFIG['actions_'].append(table_action) file_content.update(N3FIT_FIXED_CONFIG) return cls(file_content, *args, **kwargs) @@ -184,6 +193,12 @@ def check_positive(value): parser.add_argument( "-r", "--replica_range", help="End of the range of replicas to compute", type=check_positive ) + parser.add_argument( + "-d", + "--dump-pseudodata", + action="store_true", + help="Boolean flag, when enabled saves pseudodata table for replica/s." + ) return parser def get_commandline_arguments(self, cmdline=None): @@ -202,6 +217,7 @@ def run(self): replicas = [replica] self.environment.replicas = NSList(replicas, nskey="replica") self.environment.hyperopt = self.args["hyperopt"] + self.environment.dump_pseudodata = self.args["dump_pseudodata"] super().run() except N3FitError as e: log.error(f"Error in n3fit:\n{e}") From f707515d92629d96c03c2a2ce6dabd3b4d1d0e3a Mon Sep 17 00:00:00 2001 From: wilsonm Date: Tue, 2 Mar 2021 14:27:20 +0000 Subject: [PATCH 06/16] Move n3fit reader functions and providers to validphys. --- n3fit/src/n3fit/scripts/n3fit_exec.py | 11 ++------ .../src/validphys/n3fit_data.py | 25 +++++++++++++++---- .../src/validphys/n3fit_data_utils.py | 5 +++- validphys2/src/validphys/pseudodata.py | 2 +- 4 files changed, 27 insertions(+), 16 deletions(-) rename n3fit/src/n3fit/io/reader_providers.py => validphys2/src/validphys/n3fit_data.py (94%) rename n3fit/src/n3fit/io/reader.py => validphys2/src/validphys/n3fit_data_utils.py (99%) diff --git a/n3fit/src/n3fit/scripts/n3fit_exec.py b/n3fit/src/n3fit/scripts/n3fit_exec.py index 8cd0e6dd21..303b5c437c 100755 --- a/n3fit/src/n3fit/scripts/n3fit_exec.py +++ b/n3fit/src/n3fit/scripts/n3fit_exec.py @@ -26,7 +26,7 @@ actions_ = [] ) -N3FIT_PROVIDERS = ["n3fit.performfit", "validphys.results", "n3fit.io.reader_providers"] +N3FIT_PROVIDERS = ["n3fit.performfit", "validphys.results", "validphys.n3fit_data"] log = logging.getLogger(__name__) @@ -122,7 +122,7 @@ def from_yaml(cls, o, *args, **kwargs): fit_action = 'datacuts::theory::fitting performfit' N3FIT_FIXED_CONFIG['actions_'].append(fit_action) - if kwargs["environment"].dump_pseudodata: + if file_content["fitting"].get("savepseudodata"): # take same namespace configuration on the pseudodata_table action. table_action = fit_action.replace('performfit', 'pseudodata_table') N3FIT_FIXED_CONFIG['actions_'].append(table_action) @@ -193,12 +193,6 @@ def check_positive(value): parser.add_argument( "-r", "--replica_range", help="End of the range of replicas to compute", type=check_positive ) - parser.add_argument( - "-d", - "--dump-pseudodata", - action="store_true", - help="Boolean flag, when enabled saves pseudodata table for replica/s." - ) return parser def get_commandline_arguments(self, cmdline=None): @@ -217,7 +211,6 @@ def run(self): replicas = [replica] self.environment.replicas = NSList(replicas, nskey="replica") self.environment.hyperopt = self.args["hyperopt"] - self.environment.dump_pseudodata = self.args["dump_pseudodata"] super().run() except N3FitError as e: log.error(f"Error in n3fit:\n{e}") diff --git a/n3fit/src/n3fit/io/reader_providers.py b/validphys2/src/validphys/n3fit_data.py similarity index 94% rename from n3fit/src/n3fit/io/reader_providers.py rename to validphys2/src/validphys/n3fit_data.py index 6e1a1b2185..6d909c8e3c 100644 --- a/n3fit/src/n3fit/io/reader_providers.py +++ b/validphys2/src/validphys/n3fit_data.py @@ -1,5 +1,5 @@ """ -reader_providers.py +n3fit_data.py Providers which prepare the data ready for :py:func:`n3fit.performfit.performfit`. Returns python objects but the underlying @@ -18,7 +18,10 @@ from reportengine import collect from reportengine.table import table -from n3fit.io.reader import common_data_reader_experiment, positivity_reader +from validphys.n3fit_data_utils import ( + common_data_reader_experiment, + positivity_reader, +) log = logging.getLogger() @@ -110,7 +113,7 @@ def _mask_fk_tables(dataset_dicts, tr_masks): ---------- dataset_dicts: list[dict] list of datasets dictionaries returned by - :py:func:`n3fit.io.reader.common_data_reader_experiment`. + :py:func:`validphys.n3fit_data_utils.common_data_reader_experiment`. tr_masks: list[np.array] a tuple containing the lists of training masks for each dataset. @@ -296,8 +299,20 @@ def pseudodata_table(replicas_exps_pseudodata, replicas, experiments_index): Notes ----- - The table folder where this table is saved is - created inside the final fitted replica + Whilst running ``n3fit``, the directory where this table is saved is + created inside the final fitted replica. For example running + + .. code:: + n3fit .yml 1 + + will create the table folder at ``/nnfit/replica_1/tables``. + However, + + .. code:: + n3fit .yml 1 --replica_range 5 + + will create the table folder at ``/nnfit/replica_5/tables``, + since that was the final requested replica. """ rep_dfs = [] diff --git a/n3fit/src/n3fit/io/reader.py b/validphys2/src/validphys/n3fit_data_utils.py similarity index 99% rename from n3fit/src/n3fit/io/reader.py rename to validphys2/src/validphys/n3fit_data_utils.py index c6f598cd0c..601d383ad6 100644 --- a/n3fit/src/n3fit/io/reader.py +++ b/validphys2/src/validphys/n3fit_data_utils.py @@ -1,5 +1,8 @@ """ - Library of function for reading NNPDF objects +n3fit_data_utils.py + +Library of function for reading libnnpdf objects. + """ import hashlib from copy import deepcopy diff --git a/validphys2/src/validphys/pseudodata.py b/validphys2/src/validphys/pseudodata.py index dba391e6ad..b5a9a1f985 100644 --- a/validphys2/src/validphys/pseudodata.py +++ b/validphys2/src/validphys/pseudodata.py @@ -16,7 +16,7 @@ from reportengine import collect -import n3fit.io.reader as reader +import validphys.n3fit_data_utils as reader from n3fit.performfit import initialize_seeds log = logging.getLogger(__name__) From 622b5165ea58c5c6f021151b954cae8eee035022 Mon Sep 17 00:00:00 2001 From: wilsonmr <33907451+wilsonmr@users.noreply.github.com> Date: Tue, 2 Mar 2021 15:47:59 +0000 Subject: [PATCH 07/16] Update n3fit/src/n3fit/performfit.py Co-authored-by: Juacrumar --- n3fit/src/n3fit/performfit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index 1b47588159..05e2b48d78 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -196,7 +196,7 @@ def performfit( from n3fit.io.writer import WriterWrapper # Note: In the basic scenario we are only running for one replica and thus this loop is only - # run once and replicas_nnseed_fitting_data_dict is a list of just than one element + # run once as replicas_nnseed_fitting_data_dict is a list of just one element stopwatch.register_times("data_loaded") for replica_number, exp_info, nnseed in replicas_nnseed_fitting_data_dict: replica_path_set = replica_path / f"replica_{replica_number}" From e95ca1f6963878826dba4c688d0f4ee74251368b Mon Sep 17 00:00:00 2001 From: wilsonm Date: Tue, 2 Mar 2021 16:30:03 +0000 Subject: [PATCH 08/16] reorganise parameters --- n3fit/src/n3fit/performfit.py | 66 ++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index 05e2b48d78..26834e2722 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -72,33 +72,34 @@ def initialize_seeds(replicas: list, trvlseed: int, nnseed: int, mcseed: int, ge return Seeds(trvalseeds, nnseeds, mcseeds) -# Action to be called by valid phys +# Action to be called by validphys # All information defining the NN should come here in the "parameters" dict @n3fit.checks.check_consistent_basis @n3fit.checks.wrapper_check_NN @n3fit.checks.wrapper_hyperopt def performfit( - genrep, - data, + *, + genrep, # used for checks + data, # used for checks replicas_nnseed_fitting_data_dict, posdatasets_fitting_pos_dict, integdatasets_fitting_integ_dict, - replica_path, - output_path, theoryid, - kfold_parameters, basis, fitbasis, + sum_rules=True, parameters, + replica_path, + output_path, + save_weights_each=None, + save=None, + load=None, hyperscan=None, hyperopt=None, + kfold_parameters, + tensorboard=None, debug=False, maxcores=None, - save_weights_each=None, - load=None, - sum_rules=True, - tensorboard=None, - save=None, ): """ This action will (upon having read a validcard) process a full PDF fit @@ -125,9 +126,10 @@ def performfit( Parameters ---------- genrep: bool - Whether or not to generate MC replicas. + Whether or not to generate MC replicas. (Only used for checks) data: validphys.core.DataGroupSpec - containing the datasets to be included in the fit. + containing the datasets to be included in the fit. (Only used + for checks) replicas_nnseed_fitting_data_dict: list[tuple] list with element for each replica (typically just one) to be fitted. Each element @@ -140,45 +142,45 @@ def performfit( integdatasets_fitting_integ_dict: list[dict] list of dictionaries containing all data and metadata for each integrability dataset - replica_path: pathlib.Path - path to the output of this run - output_path: str - name of the fit theoryid: validphys.core.TheoryIDSpec Theory which is used to generate theory predictions from model during fit. Object also contains some metadata on the theory settings. - kfold_parameters: None, dict - dictionary with kfold settings used in hyperopt. basis: list[dict] preprocessing information for each flavour to be fitted. fitbasis: str Valid basis which the fit is to be ran in. Available bases can be found in :py:mod:`validphys.pdfbases`. + sum_rules: bool + Whether to impose sum rules in fit. By default set to True parameters: dict Mapping containing parameters which define the network architecture/fitting methodology. - hyperscan: dict - dictionary containing the details of the hyperscan - hyperopt: int - if given, number of hyperopt iterations to run - debug: bool - activate some debug options - maxcores: int - maximum number of (logical) cores that the backend should be aware of + replica_path: pathlib.Path + path to the output of this run + output_path: str + name of the fit save_weights_each: None, int if set, save the state of the fit every ``save_weights_each`` epochs + save: None, str + model file where weights will be save, used in conjunction with + ``load``. load: None, str model file from which to load weights from. - sum_rules: bool - Whether to impose sum rules in fit. By default set to True + hyperscan: dict + dictionary containing the details of the hyperscan + hyperopt: int + if given, number of hyperopt iterations to run + kfold_parameters: None, dict + dictionary with kfold settings used in hyperopt. tensorboard: None, dict mapping containing tensorboard settings if it is to be used. By default it is None and tensorboard is not enabled. - save: None, str - model file where weights will be save, used in conjunction with - ``load``. + debug: bool + activate some debug options + maxcores: int + maximum number of (logical) cores that the backend should be aware of """ from n3fit.backends import set_initial_state From 9946026f6fa6aec6d63eb81ebbe6ae565beec730 Mon Sep 17 00:00:00 2001 From: wilsonm Date: Tue, 2 Mar 2021 16:45:07 +0000 Subject: [PATCH 09/16] only allow pseudodata saving with single replica fits - for now. Update docs to reflect --- n3fit/src/n3fit/scripts/n3fit_exec.py | 11 ++++++++--- validphys2/src/validphys/n3fit_data.py | 17 +++-------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/n3fit/src/n3fit/scripts/n3fit_exec.py b/n3fit/src/n3fit/scripts/n3fit_exec.py index 303b5c437c..6861f9cbda 100755 --- a/n3fit/src/n3fit/scripts/n3fit_exec.py +++ b/n3fit/src/n3fit/scripts/n3fit_exec.py @@ -77,9 +77,8 @@ def init_output(self): except OSError as e: raise EnvironmentError_(e) from e - # place tables in last replica path - self.table_folder = path / TAB_FOLDER - self.table_folder.mkdir(exist_ok=True) + # place tables in last replica path, folder already exists. + self.table_folder = path # make lockfile input inside of replica folder # avoid conflict with setupfit @@ -123,6 +122,12 @@ def from_yaml(cls, o, *args, **kwargs): N3FIT_FIXED_CONFIG['actions_'].append(fit_action) if file_content["fitting"].get("savepseudodata"): + if len(kwargs["environment"].replicas) != 1: + raise ConfigError( + "Cannot request that multiple replicas are fitted and that " + "pseudodata is saved. Either set `fitting::savepseudodata` " + "to `false` or fit replicas one at a time." + ) # take same namespace configuration on the pseudodata_table action. table_action = fit_action.replace('performfit', 'pseudodata_table') N3FIT_FIXED_CONFIG['actions_'].append(table_action) diff --git a/validphys2/src/validphys/n3fit_data.py b/validphys2/src/validphys/n3fit_data.py index 6d909c8e3c..45ea40ca2f 100644 --- a/validphys2/src/validphys/n3fit_data.py +++ b/validphys2/src/validphys/n3fit_data.py @@ -299,20 +299,9 @@ def pseudodata_table(replicas_exps_pseudodata, replicas, experiments_index): Notes ----- - Whilst running ``n3fit``, the directory where this table is saved is - created inside the final fitted replica. For example running - - .. code:: - n3fit .yml 1 - - will create the table folder at ``/nnfit/replica_1/tables``. - However, - - .. code:: - n3fit .yml 1 --replica_range 5 - - will create the table folder at ``/nnfit/replica_5/tables``, - since that was the final requested replica. + Whilst running ``n3fit``, this action will only be called if + `fitting::savepseudodata` is `true` and replicas are fitted one at a time. + The table can be found in the replica folder i.e. /nnfit/replica_*/ """ rep_dfs = [] From 86716b998379b44dac605aeaa025de9e1a6f8e40 Mon Sep 17 00:00:00 2001 From: wilsonm Date: Wed, 3 Mar 2021 15:58:02 +0000 Subject: [PATCH 10/16] set min tf version --- conda-recipe/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 12216f96d2..d42b007063 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -23,7 +23,7 @@ requirements: - numpy run: # Only install tensorflow on linux. Eigen build is default (for now). - - tensorflow # [linux] + - tensorflow >= 2 # [linux] - psutil # to ensure n3fit affinity is with the right processors - hyperopt - seaborn From 4afd711ea7f5c17fedaa26c22cf6ac8bebfa046c Mon Sep 17 00:00:00 2001 From: wilsonmr <33907451+wilsonmr@users.noreply.github.com> Date: Wed, 3 Mar 2021 16:08:05 +0000 Subject: [PATCH 11/16] Update conda-recipe/meta.yaml --- conda-recipe/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index d42b007063..2cb23ea439 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -23,7 +23,7 @@ requirements: - numpy run: # Only install tensorflow on linux. Eigen build is default (for now). - - tensorflow >= 2 # [linux] + - tensorflow >=2 # [linux] - psutil # to ensure n3fit affinity is with the right processors - hyperopt - seaborn From e228139e30d98ed5a15a9c1dace797322c09abb6 Mon Sep 17 00:00:00 2001 From: wilsonm Date: Thu, 4 Mar 2021 12:53:19 +0000 Subject: [PATCH 12/16] get rid of old initialize seed and propagate that change through. The behaviour has changed slightly but now is reproducible independently of number of replicas being fitted at once --- n3fit/src/n3fit/performfit.py | 60 ------------------ n3fit/src/n3fit/tests/test_fit.py | 31 +++++---- validphys2/src/validphys/app.py | 1 + validphys2/src/validphys/pseudodata.py | 22 +++++-- .../tests/regressions/test_exp_infos.pickle | Bin 5467250 -> 7942172 bytes 5 files changed, 33 insertions(+), 81 deletions(-) diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index 26834e2722..3f21b1a54f 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -12,66 +12,6 @@ log = logging.getLogger(__name__) -def initialize_seeds(replicas: list, trvlseed: int, nnseed: int, mcseed: int, genrep: bool): - """Action to initialize seeds for random number generation. - We initialize three different seeds. The first is the seed - used for training/validation splits, the second is used for - initialization of the neural network's parameters and the - final one is the monte carlo seeds for pseudodata replica - generation. - - The generation of these seeds depend on the replica number - in question. This dependence comes in by sampling the random - number generator times in the for loop. - - Parameters - ---------- - replicas: list - A list of replica numbers to run over typically of size one - trvlseed: int - Seed initialization for training/validation split - nnseed: int - Seed for network initialization - mcseed: int - Seed for pseudodata replica generation - genrep: bool - - Returns - ------- - seeds: NamedTuple[List, List, List] - A namedtuple of lists containing the trvalseeds, nnseeds, mcseeds - """ - # First set the seed variables for - # - Tr/Vl split - # - Neural Network initialization - # - Replica generation - # These depend both on the seed set in the runcard and the replica number - trvalseeds = [] - nnseeds = [] - mcseeds = [] - for replica_number in replicas: - np.random.seed(trvlseed) - for _ in range(replica_number): - trvalseed = np.random.randint(0, pow(2, 31)) - - np.random.seed(nnseed) - for _ in range(replica_number): - nnseed = np.random.randint(0, pow(2, 31)) - - np.random.seed(mcseed) - for _ in range(replica_number): - mcseed = np.random.randint(0, pow(2, 31)) - trvalseeds.append(trvalseed) - nnseeds.append(nnseed) - mcseeds.append(mcseed) - - if genrep == 0: - mcseeds = [] - - Seeds = namedtuple("Seeds", ["trvlseeds", "nnseeds", "mcseeds"]) - return Seeds(trvalseeds, nnseeds, mcseeds) - - # Action to be called by validphys # All information defining the NN should come here in the "parameters" dict @n3fit.checks.check_consistent_basis diff --git a/n3fit/src/n3fit/tests/test_fit.py b/n3fit/src/n3fit/tests/test_fit.py index adfb4809a8..a0d7b2d21a 100644 --- a/n3fit/src/n3fit/tests/test_fit.py +++ b/n3fit/src/n3fit/tests/test_fit.py @@ -30,7 +30,7 @@ from numpy.testing import assert_equal, assert_allclose from reportengine.compat import yaml import n3fit -from n3fit.performfit import initialize_seeds +from validphys.n3fit_data import replica_trvlseed, replica_nnseed, replica_mcseed log = logging.getLogger(__name__) REGRESSION_FOLDER = pathlib.Path(__file__).with_name("regressions") @@ -69,19 +69,22 @@ def load_data(info_file): def test_initialize_seeds(): # Regression tests for seed generation replicas = [1, 4] - Seeds = namedtuple("Seeds", ["trvlseeds", "nnseeds", "mcseeds"]) - regression = Seeds( - trvlseeds=[2005877882, 741720773], - nnseeds=[327741615, 87982037], - mcseeds=[1791095845, 2029572362], - ) - result = initialize_seeds(replicas, 4, 7, 1, True) - assert result == regression - result_nomc = initialize_seeds(replicas, 10, 100, 1000, False) - regression_nomc = Seeds( - trvlseeds=[1165313289, 2124247567], nnseeds=[186422792, 1315999533], mcseeds=[] - ) - assert result_nomc == regression_nomc + + trvlseeds = [replica_trvlseed(rep, 4) for rep in replicas] + assert trvlseeds == [2005877882, 741720773] + nnseeds = [replica_nnseed(rep, 7) for rep in replicas] + assert nnseeds == [327741615, 1369975286] + mcseeds = [replica_mcseed(rep, 1, True) for rep in replicas] + assert mcseeds == [1791095845, 1857819720] + + mcseeds_nomc = [replica_mcseed(rep, 1000, False) for rep in replicas] + assert mcseeds_nomc == [None, None] + + # test that we always get same answer + same_replicas = [3]*10 + assert len({replica_trvlseed(rep, 4) for rep in same_replicas}) == 1 + assert len({replica_nnseed(rep, 7) for rep in same_replicas}) == 1 + assert len({replica_mcseed(rep, 1, True) for rep in same_replicas}) == 1 def auxiliary_performfit(tmp_path, replica=1, timing=True, rel_error=2e-3): diff --git a/validphys2/src/validphys/app.py b/validphys2/src/validphys/app.py index 0de5827f6e..a7cd2a6489 100644 --- a/validphys2/src/validphys/app.py +++ b/validphys2/src/validphys/app.py @@ -50,6 +50,7 @@ 'validphys.covmats', 'validphys.hyperoptplot', 'validphys.deltachi2', + 'validphys.n3fit_data', 'reportengine.report', ] diff --git a/validphys2/src/validphys/pseudodata.py b/validphys2/src/validphys/pseudodata.py index b5a9a1f985..b3c9c7036a 100644 --- a/validphys2/src/validphys/pseudodata.py +++ b/validphys2/src/validphys/pseudodata.py @@ -16,8 +16,8 @@ from reportengine import collect +from validphys.n3fit_data import replica_mcseed, replica_trvlseed import validphys.n3fit_data_utils as reader -from n3fit.performfit import initialize_seeds log = logging.getLogger(__name__) @@ -301,12 +301,20 @@ def fitted_pseudodata_internal(fit, experiments, num_fitted_replicas, t0pdfset=N # include the last replica replica = range(1, num_fitted_replicas + 1) - trvlseed, nnseed, mcseed, genrep = [ + trvlseed, mcseed, genrep = [ fit.as_input().get("fitting").get(i) - for i in ["trvlseed", "nnseed", "mcseed", "genrep"] + for i in ["trvlseed", "mcseed", "genrep"] ] - seeds = initialize_seeds(replica, trvlseed, nnseed, mcseed, genrep) + # common_data_reader expects None if genrep is False + if genrep: + replicas_mcseed = [ + replica_mcseed(rep, mcseed, genrep) for rep in replica + ] + else: + replicas_mcseed = None + + replicas_trvlseeds = [replica_trvlseed(rep, trvlseed) for rep in replica] def task(d, mcseeds, trvlseeds, replicas): all_exp_infos = [[] for _ in range(len(mcseeds))] @@ -321,7 +329,7 @@ def task(d, mcseeds, trvlseeds, replicas): if NPROC == 1: pseudodata_dicts = dict() - task(pseudodata_dicts, seeds.mcseeds, seeds.trvlseeds, replica) + task(pseudodata_dicts, replicas_mcseed, replicas_trvlseeds, replica) else: with mp.Manager() as manager: d = manager.dict() @@ -340,8 +348,8 @@ def task(d, mcseeds, trvlseeds, replicas): list_split = lambda lst, n: [ arr.tolist() for arr in np.array_split(lst, n) ] - batched_mcseeds = list_split(seeds.mcseeds, NPROC) - batched_trvlseeds = list_split(seeds.trvlseeds, NPROC) + batched_mcseeds = list_split(replicas_mcseed, NPROC) + batched_trvlseeds = list_split(replicas_trvlseeds, NPROC) batched_replica_num = list_split(replica, NPROC) for mc_batch, trvl_batch, replica_batch in zip( batched_mcseeds, batched_trvlseeds, batched_replica_num diff --git a/validphys2/src/validphys/tests/regressions/test_exp_infos.pickle b/validphys2/src/validphys/tests/regressions/test_exp_infos.pickle index a7e6829337b6286de24f779edc46665d101b96f7..e854305bfb25ea15464a70c8935ce4fff7c399dd 100644 GIT binary patch delta 105250 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

`=R=-!qA@ne)t?V8sb0F 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 b)ke$FP4zww4~zK#%l}M!|LeoKU*&%Qm)T^G From 588336614415a19b26f8b0a89e57c134dc84fc5a Mon Sep 17 00:00:00 2001 From: wilsonm Date: Tue, 9 Mar 2021 10:34:09 +0000 Subject: [PATCH 13/16] add many more examples and explain parameters --- n3fit/src/n3fit/performfit.py | 2 +- validphys2/src/validphys/n3fit_data.py | 169 ++++++++++++++++++++++++- 2 files changed, 167 insertions(+), 4 deletions(-) diff --git a/n3fit/src/n3fit/performfit.py b/n3fit/src/n3fit/performfit.py index 3f21b1a54f..669eb3b647 100644 --- a/n3fit/src/n3fit/performfit.py +++ b/n3fit/src/n3fit/performfit.py @@ -104,7 +104,7 @@ def performfit( if set, save the state of the fit every ``save_weights_each`` epochs save: None, str - model file where weights will be save, used in conjunction with + model file where weights will be saved, used in conjunction with ``load``. load: None, str model file from which to load weights from. diff --git a/validphys2/src/validphys/n3fit_data.py b/validphys2/src/validphys/n3fit_data.py index 45ea40ca2f..3c223bcb1c 100644 --- a/validphys2/src/validphys/n3fit_data.py +++ b/validphys2/src/validphys/n3fit_data.py @@ -23,7 +23,7 @@ positivity_reader, ) -log = logging.getLogger() +log = logging.getLogger(__name__) def replica_trvlseed(replica, trvlseed): """Generates the ``trvlseed`` for a ``replica``.""" @@ -84,6 +84,46 @@ def kfold_masks(kpartitions, data): These will be applied to the experimental data before starting the training of each fold. + Parameters + ---------- + kpartitions: list[dict] + list of partitions, each partition dictionary with key-value pair + `datasets` and a list containing the names of all datasets in that + partition. See n3fit/runcards/Basic_hyperopt.yml for an example + runcard or the hyperopt documentation for an expanded discussion on + k-fold partitions. + data: validphys.core.DataGroupSpec + full list of data which is to be partitioned. + + Returns + kfold_masks: list[np.array] + A list containing a boolean array for each partition. Each array is + a 1-D boolean array with length equal to the number of cut datapoints + in ``data``. If a dataset is included in a particular fold then the + mask will be True for the elements corresponding to those datasets + such that data.load().get_cv()[kfold_masks[i]] will return the + datapoints in the ith partition. See example below. + + Examples + -------- + >>> from validphys.api import API + >>> partitions=[ + ... {"datasets": ["HERACOMBCCEM", "HERACOMBNCEP460", "NMC", "NTVNBDMNFe"]}, + ... {"datasets": ["HERACOMBCCEP", "HERACOMBNCEP575", "NMCPD", "NTVNUDMNFe"]} + ... ] + >>> ds_inputs = [{"dataset": ds} for part in partitions for ds in part["datasets"]] + >>> kfold_masks = API.kfold_masks(dataset_inputs=ds_inputs, kpartitions=partitions, theoryid=53, use_cuts="nocuts") + >>> len(kfold_masks) # one element for each partition + 2 + >>> kfold_masks[0] # mask which splits data into first partition + array([False, False, False, ..., True, True, True]) + >>> data = API.data(dataset_inputs=ds_inputs, theoryid=53, use_cuts="nocuts") + >>> fold_data = data.load().get_cv()[kfold_masks[0]] + >>> len(fold_data) + 604 + >>> kfold_masks[0].sum() + 604 + """ list_folds = [] if kpartitions is not None: @@ -284,6 +324,16 @@ def fitting_data_dict( exps_fitting_data_dict = collect("fitting_data_dict", ("group_dataset_inputs_by_experiment",)) def replica_nnseed_fitting_data_dict(replica, exps_fitting_data_dict, replica_nnseed): + """For a single replica return a tuple of the inputs to this function. + Used with `collect` over replicas to avoid having to perform multiple + collects. + + See Also + -------- + replicas_nnseed_fitting_data_dict - the result of collecting this function + over replicas. + + """ return (replica, exps_fitting_data_dict, replica_nnseed) replicas_nnseed_fitting_data_dict = collect("replica_nnseed_fitting_data_dict", ("replicas",)) @@ -314,8 +364,98 @@ def pseudodata_table(replicas_exps_pseudodata, replicas, experiments_index): )) return pd.concat(rep_dfs, axis=1) + +exps_tr_masks = collect("tr_masks", ("group_dataset_inputs_by_experiment",)) +replicas_exps_tr_masks = collect("exps_tr_masks", ("replicas",)) + + +@table +def training_mask_table(replicas_exps_tr_masks, replicas, experiments_index): + """Save the boolean mask used to split data into training and validation + for each replica as a pandas DataFrame, indexed by + :py:func:`validphys.results.experiments_index`. Can be used to reconstruct + the training and validation data used in a fit. + + Parameters + ---------- + replicas_exps_tr_masks: list[list[list[np.array]]] + Result of :py:func:`tr_masks` collected over experiments then replicas, + which creates the nested structure. The outer list is len(replicas), + the next list is len(group_dataset_inputs_by_experiment) and the + inner-most list has an array for each dataset in that particular + experiment - as defined by the metadata. The arrays should be 1-D + boolean arrays which can be used as masks. + replicas: NSlist + Namespace list of replica numbers to tabulate masks for, each element + of the list should be a `replica`. See example below for more + information. + experiments_index: pd.MultiIndex + Index returned by :py:func:`validphys.results.experiments_index`. + + + Example + ------- + >>> from validphys.api import API + >>> from reportengine.namespaces import NSList + >>> # create namespace list for collects over replicas. + >>> reps = NSList(list(range(1, 4)), nskey="replica") + >>> ds_inp = [ + ... {'dataset': 'NMC', 'frac': 0.75}, + ... {'dataset': 'ATLASTTBARTOT', 'cfac':['QCD'], 'frac': 0.75}, + ... {'dataset': 'CMSZDIFF12', 'cfac':('QCD', 'NRM'), 'sys':10, 'frac': 0.75} + ... ] + >>> API.training_mask_table(dataset_inputs=ds_inp, replicas=reps, trvlseed=123, theoryid=162, use_cuts="nocuts", mcseed=None, genrep=False) + replica 1 replica 2 replica 3 + group dataset id + NMC NMC 0 True False False + 1 True True True + 2 False True True + 3 True True False + 4 True True True + ... ... ... ... + CMS CMSZDIFF12 45 True True True + 46 True False True + 47 True True True + 48 False True True + 49 True True True + + [345 rows x 3 columns] + + """ + rep_dfs = [] + for rep_exps_masks, rep in zip(replicas_exps_tr_masks, replicas): + # create flat list with all dataset masks in, then concatenate to single + # array. + all_masks = np.concatenate([ + ds_mask + for exp_masks in rep_exps_masks + for ds_mask in exp_masks + ]) + rep_dfs.append(pd.DataFrame( + all_masks[:, np.newaxis], + columns=[f"replica {rep}"], + index=experiments_index + )) + return pd.concat(rep_dfs, axis=1) + def fitting_pos_dict(posdataset): - """Loads a positivity dataset""" + """Loads a positivity dataset. For more information see + :py:func:`validphys.n3fit_data_utils.positivity_reader`. + + Parameters + ---------- + posdataset: validphys.core.PositivitySetSpec + Positivity set which is to be loaded. + + Examples + -------- + >>> from validphys.api import API + >>> posdataset = {"dataset": "POSF2U", "poslambda": 1e6} + >>> pos = API.fitting_pos_dict(posdataset=posdataset, theoryid=162) + >>> len(pos) + 9 + + """ log.info("Loading positivity dataset %s", posdataset) return positivity_reader(posdataset) @@ -324,7 +464,29 @@ def fitting_pos_dict(posdataset): #can't use collect here because integdatasets might not exist. def integdatasets_fitting_integ_dict(integdatasets=None): - """Loads a integrability dataset""" + """Loads a integrability dataset. Calls same function as + :py:func:`fitting_pos_dict`, except on each element of + ``integdatasets`` if ``integdatasets`` is not None. + + Parameters + ---------- + integdatasets: list[validphys.core.PositivitySetSpec] + list containing the settings for the integrability sets. Examples of + these can be found in the runcards located in n3fit/runcards. They have + a format similar to ``dataset_input``. + + Examples + -------- + >>> from validphys.api import API + >>> integdatasets = [{"dataset": "INTEGXT3", "poslambda": 1e2}] + >>> res = API.integdatasets_fitting_integ_dict(integdatasets=integdatasets, theoryid=53) + >>> len(res), len(res[0]) + (1, 9) + >>> res = API.integdatasets_fitting_integ_dict(integdatasets=None) + >>> print(res) + None + + """ if integdatasets is not None: integ_info = [] for integ_set in integdatasets: @@ -333,4 +495,5 @@ def integdatasets_fitting_integ_dict(integdatasets=None): integ_dict = positivity_reader(integ_set) integ_info.append(integ_dict) return integ_info + log.warning("Not using any integrability datasets.") return None From c6f587986b8c7171ac71bc52c538a0cf737539bd Mon Sep 17 00:00:00 2001 From: wilsonm Date: Tue, 9 Mar 2021 10:53:13 +0000 Subject: [PATCH 14/16] update format to be consistent within file --- validphys2/src/validphys/n3fit_data.py | 55 ++++++++++++++++---------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/validphys2/src/validphys/n3fit_data.py b/validphys2/src/validphys/n3fit_data.py index 3c223bcb1c..2cb7d976df 100644 --- a/validphys2/src/validphys/n3fit_data.py +++ b/validphys2/src/validphys/n3fit_data.py @@ -211,27 +211,40 @@ def fitting_data_dict( """ Provider which takes the information from validphys ``data``. - # Returns: - - `all_dict_out`: a dictionary containing all the information of the experiment/dataset - for training, validation and experimental - 'datasets' : list of dictionaries for each of the datasets - contained in ``data`` - 'name' : name of the ``data`` - typically experiment/group name - 'expdata_true' : non-replica data - 'invcovmat_true' : inverse of the covmat (non-replica) - - 'trmask' : mask for the training data - 'invcovmat' : inverse of the covmat for the training data - 'ndata' : number of datapoints for the training data - 'expdata' : experimental data (replica'd) for training - - 'vlmask' : (same as above for validation) - 'invcovmat_vl' : (same as above for validation) - 'ndata_vl' : (same as above for validation) - 'expdata_vl' : (same as above for validation) - - 'positivity' : bool - is this a positivity set? - 'count_chi2' : should this be counted towards the chi2 + Returns + ------- + all_dict_out: dict + Containing all the information of the experiment/dataset + for training, validation and experimental With the following keys: + + 'datasets' + list of dictionaries for each of the datasets contained in ``data`` + 'name' + name of the ``data`` - typically experiment/group name + 'expdata_true' + non-replica data + 'invcovmat_true' + inverse of the covmat (non-replica) + 'trmask' + mask for the training data + 'invcovmat' + inverse of the covmat for the training data + 'ndata' + number of datapoints for the training data + 'expdata' + experimental data (replica'd) for training + 'vlmask' + (same as above for validation) + 'invcovmat_vl' + (same as above for validation) + 'ndata_vl' + (same as above for validation) + 'expdata_vl' + (same as above for validation) + 'positivity' + bool - is this a positivity set? + 'count_chi2' + should this be counted towards the chi2 """ # TODO: Plug in the python data loading when available. Including but not # limited to: central values, ndata, replica generation, covmat construction From c59d3a80dd4a496b4052d2b4d7526d526c911360 Mon Sep 17 00:00:00 2001 From: siranipour <43517072+siranipour@users.noreply.github.com> Date: Tue, 9 Mar 2021 15:16:02 +0000 Subject: [PATCH 15/16] Adding dashes for returns section --- validphys2/src/validphys/n3fit_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/validphys2/src/validphys/n3fit_data.py b/validphys2/src/validphys/n3fit_data.py index 2cb7d976df..c2bc3f56f0 100644 --- a/validphys2/src/validphys/n3fit_data.py +++ b/validphys2/src/validphys/n3fit_data.py @@ -96,6 +96,7 @@ def kfold_masks(kpartitions, data): full list of data which is to be partitioned. Returns + ------- kfold_masks: list[np.array] A list containing a boolean array for each partition. Each array is a 1-D boolean array with length equal to the number of cut datapoints From 5183469ae11e6ba72a80a3cba50ba8bf027cf0d4 Mon Sep 17 00:00:00 2001 From: wilsonmr <33907451+wilsonmr@users.noreply.github.com> Date: Tue, 9 Mar 2021 15:47:15 +0000 Subject: [PATCH 16/16] Remove adding new axis to 1d arrays in dataframe construction --- validphys2/src/validphys/n3fit_data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validphys2/src/validphys/n3fit_data.py b/validphys2/src/validphys/n3fit_data.py index c2bc3f56f0..9d94c12839 100644 --- a/validphys2/src/validphys/n3fit_data.py +++ b/validphys2/src/validphys/n3fit_data.py @@ -372,7 +372,7 @@ def pseudodata_table(replicas_exps_pseudodata, replicas, experiments_index): for rep_exps_pseudodata, rep in zip(replicas_exps_pseudodata, replicas): all_pseudodata = np.concatenate(rep_exps_pseudodata) rep_dfs.append(pd.DataFrame( - all_pseudodata[:, np.newaxis], + all_pseudodata, columns=[f"replica {rep}"], index=experiments_index )) @@ -446,7 +446,7 @@ def training_mask_table(replicas_exps_tr_masks, replicas, experiments_index): for ds_mask in exp_masks ]) rep_dfs.append(pd.DataFrame( - all_masks[:, np.newaxis], + all_masks, columns=[f"replica {rep}"], index=experiments_index ))