From 5fdecc72ce2fa10cc3376e014a92e30f3424d6cc Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 13 Mar 2022 22:21:44 +0100 Subject: [PATCH 001/148] Move output into a subpackage --- src/eko/{output.py => output/__init__.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/eko/{output.py => output/__init__.py} (99%) diff --git a/src/eko/output.py b/src/eko/output/__init__.py similarity index 99% rename from src/eko/output.py rename to src/eko/output/__init__.py index c6a16af87..6a7de0aac 100644 --- a/src/eko/output.py +++ b/src/eko/output/__init__.py @@ -13,8 +13,8 @@ import numpy as np import yaml -from . import basis_rotation as br -from . import interpolation, version +from .. import basis_rotation as br +from .. import interpolation, version logger = logging.getLogger(__name__) From d9f5fdbef8505abe8b16ffbf2cc7aa5d94670226 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 14 Mar 2022 00:08:42 +0100 Subject: [PATCH 002/148] Split output in modules --- src/eko/output/__init__.py | 422 +---------------------------------- src/eko/output/legacy.py | 259 +++++++++++++++++++++ src/eko/output/manipulate.py | 165 ++++++++++++++ src/eko/output/struct.py | 6 + 4 files changed, 432 insertions(+), 420 deletions(-) create mode 100644 src/eko/output/legacy.py create mode 100644 src/eko/output/manipulate.py create mode 100644 src/eko/output/struct.py diff --git a/src/eko/output/__init__.py b/src/eko/output/__init__.py index 6a7de0aac..f278e2860 100644 --- a/src/eko/output/__init__.py +++ b/src/eko/output/__init__.py @@ -1,424 +1,6 @@ # -*- coding: utf-8 -*- """ -This file contains the output management +This subpackage orchestrate the calculation workflow. """ -import io -import logging -import pathlib -import tarfile -import tempfile -import warnings -import lz4.frame -import numpy as np -import yaml - -from .. import basis_rotation as br -from .. import interpolation, version - -logger = logging.getLogger(__name__) - - -class Output(dict): - """ - Wrapper for the output to help with application - to PDFs and dumping to file. - """ - - def xgrid_reshape(self, targetgrid=None, inputgrid=None): - """ - Changes the operators to have in the output targetgrid and/or in the input inputgrid. - - The operation is in-place. - - Parameters - ---------- - targetgrid : None or list - xgrid for the target - inputgrid : None or list - xgrid for the input - """ - # calling with no arguments is an error - if targetgrid is None and inputgrid is None: - raise ValueError("Nor inputgrid nor targetgrid was given") - # now check to the current status - if ( - targetgrid is not None - and len(targetgrid) == len(self["targetgrid"]) - and np.allclose(targetgrid, self["targetgrid"]) - ): - targetgrid = None - warnings.warn("The new targetgrid is close to the current targetgrid") - if ( - inputgrid is not None - and len(inputgrid) == len(self["inputgrid"]) - and np.allclose(inputgrid, self["inputgrid"]) - ): - inputgrid = None - warnings.warn("The new inputgrid is close to the current inputgrid") - # after the checks: if there is still nothing to do, skip - if targetgrid is None and inputgrid is None: - logger.debug("Nothing done.") - return - - # construct matrices - if targetgrid is not None: - b = interpolation.InterpolatorDispatcher( - self["targetgrid"], - self["interpolation_polynomial_degree"], - self["interpolation_is_log"], - False, - ) - target_rot = b.get_interpolation(targetgrid) - self["targetgrid"] = np.array(targetgrid) - if inputgrid is not None: - b = interpolation.InterpolatorDispatcher( - inputgrid, - self["interpolation_polynomial_degree"], - self["interpolation_is_log"], - False, - ) - input_rot = b.get_interpolation(self["inputgrid"]) - self["inputgrid"] = np.array(inputgrid) - - # build new grid - for elem in self["Q2grid"].values(): - ops = elem["operators"] - errs = elem["operator_errors"] - if targetgrid is not None and inputgrid is None: - ops = np.einsum("ij,ajbk->aibk", target_rot, ops) - errs = np.einsum("ij,ajbk->aibk", target_rot, errs) - elif inputgrid is not None and targetgrid is None: - ops = np.einsum("ajbk,kl->ajbl", ops, input_rot) - errs = np.einsum("ajbk,kl->ajbl", errs, input_rot) - else: - ops = np.einsum("ij,ajbk,kl->aibl", target_rot, ops, input_rot) - errs = np.einsum("ij,ajbk,kl->aibl", target_rot, errs, input_rot) - elem["operators"] = ops - elem["operator_errors"] = errs - - def flavor_reshape(self, targetbasis=None, inputbasis=None): - """ - Changes the operators to have in the output targetbasis and/or in the input inputbasis. - - The operation is in-place. - - Parameters - ---------- - targetbasis : numpy.ndarray - target rotation specified in the flavor basis - inputbasis : None or list - input rotation specified in the flavor basis - """ - # calling with no arguments is an error - if targetbasis is None and inputbasis is None: - raise ValueError("Nor inputbasis nor targetbasis was given") - # now check to the current status - if targetbasis is not None and np.allclose( - targetbasis, np.eye(len(self["targetpids"])) - ): - targetbasis = None - warnings.warn("The new targetbasis is close to current basis") - if inputbasis is not None and np.allclose( - inputbasis, np.eye(len(self["inputpids"])) - ): - inputbasis = None - warnings.warn("The new inputbasis is close to current basis") - # after the checks: if there is still nothing to do, skip - if targetbasis is None and inputbasis is None: - logger.debug("Nothing done.") - return - - # flip input around - if inputbasis is not None: - inv_inputbasis = np.linalg.inv(inputbasis) - - # build new grid - for elem in self["Q2grid"].values(): - ops = elem["operators"] - errs = elem["operator_errors"] - if targetbasis is not None and inputbasis is None: - ops = np.einsum("ca,ajbk->cjbk", targetbasis, ops) - errs = np.einsum("ca,ajbk->cjbk", targetbasis, errs) - elif inputbasis is not None and targetbasis is None: - ops = np.einsum("ajbk,bd->ajdk", ops, inv_inputbasis) - errs = np.einsum("ajbk,bd->ajdk", errs, inv_inputbasis) - else: - ops = np.einsum("ca,ajbk,bd->cjdk", targetbasis, ops, inv_inputbasis) - errs = np.einsum("ca,ajbk,bd->cjdk", targetbasis, errs, inv_inputbasis) - elem["operators"] = ops - elem["operator_errors"] = errs - # drop PIDs - keeping them int nevertheless - if inputbasis is not None: - self["inputpids"] = [0] * len(self["inputpids"]) - if targetbasis is not None: - self["targetpids"] = [0] * len(self["targetpids"]) - - def to_evol(self, source=True, target=False): - """ - Rotate the operator into evolution basis. - - This also assigns also the pids. The operation is in-place. - - Parameters - ---------- - source : bool - rotate on the input tensor - target : bool - rotate on the output tensor - """ - # rotate - inputbasis = br.rotate_flavor_to_evolution if source else None - targetbasis = br.rotate_flavor_to_evolution if target else None - self.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) - # assign pids - if source: - self["inputpids"] = br.evol_basis_pids - if target: - self["targetpids"] = br.evol_basis_pids - - def get_raw(self, binarize=True, skip_q2_grid=False): - """ - Serialize result as dict/YAML. - - This maps the original numpy matrices to lists. - - Parameters - ---------- - binarize : bool - dump in binary format (instead of list format) - - Returns - ------- - out : dict - dictionary which will be written on output - """ - # prepare output dict - out = {"Q2grid": {}, "eko_version": version.__version__} - # dump raw elements - for f in [ - "interpolation_polynomial_degree", - "interpolation_is_log", - "q2_ref", - ]: - out[f] = self[f] - - # list() work both for tuple and list - out["inputpids"] = list(self["inputpids"]) - out["targetpids"] = list(self["targetpids"]) - # make raw lists - # TODO: is interpolation_xgrid really needed in the output? - for k in ["interpolation_xgrid", "targetgrid", "inputgrid"]: - out[k] = self[k].tolist() - # make operators raw - if not skip_q2_grid: - for q2, op in self["Q2grid"].items(): - out["Q2grid"][q2] = {} - for k, v in op.items(): - if k == "alphas": - out["Q2grid"][q2][k] = float(v) - continue - if binarize: - out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) - else: - out["Q2grid"][q2][k] = v.tolist() - else: - out["Q2grid"] = self["Q2grid"] - return out - - def dump_yaml(self, stream=None, binarize=True, skip_q2_grid=False): - """ - Serialize result as YAML. - - Parameters - ---------- - stream : None or stream - if given, dump is written on it - binarize : bool - dump in binary format (instead of list format) - skip_q2_grid : bool - avoid dumping Q2grid (i.e. the actual operators) into the yaml - file (default: ``False``) - - Returns - ------- - dump : any - result of dump(output, stream), i.e. a string, if no stream is given or - Null, if written successfully to stream - """ - # TODO explicitly silence yaml - out = self.get_raw(binarize, skip_q2_grid=skip_q2_grid) - return yaml.dump(out, stream) - - def dump_yaml_to_file(self, filename, binarize=True, skip_q2_grid=False): - """ - Writes YAML representation to a file. - - Parameters - ---------- - filename : str - target file name - binarize : bool - dump in binary format (instead of list format) - skip_q2_grid : bool - avoid dumping Q2grid (i.e. the actual operators) into the yaml - file (default: ``False``) - - Returns - ------- - ret : any - result of dump(output, stream), i.e. Null if written successfully - """ - with open(filename, "w", encoding="utf-8") as f: - ret = self.dump_yaml(f, binarize, skip_q2_grid=skip_q2_grid) - return ret - - def dump_tar(self, tarname): - """ - Writes representation into a tar archive containing: - - - metadata (in YAML) - - operator (in numpy ``.npy`` format) - - Parameters - ---------- - tarname : str - target file name - """ - tarpath = pathlib.Path(tarname) - if tarpath.suffix != ".tar": - raise ValueError(f"'{tarname}' is not a valid tar filename, wrong suffix") - - with tempfile.TemporaryDirectory() as tmpdir: - tmpdir = pathlib.Path(tmpdir) - - cls = self.__class__ - metadata = cls(**{str(k): v for k, v in self.items() if k != "Q2grid"}) - metadata["Q2grid"] = list(self["Q2grid"].keys()) - - yamlname = tmpdir / "metadata.yaml" - metadata.dump_yaml_to_file(yamlname, skip_q2_grid=True) - - for kind in next(iter(self["Q2grid"].values())).keys(): - operator = np.stack([q2[kind] for q2 in self["Q2grid"].values()]) - stream = io.BytesIO() - np.save(stream, operator) - stream.seek(0) - with lz4.frame.open( - (tmpdir / kind).with_suffix(".npy.lz4"), "wb" - ) as fo: - fo.write(stream.read()) - - with tarfile.open(tarpath, "w") as tar: - tar.add(tmpdir, arcname=tarpath.stem) - - @classmethod - def load_yaml(cls, stream, skip_q2_grid=False): - """ - Load YAML representation from stream - - Parameters - ---------- - stream : any - source stream - skip_q2_grid : bool - avoid loading Q2grid (i.e. the actual operators) from the yaml - file (default: ``False``) - - Returns - ------- - obj : output - loaded object - """ - obj = yaml.safe_load(stream) - len_tpids = len(obj["targetpids"]) - len_ipids = len(obj["inputpids"]) - len_tgrid = len(obj["targetgrid"]) - len_igrid = len(obj["inputgrid"]) - # cast lists to numpy - for k in ["interpolation_xgrid", "inputgrid", "targetgrid"]: - obj[k] = np.array(obj[k]) - # make operators numpy - if not skip_q2_grid: - for op in obj["Q2grid"].values(): - for k, v in op.items(): - if k == "alphas": - v = float(v) - elif isinstance(v, list): - v = np.array(v) - elif isinstance(v, bytes): - v = np.frombuffer(lz4.frame.decompress(v)) - v = v.reshape(len_tpids, len_tgrid, len_ipids, len_igrid) - op[k] = v - return cls(obj) - - @classmethod - def load_yaml_from_file(cls, filename, skip_q2_grid=False): - """ - Load YAML representation from file - - Parameters - ---------- - filename : str - source file name - skip_q2_grid : bool - avoid loading Q2grid (i.e. the actual operators) from the yaml - file (default: ``False``) - - Returns - ------- - obj : output - loaded object - """ - obj = None - with open(filename, encoding="utf-8") as o: - obj = Output.load_yaml(o, skip_q2_grid) - return obj - - @classmethod - def load_tar(cls, tarname): - """ - Load tar representation from file (compliant with :meth:`dump_tar` - output). - - Parameters - ---------- - tarname : str - source tar name - - Returns - ------- - obj : output - loaded object - """ - tarpath = pathlib.Path(tarname) - - with tempfile.TemporaryDirectory() as tmpdir: - tmpdir = pathlib.Path(tmpdir) - - with tarfile.open(tarpath, "r") as tar: - tar.extractall(tmpdir) - - # metadata = cls(**{str(k): v for k, v in self.items() if k != "Q2grid"}) - # metadata["Q2grid"] = list(self["Q2grid"].keys()) - - innerdir = list(tmpdir.glob("*"))[0] - yamlname = innerdir / "metadata.yaml" - metadata = cls.load_yaml_from_file(yamlname, skip_q2_grid=True) - - grids = {} - for fp in innerdir.glob("*.npy.lz4"): - with lz4.frame.open(fp, "rb") as fd: - stream = io.BytesIO(fd.read()) - stream.seek(0) - grids[pathlib.Path(fp.stem).stem] = np.load(stream) - - fp.unlink() - - q2grid = metadata["Q2grid"] - operator_grid = {} - for q2, slices in zip(q2grid, zip(*grids.values())): - operator_grid[q2] = dict(zip(grids.keys(), slices)) - metadata["Q2grid"] = operator_grid - - return metadata +from .struct import Eko diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py new file mode 100644 index 000000000..9e9849ee3 --- /dev/null +++ b/src/eko/output/legacy.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 -*- +import io +import pathlib +import tarfile +import tempfile + +import lz4.frame +import numpy as np +import yaml + +from .. import version + + +def get_raw(obj, binarize=True, skip_q2_grid=False): + """ + Serialize result as dict/YAML. + + This maps the original numpy matrices to lists. + + Parameters + ---------- + binarize : bool + dump in binary format (instead of list format) + + Returns + ------- + out : dict + dictionary which will be written on output + """ + # prepare output dict + out = {"Q2grid": {}, "eko_version": version.__version__} + # dump raw elements + for f in [ + "interpolation_polynomial_degree", + "interpolation_is_log", + "q2_ref", + ]: + out[f] = obj[f] + + # list() work both for tuple and list + out["inputpids"] = list(obj["inputpids"]) + out["targetpids"] = list(obj["targetpids"]) + # make raw lists + # TODO: is interpolation_xgrid really needed in the output? + for k in ["interpolation_xgrid", "targetgrid", "inputgrid"]: + out[k] = obj[k].tolist() + # make operators raw + if not skip_q2_grid: + for q2, op in obj["Q2grid"].items(): + out["Q2grid"][q2] = {} + for k, v in op.items(): + if k == "alphas": + out["Q2grid"][q2][k] = float(v) + continue + if binarize: + out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) + else: + out["Q2grid"][q2][k] = v.tolist() + else: + out["Q2grid"] = obj["Q2grid"] + return out + + +def dump_yaml(obj, stream=None, binarize=True, skip_q2_grid=False): + """ + Serialize result as YAML. + + Parameters + ---------- + stream : None or stream + if given, dump is written on it + binarize : bool + dump in binary format (instead of list format) + skip_q2_grid : bool + avoid dumping Q2grid (i.e. the actual operators) into the yaml + file (default: ``False``) + + Returns + ------- + dump : any + result of dump(output, stream), i.e. a string, if no stream is given or + Null, if written successfully to stream + """ + # TODO explicitly silence yaml + out = obj.get_raw(binarize, skip_q2_grid=skip_q2_grid) + return yaml.dump(out, stream) + + +def dump_yaml_to_file(obj, filename, binarize=True, skip_q2_grid=False): + """ + Writes YAML representation to a file. + + Parameters + ---------- + filename : str + target file name + binarize : bool + dump in binary format (instead of list format) + skip_q2_grid : bool + avoid dumping Q2grid (i.e. the actual operators) into the yaml + file (default: ``False``) + + Returns + ------- + ret : any + result of dump(output, stream), i.e. Null if written successfully + """ + with open(filename, "w", encoding="utf-8") as f: + ret = obj.dump_yaml(f, binarize, skip_q2_grid=skip_q2_grid) + return ret + + +def dump_tar(obj, tarname): + """ + Writes representation into a tar archive containing: + + - metadata (in YAML) + - operator (in numpy ``.npy`` format) + + Parameters + ---------- + tarname : str + target file name + """ + tarpath = pathlib.Path(tarname) + if tarpath.suffix != ".tar": + raise ValueError(f"'{tarname}' is not a valid tar filename, wrong suffix") + + with tempfile.TemporaryDirectory() as tmpdir: + tmpdir = pathlib.Path(tmpdir) + + cls = obj.__class__ + metadata = cls(**{str(k): v for k, v in obj.items() if k != "Q2grid"}) + metadata["Q2grid"] = list(obj["Q2grid"].keys()) + + yamlname = tmpdir / "metadata.yaml" + metadata.dump_yaml_to_file(yamlname, skip_q2_grid=True) + + for kind in next(iter(obj["Q2grid"].values())).keys(): + operator = np.stack([q2[kind] for q2 in obj["Q2grid"].values()]) + stream = io.BytesIO() + np.save(stream, operator) + stream.seek(0) + with lz4.frame.open((tmpdir / kind).with_suffix(".npy.lz4"), "wb") as fo: + fo.write(stream.read()) + + with tarfile.open(tarpath, "w") as tar: + tar.add(tmpdir, arcname=tarpath.stem) + + +def load_yaml(stream, skip_q2_grid=False): + """ + Load YAML representation from stream + + Parameters + ---------- + stream : any + source stream + skip_q2_grid : bool + avoid loading Q2grid (i.e. the actual operators) from the yaml + file (default: ``False``) + + Returns + ------- + obj : output + loaded object + """ + obj = yaml.safe_load(stream) + len_tpids = len(obj["targetpids"]) + len_ipids = len(obj["inputpids"]) + len_tgrid = len(obj["targetgrid"]) + len_igrid = len(obj["inputgrid"]) + # cast lists to numpy + for k in ["interpolation_xgrid", "inputgrid", "targetgrid"]: + obj[k] = np.array(obj[k]) + # make operators numpy + if not skip_q2_grid: + for op in obj["Q2grid"].values(): + for k, v in op.items(): + if k == "alphas": + v = float(v) + elif isinstance(v, list): + v = np.array(v) + elif isinstance(v, bytes): + v = np.frombuffer(lz4.frame.decompress(v)) + v = v.reshape(len_tpids, len_tgrid, len_ipids, len_igrid) + op[k] = v + return cls(obj) + + +def load_yaml_from_file(filename, skip_q2_grid=False): + """ + Load YAML representation from file + + Parameters + ---------- + filename : str + source file name + skip_q2_grid : bool + avoid loading Q2grid (i.e. the actual operators) from the yaml + file (default: ``False``) + + Returns + ------- + obj : output + loaded object + """ + obj = None + with open(filename, encoding="utf-8") as o: + obj = load_yaml(o, skip_q2_grid) + return obj + + +def load_tar(tarname): + """ + Load tar representation from file (compliant with :meth:`dump_tar` + output). + + Parameters + ---------- + tarname : str + source tar name + + Returns + ------- + obj : output + loaded object + """ + tarpath = pathlib.Path(tarname) + + with tempfile.TemporaryDirectory() as tmpdir: + tmpdir = pathlib.Path(tmpdir) + + with tarfile.open(tarpath, "r") as tar: + tar.extractall(tmpdir) + + # metadata = cls(**{str(k): v for k, v in obj.items() if k != "Q2grid"}) + # metadata["Q2grid"] = list(obj["Q2grid"].keys()) + + innerdir = list(tmpdir.glob("*"))[0] + yamlname = innerdir / "metadata.yaml" + metadata = load_yaml_from_file(yamlname, skip_q2_grid=True) + + grids = {} + for fp in innerdir.glob("*.npy.lz4"): + with lz4.frame.open(fp, "rb") as fd: + stream = io.BytesIO(fd.read()) + stream.seek(0) + grids[pathlib.Path(fp.stem).stem] = np.load(stream) + + fp.unlink() + + q2grid = metadata["Q2grid"] + operator_grid = {} + for q2, slices in zip(q2grid, zip(*grids.values())): + operator_grid[q2] = dict(zip(grids.keys(), slices)) + metadata["Q2grid"] = operator_grid + + return metadata diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py new file mode 100644 index 000000000..aa1e79f79 --- /dev/null +++ b/src/eko/output/manipulate.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +import logging +import warnings + +import numpy as np + +from .. import basis_rotation as br +from .. import interpolation + +logger = logging.getLogger(__name__) + + +def xgrid_reshape(obj, targetgrid=None, inputgrid=None): + """ + Changes the operators to have in the output targetgrid and/or in the input inputgrid. + + The operation is in-place. + + Parameters + ---------- + targetgrid : None or list + xgrid for the target + inputgrid : None or list + xgrid for the input + """ + # calling with no arguments is an error + if targetgrid is None and inputgrid is None: + raise ValueError("Nor inputgrid nor targetgrid was given") + # now check to the current status + if ( + targetgrid is not None + and len(targetgrid) == len(obj["targetgrid"]) + and np.allclose(targetgrid, obj["targetgrid"]) + ): + targetgrid = None + warnings.warn("The new targetgrid is close to the current targetgrid") + if ( + inputgrid is not None + and len(inputgrid) == len(obj["inputgrid"]) + and np.allclose(inputgrid, obj["inputgrid"]) + ): + inputgrid = None + warnings.warn("The new inputgrid is close to the current inputgrid") + # after the checks: if there is still nothing to do, skip + if targetgrid is None and inputgrid is None: + logger.debug("Nothing done.") + return + + # construct matrices + if targetgrid is not None: + b = interpolation.InterpolatorDispatcher( + obj["targetgrid"], + obj["interpolation_polynomial_degree"], + obj["interpolation_is_log"], + False, + ) + target_rot = b.get_interpolation(targetgrid) + obj["targetgrid"] = np.array(targetgrid) + if inputgrid is not None: + b = interpolation.InterpolatorDispatcher( + inputgrid, + obj["interpolation_polynomial_degree"], + obj["interpolation_is_log"], + False, + ) + input_rot = b.get_interpolation(obj["inputgrid"]) + obj["inputgrid"] = np.array(inputgrid) + + # build new grid + for elem in obj["Q2grid"].values(): + ops = elem["operators"] + errs = elem["operator_errors"] + if targetgrid is not None and inputgrid is None: + ops = np.einsum("ij,ajbk->aibk", target_rot, ops) + errs = np.einsum("ij,ajbk->aibk", target_rot, errs) + elif inputgrid is not None and targetgrid is None: + ops = np.einsum("ajbk,kl->ajbl", ops, input_rot) + errs = np.einsum("ajbk,kl->ajbl", errs, input_rot) + else: + ops = np.einsum("ij,ajbk,kl->aibl", target_rot, ops, input_rot) + errs = np.einsum("ij,ajbk,kl->aibl", target_rot, errs, input_rot) + elem["operators"] = ops + elem["operator_errors"] = errs + + +def flavor_reshape(obj, targetbasis=None, inputbasis=None): + """ + Changes the operators to have in the output targetbasis and/or in the input inputbasis. + + The operation is in-place. + + Parameters + ---------- + targetbasis : numpy.ndarray + target rotation specified in the flavor basis + inputbasis : None or list + input rotation specified in the flavor basis + """ + # calling with no arguments is an error + if targetbasis is None and inputbasis is None: + raise ValueError("Nor inputbasis nor targetbasis was given") + # now check to the current status + if targetbasis is not None and np.allclose( + targetbasis, np.eye(len(obj["targetpids"])) + ): + targetbasis = None + warnings.warn("The new targetbasis is close to current basis") + if inputbasis is not None and np.allclose( + inputbasis, np.eye(len(obj["inputpids"])) + ): + inputbasis = None + warnings.warn("The new inputbasis is close to current basis") + # after the checks: if there is still nothing to do, skip + if targetbasis is None and inputbasis is None: + logger.debug("Nothing done.") + return + + # flip input around + if inputbasis is not None: + inv_inputbasis = np.linalg.inv(inputbasis) + + # build new grid + for elem in obj["Q2grid"].values(): + ops = elem["operators"] + errs = elem["operator_errors"] + if targetbasis is not None and inputbasis is None: + ops = np.einsum("ca,ajbk->cjbk", targetbasis, ops) + errs = np.einsum("ca,ajbk->cjbk", targetbasis, errs) + elif inputbasis is not None and targetbasis is None: + ops = np.einsum("ajbk,bd->ajdk", ops, inv_inputbasis) + errs = np.einsum("ajbk,bd->ajdk", errs, inv_inputbasis) + else: + ops = np.einsum("ca,ajbk,bd->cjdk", targetbasis, ops, inv_inputbasis) + errs = np.einsum("ca,ajbk,bd->cjdk", targetbasis, errs, inv_inputbasis) + elem["operators"] = ops + elem["operator_errors"] = errs + # drop PIDs - keeping them int nevertheless + if inputbasis is not None: + obj["inputpids"] = [0] * len(obj["inputpids"]) + if targetbasis is not None: + obj["targetpids"] = [0] * len(obj["targetpids"]) + + +def to_evol(obj, source=True, target=False): + """ + Rotate the operator into evolution basis. + + This also assigns also the pids. The operation is in-place. + + Parameters + ---------- + source : bool + rotate on the input tensor + target : bool + rotate on the output tensor + """ + # rotate + inputbasis = br.rotate_flavor_to_evolution if source else None + targetbasis = br.rotate_flavor_to_evolution if target else None + obj.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) + # assign pids + if source: + obj["inputpids"] = br.evol_basis_pids + if target: + obj["targetpids"] = br.evol_basis_pids diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py new file mode 100644 index 000000000..939eef3f7 --- /dev/null +++ b/src/eko/output/struct.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +class Eko: + """ + Wrapper for the output to help with application + to PDFs and dumping to file. + """ From 10b50f769d08f63e2d678f6388d9fcfa6d67c231 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:43:26 +0200 Subject: [PATCH 003/148] Break output into modules (and break everything) --- src/eko/output/__init__.py | 3 +- src/eko/output/manipulate.py | 66 ++++++++------- src/eko/output/struct.py | 54 +++++++++++- tests/eko/test_output.py | 155 ++++++++++++++++++----------------- tests/eko/test_runner.py | 17 ++-- 5 files changed, 176 insertions(+), 119 deletions(-) diff --git a/src/eko/output/__init__.py b/src/eko/output/__init__.py index f278e2860..312c35bb0 100644 --- a/src/eko/output/__init__.py +++ b/src/eko/output/__init__.py @@ -3,4 +3,5 @@ This subpackage orchestrate the calculation workflow. """ -from .struct import Eko +from . import manipulate +from .struct import EKO diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index aa1e79f79..386815fd7 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) -def xgrid_reshape(obj, targetgrid=None, inputgrid=None): +def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): """ Changes the operators to have in the output targetgrid and/or in the input inputgrid. @@ -29,15 +29,15 @@ def xgrid_reshape(obj, targetgrid=None, inputgrid=None): # now check to the current status if ( targetgrid is not None - and len(targetgrid) == len(obj["targetgrid"]) - and np.allclose(targetgrid, obj["targetgrid"]) + and len(targetgrid) == len(eko.targetgrid) + and np.allclose(targetgrid, eko.targetgrid) ): targetgrid = None warnings.warn("The new targetgrid is close to the current targetgrid") if ( inputgrid is not None - and len(inputgrid) == len(obj["inputgrid"]) - and np.allclose(inputgrid, obj["inputgrid"]) + and len(inputgrid) == len(eko.inputgrid) + and np.allclose(inputgrid, eko.inputgrid) ): inputgrid = None warnings.warn("The new inputgrid is close to the current inputgrid") @@ -49,27 +49,27 @@ def xgrid_reshape(obj, targetgrid=None, inputgrid=None): # construct matrices if targetgrid is not None: b = interpolation.InterpolatorDispatcher( - obj["targetgrid"], - obj["interpolation_polynomial_degree"], - obj["interpolation_is_log"], + eko.targetgrid, + eko.interpolation_polynomial_degree, + eko.interpolation_is_log, False, ) target_rot = b.get_interpolation(targetgrid) - obj["targetgrid"] = np.array(targetgrid) + eko.targetgrid = np.array(targetgrid) if inputgrid is not None: b = interpolation.InterpolatorDispatcher( inputgrid, - obj["interpolation_polynomial_degree"], - obj["interpolation_is_log"], + eko.interpolation_polynomial_degree, + eko.interpolation_is_log, False, ) - input_rot = b.get_interpolation(obj["inputgrid"]) - obj["inputgrid"] = np.array(inputgrid) + input_rot = b.get_interpolation(eko.inputgrid) + eko.inputgrid = np.array(inputgrid) # build new grid - for elem in obj["Q2grid"].values(): - ops = elem["operators"] - errs = elem["operator_errors"] + for elem in eko.Q2grid.values(): + ops = elem.operators + errs = elem.operator_errors if targetgrid is not None and inputgrid is None: ops = np.einsum("ij,ajbk->aibk", target_rot, ops) errs = np.einsum("ij,ajbk->aibk", target_rot, errs) @@ -79,11 +79,11 @@ def xgrid_reshape(obj, targetgrid=None, inputgrid=None): else: ops = np.einsum("ij,ajbk,kl->aibl", target_rot, ops, input_rot) errs = np.einsum("ij,ajbk,kl->aibl", target_rot, errs, input_rot) - elem["operators"] = ops - elem["operator_errors"] = errs + elem.operators = ops + elem.operator_errors = errs -def flavor_reshape(obj, targetbasis=None, inputbasis=None): +def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): """ Changes the operators to have in the output targetbasis and/or in the input inputbasis. @@ -101,13 +101,11 @@ def flavor_reshape(obj, targetbasis=None, inputbasis=None): raise ValueError("Nor inputbasis nor targetbasis was given") # now check to the current status if targetbasis is not None and np.allclose( - targetbasis, np.eye(len(obj["targetpids"])) + targetbasis, np.eye(len(eko.targetpids)) ): targetbasis = None warnings.warn("The new targetbasis is close to current basis") - if inputbasis is not None and np.allclose( - inputbasis, np.eye(len(obj["inputpids"])) - ): + if inputbasis is not None and np.allclose(inputbasis, np.eye(len(eko.inputpids))): inputbasis = None warnings.warn("The new inputbasis is close to current basis") # after the checks: if there is still nothing to do, skip @@ -120,9 +118,9 @@ def flavor_reshape(obj, targetbasis=None, inputbasis=None): inv_inputbasis = np.linalg.inv(inputbasis) # build new grid - for elem in obj["Q2grid"].values(): - ops = elem["operators"] - errs = elem["operator_errors"] + for elem in eko.Q2grid.values(): + ops = elem.operators + errs = elem.operator_errors if targetbasis is not None and inputbasis is None: ops = np.einsum("ca,ajbk->cjbk", targetbasis, ops) errs = np.einsum("ca,ajbk->cjbk", targetbasis, errs) @@ -132,16 +130,16 @@ def flavor_reshape(obj, targetbasis=None, inputbasis=None): else: ops = np.einsum("ca,ajbk,bd->cjdk", targetbasis, ops, inv_inputbasis) errs = np.einsum("ca,ajbk,bd->cjdk", targetbasis, errs, inv_inputbasis) - elem["operators"] = ops - elem["operator_errors"] = errs + elem.operators = ops + elem.operator_errors = errs # drop PIDs - keeping them int nevertheless if inputbasis is not None: - obj["inputpids"] = [0] * len(obj["inputpids"]) + eko.inputpids = [0] * len(eko.inputpids) if targetbasis is not None: - obj["targetpids"] = [0] * len(obj["targetpids"]) + eko.targetpids = [0] * len(eko.targetpids) -def to_evol(obj, source=True, target=False): +def to_evol(eko, source=True, target=False, inplace=True): """ Rotate the operator into evolution basis. @@ -157,9 +155,9 @@ def to_evol(obj, source=True, target=False): # rotate inputbasis = br.rotate_flavor_to_evolution if source else None targetbasis = br.rotate_flavor_to_evolution if target else None - obj.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) + eko.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) # assign pids if source: - obj["inputpids"] = br.evol_basis_pids + eko.inputpids = br.evol_basis_pids if target: - obj["targetpids"] = br.evol_basis_pids + eko.targetpids = br.evol_basis_pids diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 939eef3f7..5126b4a44 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,6 +1,58 @@ # -*- coding: utf-8 -*- -class Eko: +from dataclasses import dataclass +from typing import Dict, Optional + +import numpy as np + + +@dataclass +class Operator: + alphas: float + operators: np.ndarray + operator_errors: Optional[np.ndarray] = None + + @classmethod + def from_dict(cls, dictionary): + return cls(**dictionary) + + +@dataclass +class Debug: + skip_singlet: bool + skip_non_singlet: bool + + +@dataclass +class Configs: + ev_op_max_order: int + ev_op_iterations: int + interpolation_polynomial_degree: int + interpolation_is_log: bool + backward_inversion: str + + +@dataclass +class EKO: """ Wrapper for the output to help with application to PDFs and dumping to file. """ + + xgrid: np.ndarray + targetgrid: np.ndarray + inputgrid: np.ndarray + targetpids: np.ndarray + inputpids: np.ndarray + Q02: float + Q2grid: Dict[float, Operator] + # _operators: Dict[float, Optional[Operator]] + configs: Configs + debug: Debug + + # @property + # def Q2grid(self): + # return np.ndarray(self._operators.keys()) + + @classmethod + def from_dict(cls, dictionary): + return cls(**dictionary) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 6e7fc2617..1d21b6fe1 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -11,6 +11,7 @@ from eko import basis_rotation as br from eko import output +from eko.output import legacy, manipulate def eko_identity(shape): @@ -23,101 +24,103 @@ def eko_identity(shape): def chk_keys(a, b): """Check all keys are preserved""" assert sorted(a.keys()) == sorted(b.keys()) - for q2, op in a["Q2grid"].items(): - assert q2 in b["Q2grid"] - opb = b["Q2grid"][q2] + for q2, op in a.Q2grid.items(): + assert q2 in b.Q2grid + opb = b.Q2grid[q2] assert sorted(op.keys()) == sorted(opb.keys()) assert op["alphas"] == opb["alphas"] -class TestOutput: +class TestLegacy: def test_io(self, fake_output): # create object - o1 = output.Output(fake_output) + o1 = output.EKO.from_dict(fake_output) # test streams stream = io.StringIO() - o1.dump_yaml(stream) + legacy.dump_yaml(o1, stream) # rewind and read again stream.seek(0) - o2 = output.Output.load_yaml(stream) + o2 = legacy.load_yaml(stream) np.testing.assert_almost_equal( - o1["interpolation_xgrid"], fake_output["interpolation_xgrid"] + o1.interpolation_xgrid, fake_output["interpolation_xgrid"] ) np.testing.assert_almost_equal( - o2["interpolation_xgrid"], fake_output["interpolation_xgrid"] + o2.interpolation_xgrid, fake_output["interpolation_xgrid"] ) # fake output files m_out = mock.mock_open(read_data="") with mock.patch("builtins.open", m_out) as mock_file: fn = "test.yaml" - o1.dump_yaml_to_file(fn) + legacy.dump_yaml_to_file(o1, fn) mock_file.assert_called_with(fn, "w", encoding="utf-8") # fake input file stream.seek(0) m_in = mock.mock_open(read_data=stream.getvalue()) with mock.patch("builtins.open", m_in) as mock_file: fn = "test.yaml" - o3 = output.Output.load_yaml_from_file(fn) + o3 = legacy.load_yaml_from_file(fn) mock_file.assert_called_with(fn, encoding="utf-8") np.testing.assert_almost_equal( - o3["interpolation_xgrid"], fake_output["interpolation_xgrid"] + o3.interpolation_xgrid, fake_output["interpolation_xgrid"] ) # repeat for tar fn = "test.tar" with tempfile.TemporaryDirectory() as folder: fp = pathlib.Path(folder) / fn - o1.dump_tar(fp) - o4 = output.Output.load_tar(fp) + legacy.dump_tar(o1, fp) + o4 = legacy.load_tar(fp) np.testing.assert_almost_equal( - o4["interpolation_xgrid"], fake_output["interpolation_xgrid"] + o4.interpolation_xgrid, fake_output["interpolation_xgrid"] ) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): - o1.dump_tar(fn) + legacy.dump_tar(o1, fn) def test_rename_issue81(self, fake_output): # https://github.com/N3PDF/eko/issues/81 # create object - o1 = output.Output(fake_output) + o1 = output.EKO.from_dict(fake_output) with tempfile.TemporaryDirectory() as folder: # dump p = pathlib.Path(folder) fp1 = p / "test1.tar" fp2 = p / "test2.tar" - o1.dump_tar(fp1) + legacy.dump_tar(o1, fp1) # rename shutil.move(fp1, fp2) # reload - o4 = output.Output.load_tar(fp2) + o4 = legacy.load_tar(fp2) np.testing.assert_almost_equal( o4["interpolation_xgrid"], fake_output["interpolation_xgrid"] ) def test_io_bin(self, fake_output): # create object - o1 = output.Output(fake_output) + o1 = output.EKO.from_dict(fake_output) # test streams stream = io.StringIO() o1.dump_yaml(stream, False) # rewind and read again stream.seek(0) - o2 = output.Output.load_yaml(stream) + o2 = legacy.load_yaml(stream) np.testing.assert_almost_equal( - o1["interpolation_xgrid"], fake_output["interpolation_xgrid"] + o1.interpolation_xgrid, fake_output.interpolation_xgrid ) np.testing.assert_almost_equal( - o2["interpolation_xgrid"], fake_output["interpolation_xgrid"] + o2.interpolation_xgrid, fake_output.interpolation_xgrid ) + +class TestManipulate: def test_xgrid_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) - o1 = output.Output(fake_output) - o1["interpolation_xgrid"] = xg - o1["targetgrid"] = xg - o1["inputgrid"] = xg - o1["Q2grid"] = { + o1 = output.EKO.from_dict(fake_output) + o1.interpolation_xgrid = xg + o1.targetgrid = xg + o1.inputgrid = xg + o1.Q2grid = { 10: dict( operators=eko_identity([1, 2, len(xg), 2, len(xg)])[0], operator_errors=np.zeros((2, len(xg), 2, len(xg))), @@ -127,52 +130,52 @@ def test_xgrid_reshape(self, fake_output): xgp = np.geomspace(1e-5, 1.0, 11) # only target ot = copy.deepcopy(o1) - ot.xgrid_reshape(xgp) + manipulate.xgrid_reshape(ot, xgp, inplace=True) chk_keys(o1, ot) assert ot["Q2grid"][10]["operators"].shape == (2, len(xgp), 2, len(xg)) ott = copy.deepcopy(o1) with pytest.warns(Warning): - ott.xgrid_reshape(xg) + manipulate.xgrid_reshape(ott, xg) chk_keys(o1, ott) np.testing.assert_allclose( - ott["Q2grid"][10]["operators"], o1["Q2grid"][10]["operators"] + ott.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] ) # only input oi = copy.deepcopy(o1) - oi.xgrid_reshape(inputgrid=xgp) - assert oi["Q2grid"][10]["operators"].shape == (2, len(xg), 2, len(xgp)) + manipulate.xgrid_reshape(o1, inputgrid=xgp) + assert oi.Q2grid[10]["operators"].shape == (2, len(xg), 2, len(xgp)) chk_keys(o1, oi) oii = copy.deepcopy(o1) with pytest.warns(Warning): - oii.xgrid_reshape(inputgrid=xg) + manipulate.xgrid_reshape(oii, inputgrid=xg) chk_keys(o1, oii) np.testing.assert_allclose( - oii["Q2grid"][10]["operators"], o1["Q2grid"][10]["operators"] + oii.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] ) # both oit = copy.deepcopy(o1) - oit.xgrid_reshape(xgp, xgp) + manipulate.xgrid_reshape(oit, xgp, xgp) chk_keys(o1, oit) op = eko_identity([1, 2, len(xgp), 2, len(xgp)]) - np.testing.assert_allclose(oit["Q2grid"][10]["operators"], op[0], atol=1e-10) + np.testing.assert_allclose(oit.Q2grid[10]["operators"], op[0], atol=1e-10) # error with pytest.raises(ValueError): - copy.deepcopy(o1).xgrid_reshape() + manipulate.xgrid_reshape(copy.deepcopy(o1)) def test_reshape_io(self, fake_output): # create object - o1 = output.Output(fake_output) + o1 = output.EKO.from_dict(fake_output) o2 = copy.deepcopy(o1) - o2.xgrid_reshape([0.1, 1.0], [0.1, 1.0]) - o2.flavor_reshape(inputbasis=np.array([[1, -1], [1, 1]])) + manipulate.xgrid_reshape(o2, [0.1, 1.0], [0.1, 1.0]) + manipulate.flavor_reshape(o2, inputbasis=np.array([[1, -1], [1, 1]])) # dump stream = io.StringIO() - o2.dump_yaml(stream) + legacy.dump_yaml(o2, stream) # reload stream.seek(0) - o3 = output.Output.load_yaml(stream) + o3 = legacy.load_yaml(stream) # eko_version is only added in get_raw del o3["eko_version"] chk_keys(o1, o3) @@ -180,11 +183,11 @@ def test_reshape_io(self, fake_output): def test_flavor_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) - o1 = output.Output(fake_output) - o1["interpolation_xgrid"] = xg - o1["targetgrid"] = xg - o1["inputgrid"] = xg - o1["Q2grid"] = { + o1 = output.EKO.from_dict(fake_output) + o1.interpolation_xgrid = xg + o1.targetgrid = xg + o1.inputgrid = xg + o1.Q2grid = { 10: dict( operators=eko_identity([1, 2, len(xg), 2, len(xg)])[0], operator_errors=np.zeros((2, len(xg), 2, len(xg))), @@ -194,48 +197,50 @@ def test_flavor_reshape(self, fake_output): # only target target_r = np.array([[1, -1], [1, 1]]) ot = copy.deepcopy(o1) - ot.flavor_reshape(target_r) + manipulate.flavor_reshape(ot, target_r) chk_keys(o1, ot) - assert ot["Q2grid"][10]["operators"].shape == (2, len(xg), 2, len(xg)) + assert ot.Q2grid[10]["operators"].shape == (2, len(xg), 2, len(xg)) ott = copy.deepcopy(ot) - ott.flavor_reshape(np.linalg.inv(target_r)) + manipulate.flavor_reshape(ott, np.linalg.inv(target_r)) np.testing.assert_allclose( - ott["Q2grid"][10]["operators"], o1["Q2grid"][10]["operators"] + ott.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] ) with pytest.warns(Warning): - ott.flavor_reshape(np.eye(2)) + manipulate.flavor_reshape(ott, np.eye(2)) chk_keys(o1, ott) np.testing.assert_allclose( - ott["Q2grid"][10]["operators"], o1["Q2grid"][10]["operators"] + ott.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] ) # only input input_r = np.array([[1, -1], [1, 1]]) oi = copy.deepcopy(o1) - oi.flavor_reshape(inputbasis=input_r) + manipulate.flavor_reshape(oi, inputbasis=input_r) chk_keys(o1, oi) - assert oi["Q2grid"][10]["operators"].shape == (2, len(xg), 2, len(xg)) + assert oi.Q2grid[10]["operators"].shape == (2, len(xg), 2, len(xg)) oii = copy.deepcopy(oi) - oii.flavor_reshape(inputbasis=np.linalg.inv(input_r)) + manipulate.flavor_reshape(oii, inputbasis=np.linalg.inv(input_r)) np.testing.assert_allclose( - oii["Q2grid"][10]["operators"], o1["Q2grid"][10]["operators"] + oii.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] ) with pytest.warns(Warning): - oii.flavor_reshape(inputbasis=np.eye(2)) + manipulate.flavor_reshape(oii, inputbasis=np.eye(2)) chk_keys(o1, oii) np.testing.assert_allclose( - oii["Q2grid"][10]["operators"], o1["Q2grid"][10]["operators"] + oii.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] ) # both oit = copy.deepcopy(o1) - oit.flavor_reshape(np.array([[1, -1], [1, 1]]), np.array([[1, -1], [1, 1]])) + manipulate.flavor_reshape( + oit, np.array([[1, -1], [1, 1]]), np.array([[1, -1], [1, 1]]) + ) chk_keys(o1, oit) op = eko_identity([1, 2, len(xg), 2, len(xg)]).copy() - np.testing.assert_allclose(oit["Q2grid"][10]["operators"], op[0], atol=1e-10) + np.testing.assert_allclose(oit.Q2grid[10]["operators"], op[0], atol=1e-10) # error with pytest.raises(ValueError): - copy.deepcopy(o1).flavor_reshape() + manipulate.flavor_reshape(copy.deepcopy(o1)) def test_to_evol(self, fake_factory): interpolation_xgrid = np.array([0.5, 1.0]) @@ -257,30 +262,30 @@ def test_to_evol(self, fake_factory): targetpids=br.flavor_basis_pids, Q2grid=Q2grid, ) - o00 = output.Output(d) + o00 = output.EKO.from_dict(d) o01 = copy.copy(o00) - o01.to_evol() + manipulate.to_evol(o01) o10 = copy.copy(o00) - o10.to_evol(False, True) + manipulate.to_evol(o10, False, True) o11 = copy.copy(o00) - o11.to_evol(True, True) + manipulate.to_evol(o11, True, True) chk_keys(o00, o11) # check the input rotated one - np.testing.assert_allclose(o01["inputpids"], br.evol_basis_pids) - np.testing.assert_allclose(o01["targetpids"], br.flavor_basis_pids) + np.testing.assert_allclose(o01.inputpids, br.evol_basis_pids) + np.testing.assert_allclose(o01.targetpids, br.flavor_basis_pids) # rotate also target - o01.to_evol(False, True) + manipulate.to_evol(o01, False, True) np.testing.assert_allclose( - o01["Q2grid"][q2_out]["operators"], o11["Q2grid"][q2_out]["operators"] + o01.Q2grid[q2_out]["operators"], o11.Q2grid[q2_out]["operators"] ) chk_keys(o00, o01) # check the target rotated one - np.testing.assert_allclose(o10["inputpids"], br.flavor_basis_pids) - np.testing.assert_allclose(o10["targetpids"], br.evol_basis_pids) + np.testing.assert_allclose(o10.inputpids, br.flavor_basis_pids) + np.testing.assert_allclose(o10.targetpids, br.evol_basis_pids) # rotate also input - o10.to_evol() + manipulate.to_evol(o10) np.testing.assert_allclose( - o10["Q2grid"][q2_out]["operators"], o11["Q2grid"][q2_out]["operators"] + o10.Q2grid[q2_out]["operators"], o11.Q2grid[q2_out]["operators"] ) chk_keys(o00, o10) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index dc09c214c..6b5c66d5f 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -38,14 +38,15 @@ operators_card = { "Q2grid": [10, 100], "interpolation_xgrid": [0.01, 0.1, 1.0], - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": True, - "debug_skip_non_singlet": True, - "ev_op_max_order": (2, 0), - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": (2, 0), + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": {"skip_singlet": True, "skip_non_singlet": True}, } From dd9443047ae1867dd6e6da1f5a1822583306f7e6 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:45:39 +0200 Subject: [PATCH 004/148] Add new output serialization --- src/eko/output/legacy.py | 5 ++- src/eko/output/struct.py | 80 +++++++++++++++++++++++++++++----------- src/eko/runner.py | 6 +-- tests/conftest.py | 24 ++++++++---- tests/eko/test_output.py | 32 +++++----------- 5 files changed, 90 insertions(+), 57 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 9e9849ee3..86a5030be 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -9,6 +9,7 @@ import yaml from .. import version +from . import struct def get_raw(obj, binarize=True, skip_q2_grid=False): @@ -82,7 +83,7 @@ def dump_yaml(obj, stream=None, binarize=True, skip_q2_grid=False): Null, if written successfully to stream """ # TODO explicitly silence yaml - out = obj.get_raw(binarize, skip_q2_grid=skip_q2_grid) + out = get_raw(obj, binarize, skip_q2_grid=skip_q2_grid) return yaml.dump(out, stream) @@ -185,7 +186,7 @@ def load_yaml(stream, skip_q2_grid=False): v = np.frombuffer(lz4.frame.decompress(v)) v = v.reshape(len_tpids, len_tgrid, len_ipids, len_igrid) op[k] = v - return cls(obj) + return struct.EKO.from_dict(obj) def load_yaml_from_file(filename, skip_q2_grid=False): diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 5126b4a44..66cd8b59e 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,34 +1,56 @@ # -*- coding: utf-8 -*- -from dataclasses import dataclass -from typing import Dict, Optional +from dataclasses import dataclass, fields +from typing import Dict, Literal, Optional import numpy as np -@dataclass -class Operator: - alphas: float - operators: np.ndarray - operator_errors: Optional[np.ndarray] = None +class DictLike: + def __init__(self, **kwargs): + pass @classmethod def from_dict(cls, dictionary): return cls(**dictionary) + @property + def raw(self): + dictionary = {} + for field in fields(self): + value = getattr(self, field.name) + dictionary[field.name] = ( + value if not isinstance(value, np.ndarray) else value.tolist() + ) + + +@dataclass +class Operator(DictLike): + alphas: float + operators: np.ndarray + operator_errors: Optional[np.ndarray] = None + @dataclass -class Debug: - skip_singlet: bool - skip_non_singlet: bool +class Debug(DictLike): + skip_singlet: bool = False + skip_non_singlet: bool = False @dataclass -class Configs: +class Configs(DictLike): ev_op_max_order: int ev_op_iterations: int interpolation_polynomial_degree: int interpolation_is_log: bool - backward_inversion: str + backward_inversion: Literal["exact", "expanded"] + + +@dataclass +class Rotations(DictLike): + targetgrid: Optional[np.ndarray] = None + inputgrid: Optional[np.ndarray] = None + targetpids: Optional[np.ndarray] = None + inputpids: Optional[np.ndarray] = None @dataclass @@ -39,20 +61,34 @@ class EKO: """ xgrid: np.ndarray - targetgrid: np.ndarray - inputgrid: np.ndarray - targetpids: np.ndarray - inputpids: np.ndarray Q02: float - Q2grid: Dict[float, Operator] - # _operators: Dict[float, Optional[Operator]] + _operators: Dict[float, Optional[Operator]] configs: Configs + rotations: Rotations debug: Debug - # @property - # def Q2grid(self): - # return np.ndarray(self._operators.keys()) + @property + def Q2grid(self): + return np.array(self._operators.keys()) @classmethod def from_dict(cls, dictionary): - return cls(**dictionary) + return cls( + xgrid=dictionary["xgrid"], + Q02=dictionary["Q0"] ** 2, + _operators={q2: None for q2 in dictionary["Q2grid"]}, + configs=Configs.from_dict(dictionary["configs"]), + rotations=Rotations.from_dict(dictionary["rotations"]), + debug=Debug.from_dict(dictionary.get("debug", {})), + ) + + @property + def raw(self): + return dict( + xgrid=self.xgrid.tolist(), + Q0=np.sqrt(self.Q02), + Q2grid=self.Q2grid, + configs=self.configs.raw, + rotations=self.rotations.raw, + debug=self.debug.raw, + ) diff --git a/src/eko/runner.py b/src/eko/runner.py index 2270f737d..45cc42222 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -11,7 +11,7 @@ from . import compatibility, interpolation, msbar_masses from .couplings import Couplings from .evolution_operator.grid import OperatorGrid -from .output import Output +from .output import EKO from .thresholds import ThresholdsAtlas logger = logging.getLogger(__name__) @@ -40,7 +40,7 @@ class Runner: """ def __init__(self, theory_card, operators_card): - self.out = Output() + self.out = EKO() new_theory, new_operators = compatibility.update(theory_card, operators_card) @@ -82,7 +82,7 @@ def get_output(self): Returns ------- - ret : eko.output.Output + ret : eko.output.EKO output instance """ # add all operators diff --git a/tests/conftest.py b/tests/conftest.py index 62d23882d..3ca998106 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -43,14 +43,22 @@ def fake_output(self): q2_out = 2 Q2grid = self.mk_g([q2_out], len(pids), len(interpolation_xgrid)) d = dict( - interpolation_xgrid=interpolation_xgrid, - targetgrid=interpolation_xgrid, - inputgrid=interpolation_xgrid, - interpolation_polynomial_degree=interpolation_polynomial_degree, - interpolation_is_log=interpolation_is_log, - q2_ref=q2_ref, - inputpids=pids, - targetpids=pids, + xgrid=interpolation_xgrid, + rotations=dict( + targetgrid=interpolation_xgrid, + inputgrid=interpolation_xgrid, + inputpids=pids, + targetpids=pids, + ), + Q0=np.sqrt(q2_ref), + couplings=dict(), + configs=dict( + ev_op_max_order=1, + ev_op_iterations=1, + interpolation_polynomial_degree=interpolation_polynomial_degree, + interpolation_is_log=interpolation_is_log, + backward_inversion="exact", + ), Q2grid=Q2grid, ) return d diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 1d21b6fe1..53ddaaf3d 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -41,12 +41,8 @@ def test_io(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal( - o1.interpolation_xgrid, fake_output["interpolation_xgrid"] - ) - np.testing.assert_almost_equal( - o2.interpolation_xgrid, fake_output["interpolation_xgrid"] - ) + np.testing.assert_almost_equal(o1.xgrid, fake_output["interpolation_xgrid"]) + np.testing.assert_almost_equal(o2.xgrid, fake_output["interpolation_xgrid"]) # fake output files m_out = mock.mock_open(read_data="") with mock.patch("builtins.open", m_out) as mock_file: @@ -60,18 +56,14 @@ def test_io(self, fake_output): fn = "test.yaml" o3 = legacy.load_yaml_from_file(fn) mock_file.assert_called_with(fn, encoding="utf-8") - np.testing.assert_almost_equal( - o3.interpolation_xgrid, fake_output["interpolation_xgrid"] - ) + np.testing.assert_almost_equal(o3.xgrid, fake_output["interpolation_xgrid"]) # repeat for tar fn = "test.tar" with tempfile.TemporaryDirectory() as folder: fp = pathlib.Path(folder) / fn legacy.dump_tar(o1, fp) o4 = legacy.load_tar(fp) - np.testing.assert_almost_equal( - o4.interpolation_xgrid, fake_output["interpolation_xgrid"] - ) + np.testing.assert_almost_equal(o4.xgrid, fake_output["interpolation_xgrid"]) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): legacy.dump_tar(o1, fn) @@ -100,16 +92,12 @@ def test_io_bin(self, fake_output): o1 = output.EKO.from_dict(fake_output) # test streams stream = io.StringIO() - o1.dump_yaml(stream, False) + legacy.dump_yaml(o1, stream, False) # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal( - o1.interpolation_xgrid, fake_output.interpolation_xgrid - ) - np.testing.assert_almost_equal( - o2.interpolation_xgrid, fake_output.interpolation_xgrid - ) + np.testing.assert_almost_equal(o1.xgrid, fake_output.xgrid) + np.testing.assert_almost_equal(o2.xgrid, fake_output.xgrid) class TestManipulate: @@ -117,7 +105,7 @@ def test_xgrid_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) o1 = output.EKO.from_dict(fake_output) - o1.interpolation_xgrid = xg + o1.xgrid = xg o1.targetgrid = xg o1.inputgrid = xg o1.Q2grid = { @@ -184,7 +172,7 @@ def test_flavor_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) o1 = output.EKO.from_dict(fake_output) - o1.interpolation_xgrid = xg + o1.xgrid = xg o1.targetgrid = xg o1.inputgrid = xg o1.Q2grid = { @@ -252,7 +240,7 @@ def test_to_evol(self, fake_factory): [q2_out], len(br.flavor_basis_pids), len(interpolation_xgrid) ) d = dict( - interpolation_xgrid=interpolation_xgrid, + xgrid=interpolation_xgrid, targetgrid=interpolation_xgrid, inputgrid=interpolation_xgrid, interpolation_polynomial_degree=interpolation_polynomial_degree, From b3943a1197a9937430bb6573e24f44043d141759 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 16 Mar 2022 17:10:53 +0100 Subject: [PATCH 005/148] Recover one legacy test --- src/eko/output/legacy.py | 66 ++++++++++++++++++---------------------- src/eko/output/struct.py | 19 +++++++++--- tests/eko/test_output.py | 13 ++++---- 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 86a5030be..34d4b173b 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -12,7 +12,7 @@ from . import struct -def get_raw(obj, binarize=True, skip_q2_grid=False): +def get_raw(eko, binarize=True, skip_q2_grid=False): """ Serialize result as dict/YAML. @@ -20,43 +20,36 @@ def get_raw(obj, binarize=True, skip_q2_grid=False): Parameters ---------- - binarize : bool - dump in binary format (instead of list format) + binarize : bool + dump in binary format (instead of list format) Returns ------- - out : dict - dictionary which will be written on output + out : dict + dictionary which will be written on output + """ + obj = eko.raw + # prepare output dict out = {"Q2grid": {}, "eko_version": version.__version__} # dump raw elements - for f in [ - "interpolation_polynomial_degree", - "interpolation_is_log", - "q2_ref", - ]: + for f in ["Q0", "xgrid", "configs", "rotations"]: out[f] = obj[f] - # list() work both for tuple and list - out["inputpids"] = list(obj["inputpids"]) - out["targetpids"] = list(obj["targetpids"]) - # make raw lists - # TODO: is interpolation_xgrid really needed in the output? - for k in ["interpolation_xgrid", "targetgrid", "inputgrid"]: - out[k] = obj[k].tolist() # make operators raw if not skip_q2_grid: - for q2, op in obj["Q2grid"].items(): + for q2, op in eko.items(): out["Q2grid"][q2] = {} - for k, v in op.items(): - if k == "alphas": - out["Q2grid"][q2][k] = float(v) - continue - if binarize: - out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) - else: - out["Q2grid"][q2][k] = v.tolist() + if op is not None: + for k, v in op.items(): + if k == "alphas": + out["Q2grid"][q2][k] = float(v) + continue + if binarize: + out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) + else: + out["Q2grid"][q2][k] = v.tolist() else: out["Q2grid"] = obj["Q2grid"] return out @@ -107,7 +100,7 @@ def dump_yaml_to_file(obj, filename, binarize=True, skip_q2_grid=False): result of dump(output, stream), i.e. Null if written successfully """ with open(filename, "w", encoding="utf-8") as f: - ret = obj.dump_yaml(f, binarize, skip_q2_grid=skip_q2_grid) + ret = dump_yaml(obj, f, binarize, skip_q2_grid=skip_q2_grid) return ret @@ -130,12 +123,11 @@ def dump_tar(obj, tarname): with tempfile.TemporaryDirectory() as tmpdir: tmpdir = pathlib.Path(tmpdir) - cls = obj.__class__ - metadata = cls(**{str(k): v for k, v in obj.items() if k != "Q2grid"}) - metadata["Q2grid"] = list(obj["Q2grid"].keys()) + metadata = {str(k): v for k, v in obj.items() if k != "Q2grid"} + metadata["Q2grid"] = obj.Q2grid.tolist() yamlname = tmpdir / "metadata.yaml" - metadata.dump_yaml_to_file(yamlname, skip_q2_grid=True) + dump_yaml_to_file(metadata, yamlname, skip_q2_grid=True) for kind in next(iter(obj["Q2grid"].values())).keys(): operator = np.stack([q2[kind] for q2 in obj["Q2grid"].values()]) @@ -167,13 +159,13 @@ def load_yaml(stream, skip_q2_grid=False): loaded object """ obj = yaml.safe_load(stream) - len_tpids = len(obj["targetpids"]) - len_ipids = len(obj["inputpids"]) - len_tgrid = len(obj["targetgrid"]) - len_igrid = len(obj["inputgrid"]) + len_tpids = len(obj["rotations"]["targetpids"]) + len_ipids = len(obj["rotations"]["inputpids"]) + len_tgrid = len(obj["rotations"]["targetgrid"]) + len_igrid = len(obj["rotations"]["inputgrid"]) # cast lists to numpy - for k in ["interpolation_xgrid", "inputgrid", "targetgrid"]: - obj[k] = np.array(obj[k]) + for k, v in obj["rotations"].items(): + obj["rotations"][k] = np.array(v) # make operators numpy if not skip_q2_grid: for op in obj["Q2grid"].values(): diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 66cd8b59e..3bf6b45e8 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from dataclasses import dataclass, fields +from dataclasses import asdict, dataclass, fields from typing import Dict, Literal, Optional import numpy as np @@ -22,6 +22,8 @@ def raw(self): value if not isinstance(value, np.ndarray) else value.tolist() ) + return dictionary + @dataclass class Operator(DictLike): @@ -67,14 +69,23 @@ class EKO: rotations: Rotations debug: Debug + def __iter__(self): + return iter(self._operators) + + def items(self): + return ( + (k, asdict(v) if v is not None else None) + for k, v in self._operators.items() + ) + @property def Q2grid(self): - return np.array(self._operators.keys()) + return np.array(list(self._operators)) @classmethod def from_dict(cls, dictionary): return cls( - xgrid=dictionary["xgrid"], + xgrid=np.array(dictionary["xgrid"]), Q02=dictionary["Q0"] ** 2, _operators={q2: None for q2 in dictionary["Q2grid"]}, configs=Configs.from_dict(dictionary["configs"]), @@ -86,7 +97,7 @@ def from_dict(cls, dictionary): def raw(self): return dict( xgrid=self.xgrid.tolist(), - Q0=np.sqrt(self.Q02), + Q0=float(np.sqrt(self.Q02)), Q2grid=self.Q2grid, configs=self.configs.raw, rotations=self.rotations.raw, diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 53ddaaf3d..421940336 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -41,8 +41,8 @@ def test_io(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid, fake_output["interpolation_xgrid"]) - np.testing.assert_almost_equal(o2.xgrid, fake_output["interpolation_xgrid"]) + np.testing.assert_almost_equal(o1.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o2.xgrid, fake_output["xgrid"]) # fake output files m_out = mock.mock_open(read_data="") with mock.patch("builtins.open", m_out) as mock_file: @@ -56,14 +56,14 @@ def test_io(self, fake_output): fn = "test.yaml" o3 = legacy.load_yaml_from_file(fn) mock_file.assert_called_with(fn, encoding="utf-8") - np.testing.assert_almost_equal(o3.xgrid, fake_output["interpolation_xgrid"]) + np.testing.assert_almost_equal(o3.xgrid, fake_output["xgrid"]) # repeat for tar fn = "test.tar" with tempfile.TemporaryDirectory() as folder: fp = pathlib.Path(folder) / fn legacy.dump_tar(o1, fp) o4 = legacy.load_tar(fp) - np.testing.assert_almost_equal(o4.xgrid, fake_output["interpolation_xgrid"]) + np.testing.assert_almost_equal(o4.xgrid, fake_output["xgrid"]) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): legacy.dump_tar(o1, fn) @@ -78,6 +78,7 @@ def test_rename_issue81(self, fake_output): p = pathlib.Path(folder) fp1 = p / "test1.tar" fp2 = p / "test2.tar" + __import__("pdb").set_trace() legacy.dump_tar(o1, fp1) # rename shutil.move(fp1, fp2) @@ -96,8 +97,8 @@ def test_io_bin(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid, fake_output.xgrid) - np.testing.assert_almost_equal(o2.xgrid, fake_output.xgrid) + np.testing.assert_almost_equal(o1.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o2.xgrid, fake_output["xgrid"]) class TestManipulate: From 211be29c5b0996584987d38cd925402e3ff0a60f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 16 Mar 2022 18:14:00 +0100 Subject: [PATCH 006/148] Recover all legacy tests --- src/eko/output/legacy.py | 23 ++++++++++++++++------- tests/eko/test_output.py | 5 +---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 34d4b173b..aea7f244e 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -123,14 +123,22 @@ def dump_tar(obj, tarname): with tempfile.TemporaryDirectory() as tmpdir: tmpdir = pathlib.Path(tmpdir) - metadata = {str(k): v for k, v in obj.items() if k != "Q2grid"} + metadata = {str(k): v for k, v in obj.raw.items() if k != "Q2grid"} metadata["Q2grid"] = obj.Q2grid.tolist() yamlname = tmpdir / "metadata.yaml" - dump_yaml_to_file(metadata, yamlname, skip_q2_grid=True) - - for kind in next(iter(obj["Q2grid"].values())).keys(): - operator = np.stack([q2[kind] for q2 in obj["Q2grid"].values()]) + with open(yamlname, "w", encoding="utf-8") as fd: + yaml.dump(metadata, fd) + + for kind in ["alphas", "operators", "operator_errors"]: + elements = [] + for q2, op in obj.items(): + if op is not None: + el = getattr(q2, kind) + else: + el = np.zeros((len(obj.xgrid), 14, len(obj.xgrid), 14)) + elements.append(el) + operator = np.stack(elements) stream = io.BytesIO() np.save(stream, operator) stream.seek(0) @@ -232,7 +240,8 @@ def load_tar(tarname): innerdir = list(tmpdir.glob("*"))[0] yamlname = innerdir / "metadata.yaml" - metadata = load_yaml_from_file(yamlname, skip_q2_grid=True) + with open(yamlname, encoding="utf-8") as fd: + metadata = yaml.safe_load(fd) grids = {} for fp in innerdir.glob("*.npy.lz4"): @@ -249,4 +258,4 @@ def load_tar(tarname): operator_grid[q2] = dict(zip(grids.keys(), slices)) metadata["Q2grid"] = operator_grid - return metadata + return struct.EKO.from_dict(metadata) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 421940336..98002d693 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -78,15 +78,12 @@ def test_rename_issue81(self, fake_output): p = pathlib.Path(folder) fp1 = p / "test1.tar" fp2 = p / "test2.tar" - __import__("pdb").set_trace() legacy.dump_tar(o1, fp1) # rename shutil.move(fp1, fp2) # reload o4 = legacy.load_tar(fp2) - np.testing.assert_almost_equal( - o4["interpolation_xgrid"], fake_output["interpolation_xgrid"] - ) + np.testing.assert_almost_equal(o4.xgrid, fake_output["xgrid"]) def test_io_bin(self, fake_output): # create object From 04a74b640cbb9b2ca032e3864fa3317064d0362c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 13:30:32 +0100 Subject: [PATCH 007/148] Recover to manipulation tests --- src/eko/output/__init__.py | 2 +- src/eko/output/manipulate.py | 51 ++++++++++++++++++++---------------- src/eko/output/struct.py | 15 +++++++++-- src/eko/version.py | 1 + tests/eko/test_output.py | 50 +++++++++++++++++------------------ 5 files changed, 68 insertions(+), 51 deletions(-) diff --git a/src/eko/output/__init__.py b/src/eko/output/__init__.py index 312c35bb0..d8bb6ebaf 100644 --- a/src/eko/output/__init__.py +++ b/src/eko/output/__init__.py @@ -4,4 +4,4 @@ """ from . import manipulate -from .struct import EKO +from .struct import EKO, Operator diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 386815fd7..c5a972437 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -29,15 +29,15 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): # now check to the current status if ( targetgrid is not None - and len(targetgrid) == len(eko.targetgrid) - and np.allclose(targetgrid, eko.targetgrid) + and len(targetgrid) == len(eko.rotations.targetgrid) + and np.allclose(targetgrid, eko.rotations.targetgrid) ): targetgrid = None warnings.warn("The new targetgrid is close to the current targetgrid") if ( inputgrid is not None - and len(inputgrid) == len(eko.inputgrid) - and np.allclose(inputgrid, eko.inputgrid) + and len(inputgrid) == len(eko.rotations.inputgrid) + and np.allclose(inputgrid, eko.rotations.inputgrid) ): inputgrid = None warnings.warn("The new inputgrid is close to the current inputgrid") @@ -49,27 +49,29 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): # construct matrices if targetgrid is not None: b = interpolation.InterpolatorDispatcher( - eko.targetgrid, - eko.interpolation_polynomial_degree, - eko.interpolation_is_log, + eko.rotations.targetgrid, + eko.configs.interpolation_polynomial_degree, + eko.configs.interpolation_is_log, False, ) target_rot = b.get_interpolation(targetgrid) - eko.targetgrid = np.array(targetgrid) + eko.rotations.targetgrid = np.array(targetgrid) if inputgrid is not None: b = interpolation.InterpolatorDispatcher( inputgrid, - eko.interpolation_polynomial_degree, - eko.interpolation_is_log, + eko.configs.interpolation_polynomial_degree, + eko.configs.interpolation_is_log, False, ) - input_rot = b.get_interpolation(eko.inputgrid) - eko.inputgrid = np.array(inputgrid) + input_rot = b.get_interpolation(eko.rotations.inputgrid) + eko.rotations.inputgrid = np.array(inputgrid) # build new grid - for elem in eko.Q2grid.values(): - ops = elem.operators - errs = elem.operator_errors + for _, elem in eko._operators.items(): + if elem is None: + continue + ops = elem.operator + errs = elem.error if targetgrid is not None and inputgrid is None: ops = np.einsum("ij,ajbk->aibk", target_rot, ops) errs = np.einsum("ij,ajbk->aibk", target_rot, errs) @@ -79,8 +81,8 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): else: ops = np.einsum("ij,ajbk,kl->aibl", target_rot, ops, input_rot) errs = np.einsum("ij,ajbk,kl->aibl", target_rot, errs, input_rot) - elem.operators = ops - elem.operator_errors = errs + elem.operator = ops + elem.error = errs def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): @@ -101,11 +103,13 @@ def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): raise ValueError("Nor inputbasis nor targetbasis was given") # now check to the current status if targetbasis is not None and np.allclose( - targetbasis, np.eye(len(eko.targetpids)) + targetbasis, np.eye(len(eko.rotations.targetpids)) ): targetbasis = None warnings.warn("The new targetbasis is close to current basis") - if inputbasis is not None and np.allclose(inputbasis, np.eye(len(eko.inputpids))): + if inputbasis is not None and np.allclose( + inputbasis, np.eye(len(eko.rotations.inputpids)) + ): inputbasis = None warnings.warn("The new inputbasis is close to current basis") # after the checks: if there is still nothing to do, skip @@ -118,7 +122,9 @@ def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): inv_inputbasis = np.linalg.inv(inputbasis) # build new grid - for elem in eko.Q2grid.values(): + for _, elem in eko.items(): + if elem is None: + continue ops = elem.operators errs = elem.operator_errors if targetbasis is not None and inputbasis is None: @@ -133,10 +139,11 @@ def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): elem.operators = ops elem.operator_errors = errs # drop PIDs - keeping them int nevertheless + # there is no meaningful way to set them in general, after rotation if inputbasis is not None: - eko.inputpids = [0] * len(eko.inputpids) + eko.rotations.inputpids = [0] * len(eko.rotations.inputpids) if targetbasis is not None: - eko.targetpids = [0] * len(eko.targetpids) + eko.rotations.targetpids = [0] * len(eko.rotations.targetpids) def to_evol(eko, source=True, target=False, inplace=True): diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 3bf6b45e8..af9f95c40 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -4,6 +4,8 @@ import numpy as np +from .. import version as vmod + class DictLike: def __init__(self, **kwargs): @@ -28,8 +30,8 @@ def raw(self): @dataclass class Operator(DictLike): alphas: float - operators: np.ndarray - operator_errors: Optional[np.ndarray] = None + operator: np.ndarray + error: Optional[np.ndarray] = None @dataclass @@ -68,10 +70,19 @@ class EKO: configs: Configs rotations: Rotations debug: Debug + version: str = vmod.__version__ + data_version: str = vmod.__data_version__ def __iter__(self): return iter(self._operators) + def __contains__(self, q2): + return q2 in self._operators + + def __getitem__(self, q2): + # TODO: autload + return self._operators[q2] + def items(self): return ( (k, asdict(v) if v is not None else None) diff --git a/src/eko/version.py b/src/eko/version.py index f09cc807b..0aff48f78 100644 --- a/src/eko/version.py +++ b/src/eko/version.py @@ -1,2 +1,3 @@ # -*- coding: utf-8 -*- __version__ = "0.0.0" +__data_version__ = "0.0.1" diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 98002d693..ccf694b3f 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -24,11 +24,9 @@ def eko_identity(shape): def chk_keys(a, b): """Check all keys are preserved""" assert sorted(a.keys()) == sorted(b.keys()) - for q2, op in a.Q2grid.items(): - assert q2 in b.Q2grid - opb = b.Q2grid[q2] - assert sorted(op.keys()) == sorted(opb.keys()) - assert op["alphas"] == opb["alphas"] + for key, value in a.items(): + if isinstance(value, dict): + assert sorted(value.keys()) == sorted(b[key].keys()) class TestLegacy: @@ -104,48 +102,50 @@ def test_xgrid_reshape(self, fake_output): xg = np.geomspace(1e-5, 1.0, 21) o1 = output.EKO.from_dict(fake_output) o1.xgrid = xg - o1.targetgrid = xg - o1.inputgrid = xg - o1.Q2grid = { - 10: dict( - operators=eko_identity([1, 2, len(xg), 2, len(xg)])[0], - operator_errors=np.zeros((2, len(xg), 2, len(xg))), - alphas=np.random.rand(), + o1.rotations.targetgrid = xg + o1.rotations.inputgrid = xg + o1._operators = { + 10: output.Operator.from_dict( + dict( + operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], + error=np.zeros((2, len(xg), 2, len(xg))), + alphas=np.random.rand(), + ) ) } xgp = np.geomspace(1e-5, 1.0, 11) # only target ot = copy.deepcopy(o1) manipulate.xgrid_reshape(ot, xgp, inplace=True) - chk_keys(o1, ot) - assert ot["Q2grid"][10]["operators"].shape == (2, len(xgp), 2, len(xg)) + chk_keys(o1.raw, ot.raw) + assert ot._operators[10].operator.shape == (2, len(xgp), 2, len(xg)) ott = copy.deepcopy(o1) with pytest.warns(Warning): manipulate.xgrid_reshape(ott, xg) - chk_keys(o1, ott) + chk_keys(o1.raw, ott.raw) np.testing.assert_allclose( - ott.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] + ott._operators[10].operator, o1._operators[10].operator ) # only input oi = copy.deepcopy(o1) - manipulate.xgrid_reshape(o1, inputgrid=xgp) - assert oi.Q2grid[10]["operators"].shape == (2, len(xg), 2, len(xgp)) - chk_keys(o1, oi) + manipulate.xgrid_reshape(oi, inputgrid=xgp) + assert oi._operators[10].operator.shape == (2, len(xg), 2, len(xgp)) + chk_keys(o1.raw, oi.raw) oii = copy.deepcopy(o1) with pytest.warns(Warning): manipulate.xgrid_reshape(oii, inputgrid=xg) - chk_keys(o1, oii) + chk_keys(o1.raw, oii.raw) np.testing.assert_allclose( - oii.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] + oii._operators[10].operator, o1._operators[10].operator ) # both oit = copy.deepcopy(o1) manipulate.xgrid_reshape(oit, xgp, xgp) - chk_keys(o1, oit) + chk_keys(o1.raw, oit.raw) op = eko_identity([1, 2, len(xgp), 2, len(xgp)]) - np.testing.assert_allclose(oit.Q2grid[10]["operators"], op[0], atol=1e-10) + np.testing.assert_allclose(oit._operators[10].operator, op[0], atol=1e-10) # error with pytest.raises(ValueError): manipulate.xgrid_reshape(copy.deepcopy(o1)) @@ -162,9 +162,7 @@ def test_reshape_io(self, fake_output): # reload stream.seek(0) o3 = legacy.load_yaml(stream) - # eko_version is only added in get_raw - del o3["eko_version"] - chk_keys(o1, o3) + chk_keys(o1.raw, o3.raw) def test_flavor_reshape(self, fake_output): # create object From 08af72a86b21f2e8bbcae828da5cdbaef91d9863 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 13:54:05 +0100 Subject: [PATCH 008/148] Recover all manipulation tests --- src/eko/output/manipulate.py | 12 ++-- src/eko/output/struct.py | 8 ++- tests/conftest.py | 4 +- tests/eko/test_output.py | 120 ++++++++++++++++++----------------- 4 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index c5a972437..8ddf4d550 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -122,11 +122,11 @@ def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): inv_inputbasis = np.linalg.inv(inputbasis) # build new grid - for _, elem in eko.items(): + for _, elem in eko._operators.items(): if elem is None: continue - ops = elem.operators - errs = elem.operator_errors + ops = elem.operator + errs = elem.error if targetbasis is not None and inputbasis is None: ops = np.einsum("ca,ajbk->cjbk", targetbasis, ops) errs = np.einsum("ca,ajbk->cjbk", targetbasis, errs) @@ -162,9 +162,9 @@ def to_evol(eko, source=True, target=False, inplace=True): # rotate inputbasis = br.rotate_flavor_to_evolution if source else None targetbasis = br.rotate_flavor_to_evolution if target else None - eko.flavor_reshape(inputbasis=inputbasis, targetbasis=targetbasis) + flavor_reshape(eko, inputbasis=inputbasis, targetbasis=targetbasis) # assign pids if source: - eko.inputpids = br.evol_basis_pids + eko.rotations.inputpids = br.evol_basis_pids if target: - eko.targetpids = br.evol_basis_pids + eko.rotations.targetpids = br.evol_basis_pids diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index af9f95c40..ecb2bfc84 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -80,9 +80,15 @@ def __contains__(self, q2): return q2 in self._operators def __getitem__(self, q2): - # TODO: autload + # TODO: autoload return self._operators[q2] + def __setitem__(self, q2, op): + # TODO: autodump + if isinstance(op, dict): + op = Operator.from_dict(op) + self._operators[q2] = op + def items(self): return ( (k, asdict(v) if v is not None else None) diff --git a/tests/conftest.py b/tests/conftest.py index 3ca998106..03f38fa79 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,8 +27,8 @@ def mk_g(self, q2s, lpids, lx): Q2grid = {} for q2 in q2s: Q2grid[q2] = { - "operators": np.random.rand(lpids, lx, lpids, lx), - "operator_errors": np.random.rand(lpids, lx, lpids, lx), + "operator": np.random.rand(lpids, lx, lpids, lx), + "error": np.random.rand(lpids, lx, lpids, lx), "alphas": np.random.rand(), } return Q2grid diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index ccf694b3f..138fd9248 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -33,6 +33,8 @@ class TestLegacy: def test_io(self, fake_output): # create object o1 = output.EKO.from_dict(fake_output) + for q2, op in fake_output["Q2grid"].items(): + o1[q2] = output.Operator.from_dict(op) # test streams stream = io.StringIO() legacy.dump_yaml(o1, stream) @@ -70,6 +72,8 @@ def test_rename_issue81(self, fake_output): # https://github.com/N3PDF/eko/issues/81 # create object o1 = output.EKO.from_dict(fake_output) + for q2, op in fake_output["Q2grid"].items(): + o1[q2] = output.Operator.from_dict(op) with tempfile.TemporaryDirectory() as folder: # dump @@ -86,6 +90,8 @@ def test_rename_issue81(self, fake_output): def test_io_bin(self, fake_output): # create object o1 = output.EKO.from_dict(fake_output) + for q2, op in fake_output["Q2grid"].items(): + o1[q2] = output.Operator.from_dict(op) # test streams stream = io.StringIO() legacy.dump_yaml(o1, stream, False) @@ -118,34 +124,30 @@ def test_xgrid_reshape(self, fake_output): ot = copy.deepcopy(o1) manipulate.xgrid_reshape(ot, xgp, inplace=True) chk_keys(o1.raw, ot.raw) - assert ot._operators[10].operator.shape == (2, len(xgp), 2, len(xg)) + assert ot[10].operator.shape == (2, len(xgp), 2, len(xg)) ott = copy.deepcopy(o1) with pytest.warns(Warning): manipulate.xgrid_reshape(ott, xg) chk_keys(o1.raw, ott.raw) - np.testing.assert_allclose( - ott._operators[10].operator, o1._operators[10].operator - ) + np.testing.assert_allclose(ott[10].operator, o1[10].operator) # only input oi = copy.deepcopy(o1) manipulate.xgrid_reshape(oi, inputgrid=xgp) - assert oi._operators[10].operator.shape == (2, len(xg), 2, len(xgp)) + assert oi[10].operator.shape == (2, len(xg), 2, len(xgp)) chk_keys(o1.raw, oi.raw) oii = copy.deepcopy(o1) with pytest.warns(Warning): manipulate.xgrid_reshape(oii, inputgrid=xg) chk_keys(o1.raw, oii.raw) - np.testing.assert_allclose( - oii._operators[10].operator, o1._operators[10].operator - ) + np.testing.assert_allclose(oii[10].operator, o1[10].operator) # both oit = copy.deepcopy(o1) manipulate.xgrid_reshape(oit, xgp, xgp) chk_keys(o1.raw, oit.raw) op = eko_identity([1, 2, len(xgp), 2, len(xgp)]) - np.testing.assert_allclose(oit._operators[10].operator, op[0], atol=1e-10) + np.testing.assert_allclose(oit[10].operator, op[0], atol=1e-10) # error with pytest.raises(ValueError): manipulate.xgrid_reshape(copy.deepcopy(o1)) @@ -153,6 +155,8 @@ def test_xgrid_reshape(self, fake_output): def test_reshape_io(self, fake_output): # create object o1 = output.EKO.from_dict(fake_output) + for q2, op in fake_output["Q2grid"].items(): + o1[q2] = output.Operator.from_dict(op) o2 = copy.deepcopy(o1) manipulate.xgrid_reshape(o2, [0.1, 1.0], [0.1, 1.0]) manipulate.flavor_reshape(o2, inputbasis=np.array([[1, -1], [1, 1]])) @@ -169,59 +173,53 @@ def test_flavor_reshape(self, fake_output): xg = np.geomspace(1e-5, 1.0, 21) o1 = output.EKO.from_dict(fake_output) o1.xgrid = xg - o1.targetgrid = xg - o1.inputgrid = xg - o1.Q2grid = { - 10: dict( - operators=eko_identity([1, 2, len(xg), 2, len(xg)])[0], - operator_errors=np.zeros((2, len(xg), 2, len(xg))), - alphas=np.random.rand(), + o1.rotations.targetgrid = xg + o1.rotations.inputgrid = xg + o1._operators = { + 10: output.Operator.from_dict( + dict( + operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], + error=np.zeros((2, len(xg), 2, len(xg))), + alphas=np.random.rand(), + ) ) } # only target target_r = np.array([[1, -1], [1, 1]]) ot = copy.deepcopy(o1) manipulate.flavor_reshape(ot, target_r) - chk_keys(o1, ot) - assert ot.Q2grid[10]["operators"].shape == (2, len(xg), 2, len(xg)) + chk_keys(o1.raw, ot.raw) + assert ot[10].operator.shape == (2, len(xg), 2, len(xg)) ott = copy.deepcopy(ot) manipulate.flavor_reshape(ott, np.linalg.inv(target_r)) - np.testing.assert_allclose( - ott.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] - ) + np.testing.assert_allclose(ott[10].operator, o1[10].operator) with pytest.warns(Warning): manipulate.flavor_reshape(ott, np.eye(2)) - chk_keys(o1, ott) - np.testing.assert_allclose( - ott.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] - ) + chk_keys(o1.raw, ott.raw) + np.testing.assert_allclose(ott[10].operator, o1[10].operator) # only input input_r = np.array([[1, -1], [1, 1]]) oi = copy.deepcopy(o1) manipulate.flavor_reshape(oi, inputbasis=input_r) - chk_keys(o1, oi) - assert oi.Q2grid[10]["operators"].shape == (2, len(xg), 2, len(xg)) + chk_keys(o1.raw, oi.raw) + assert oi[10].operator.shape == (2, len(xg), 2, len(xg)) oii = copy.deepcopy(oi) manipulate.flavor_reshape(oii, inputbasis=np.linalg.inv(input_r)) - np.testing.assert_allclose( - oii.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] - ) + np.testing.assert_allclose(oii[10].operator, o1[10].operator) with pytest.warns(Warning): manipulate.flavor_reshape(oii, inputbasis=np.eye(2)) - chk_keys(o1, oii) - np.testing.assert_allclose( - oii.Q2grid[10]["operators"], o1.Q2grid[10]["operators"] - ) + chk_keys(o1.raw, oii.raw) + np.testing.assert_allclose(oii[10].operator, o1[10].operator) # both oit = copy.deepcopy(o1) manipulate.flavor_reshape( oit, np.array([[1, -1], [1, 1]]), np.array([[1, -1], [1, 1]]) ) - chk_keys(o1, oit) + chk_keys(o1.raw, oit.raw) op = eko_identity([1, 2, len(xg), 2, len(xg)]).copy() - np.testing.assert_allclose(oit.Q2grid[10]["operators"], op[0], atol=1e-10) + np.testing.assert_allclose(oit[10].operator, op[0], atol=1e-10) # error with pytest.raises(ValueError): manipulate.flavor_reshape(copy.deepcopy(o1)) @@ -237,39 +235,43 @@ def test_to_evol(self, fake_factory): ) d = dict( xgrid=interpolation_xgrid, - targetgrid=interpolation_xgrid, - inputgrid=interpolation_xgrid, - interpolation_polynomial_degree=interpolation_polynomial_degree, - interpolation_is_log=interpolation_is_log, - q2_ref=q2_ref, - inputpids=br.flavor_basis_pids, - targetpids=br.flavor_basis_pids, + Q0=np.sqrt(q2_ref), Q2grid=Q2grid, + rotations=dict( + targetgrid=interpolation_xgrid, + inputgrid=interpolation_xgrid, + inputpids=br.flavor_basis_pids, + targetpids=br.flavor_basis_pids, + ), + configs=dict( + interpolation_polynomial_degree=interpolation_polynomial_degree, + interpolation_is_log=interpolation_is_log, + ev_op_max_order=1, + ev_op_iterations=1, + backward_inversion="exact", + ), ) o00 = output.EKO.from_dict(d) - o01 = copy.copy(o00) + o00[q2_out] = Q2grid[q2_out] + o01 = copy.deepcopy(o00) manipulate.to_evol(o01) - o10 = copy.copy(o00) + o10 = copy.deepcopy(o00) manipulate.to_evol(o10, False, True) - o11 = copy.copy(o00) + o11 = copy.deepcopy(o00) manipulate.to_evol(o11, True, True) - chk_keys(o00, o11) + chk_keys(o00.raw, o11.raw) # check the input rotated one - np.testing.assert_allclose(o01.inputpids, br.evol_basis_pids) - np.testing.assert_allclose(o01.targetpids, br.flavor_basis_pids) + np.testing.assert_allclose(o01.rotations.inputpids, br.evol_basis_pids) + np.testing.assert_allclose(o01.rotations.targetpids, br.flavor_basis_pids) # rotate also target manipulate.to_evol(o01, False, True) - np.testing.assert_allclose( - o01.Q2grid[q2_out]["operators"], o11.Q2grid[q2_out]["operators"] - ) - chk_keys(o00, o01) + np.testing.assert_allclose(o01[q2_out].operator, o11[q2_out].operator) + chk_keys(o00.raw, o01.raw) # check the target rotated one - np.testing.assert_allclose(o10.inputpids, br.flavor_basis_pids) - np.testing.assert_allclose(o10.targetpids, br.evol_basis_pids) + np.testing.assert_allclose(o10.rotations.inputpids, br.flavor_basis_pids) + np.testing.assert_allclose(o10.rotations.targetpids, br.evol_basis_pids) # rotate also input manipulate.to_evol(o10) - np.testing.assert_allclose( - o10.Q2grid[q2_out]["operators"], o11.Q2grid[q2_out]["operators"] - ) - chk_keys(o00, o10) + np.testing.assert_allclose(o10[q2_out].operator, o11[q2_out].operator) + chk_keys(o00.raw, o10.raw) From b4d0067b0955c4eb269cd9b325869239d42f3398 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 13:58:30 +0100 Subject: [PATCH 009/148] Recover all output tests --- src/eko/output/legacy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index aea7f244e..c2636b71a 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -130,11 +130,11 @@ def dump_tar(obj, tarname): with open(yamlname, "w", encoding="utf-8") as fd: yaml.dump(metadata, fd) - for kind in ["alphas", "operators", "operator_errors"]: + for kind in ["alphas", "operator", "error"]: elements = [] for q2, op in obj.items(): if op is not None: - el = getattr(q2, kind) + el = op[kind] else: el = np.zeros((len(obj.xgrid), 14, len(obj.xgrid), 14)) elements.append(el) From bc58d46fcdc507672b30153e33817b23892b8664 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 14:15:48 +0100 Subject: [PATCH 010/148] Simplify items iteration --- pyproject.toml | 2 ++ src/eko/output/legacy.py | 5 +++-- src/eko/output/manipulate.py | 4 ++-- src/eko/output/struct.py | 19 +++++++++++-------- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1197097cf..ebcbe3e55 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,8 @@ genpdf = "ekobox.genpdf.cli:cli" [tool.poe.tasks] test = "pytest tests" +coverage = "$BROWSER htmlcov/index.html" +test-cov = ["test", "coverage"] bench = "pytest benchmarks" bench-iso.cmd = "pytest benchmarks -m isolated" bench-iso.env.NUMBA_DISABLE_JIT.default = "0" diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index c2636b71a..44d03836c 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import dataclasses import io import pathlib import tarfile @@ -42,7 +43,7 @@ def get_raw(eko, binarize=True, skip_q2_grid=False): for q2, op in eko.items(): out["Q2grid"][q2] = {} if op is not None: - for k, v in op.items(): + for k, v in dataclasses.asdict(op).items(): if k == "alphas": out["Q2grid"][q2][k] = float(v) continue @@ -134,7 +135,7 @@ def dump_tar(obj, tarname): elements = [] for q2, op in obj.items(): if op is not None: - el = op[kind] + el = getattr(op, kind) else: el = np.zeros((len(obj.xgrid), 14, len(obj.xgrid), 14)) elements.append(el) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 8ddf4d550..f9e8b14cb 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -67,7 +67,7 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): eko.rotations.inputgrid = np.array(inputgrid) # build new grid - for _, elem in eko._operators.items(): + for _, elem in eko.items(): if elem is None: continue ops = elem.operator @@ -122,7 +122,7 @@ def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): inv_inputbasis = np.linalg.inv(inputbasis) # build new grid - for _, elem in eko._operators.items(): + for _, elem in eko.items(): if elem is None: continue ops = elem.operator diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index ecb2bfc84..61772572f 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from dataclasses import asdict, dataclass, fields +from dataclasses import dataclass, fields from typing import Dict, Literal, Optional import numpy as np @@ -20,9 +20,15 @@ def raw(self): dictionary = {} for field in fields(self): value = getattr(self, field.name) - dictionary[field.name] = ( - value if not isinstance(value, np.ndarray) else value.tolist() - ) + + # replace numpy arrays with lists + if isinstance(value, np.ndarray): + value = value.tolist() + # replace numpy scalars with python ones + elif isinstance(value, float): + value = float(value) + + dictionary[field.name] = value return dictionary @@ -90,10 +96,7 @@ def __setitem__(self, q2, op): self._operators[q2] = op def items(self): - return ( - (k, asdict(v) if v is not None else None) - for k, v in self._operators.items() - ) + return self._operators.items() @property def Q2grid(self): From 7667286a8158f3d983dc4d9a446ce087a098458d Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 15:47:09 +0100 Subject: [PATCH 011/148] Recover apply tests --- benchmarks/eko/benchmark_evol_to_unity.py | 2 +- src/eko/interpolation.py | 6 ++-- src/ekomark/apply.py | 35 +++++++++++------------ tests/ekomark/test_apply.py | 29 +++++++++++-------- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/benchmarks/eko/benchmark_evol_to_unity.py b/benchmarks/eko/benchmark_evol_to_unity.py index 24deb236c..4254327c8 100644 --- a/benchmarks/eko/benchmark_evol_to_unity.py +++ b/benchmarks/eko/benchmark_evol_to_unity.py @@ -43,7 +43,7 @@ class BenchmarkBackwardForward: operators_card = { "Q2grid": [10], # here you need a very dense grid - "interpolation_xgrid": np.linspace(1e-1, 1, 30), + "xgrid": np.linspace(1e-1, 1, 30), # "interpolation_xgrid": make_grid(30,30, x_min=1e-3), "interpolation_polynomial_degree": 1, "interpolation_is_log": True, diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index 118d8ffa0..8c5d0c513 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -511,7 +511,7 @@ def from_dict(cls, operators_card, mode_N=True): * - Name - Type - description - * - ``interpolation_xgrid`` + * - ``xgrid`` - :py:obj:`list(float)` - the interpolation grid * - ``interpolation_polynomial_degree`` @@ -527,7 +527,7 @@ def from_dict(cls, operators_card, mode_N=True): input configurations """ # load xgrid - xgrid = operators_card["interpolation_xgrid"] + xgrid = operators_card["xgrid"] if len(xgrid) == 0: raise ValueError("Empty xgrid!") if xgrid[0] == "make_grid": @@ -607,7 +607,7 @@ def to_dict(self): full grid configuration """ ret = { - "interpolation_xgrid": self.xgrid_raw, + "xgrid": self.xgrid_raw, "interpolation_polynomial_degree": self.polynomial_degree, "interpolation_is_log": self.log, } diff --git a/src/ekomark/apply.py b/src/ekomark/apply.py index fc216ba62..f91db4659 100644 --- a/src/ekomark/apply.py +++ b/src/ekomark/apply.py @@ -5,13 +5,13 @@ from eko import interpolation -def apply_pdf(output, lhapdf_like, targetgrid=None, rotate_to_evolution_basis=False): +def apply_pdf(eko, lhapdf_like, targetgrid=None, rotate_to_evolution_basis=False): """ Apply all available operators to the input PDFs. Parameters ---------- - output : eko.output.Output + output : eko.output.EKO eko output object containing all informations lhapdf_like : object object that provides an xfxQ2 callable (as `lhapdf `_ @@ -28,18 +28,18 @@ def apply_pdf(output, lhapdf_like, targetgrid=None, rotate_to_evolution_basis=Fa """ if rotate_to_evolution_basis: return apply_pdf_flavor( - output, lhapdf_like, targetgrid, br.rotate_flavor_to_evolution + eko, lhapdf_like, targetgrid, br.rotate_flavor_to_evolution ) - return apply_pdf_flavor(output, lhapdf_like, targetgrid) + return apply_pdf_flavor(eko, lhapdf_like, targetgrid) -def apply_pdf_flavor(output, lhapdf_like, targetgrid=None, flavor_rotation=None): +def apply_pdf_flavor(eko, lhapdf_like, targetgrid=None, flavor_rotation=None): """ Apply all available operators to the input PDFs. Parameters ---------- - output : eko.output.Output + output : eko.output.EKO eko output object containing all informations lhapdf_like : object object that provides an xfxQ2 callable (as `lhapdf `_ @@ -55,25 +55,22 @@ def apply_pdf_flavor(output, lhapdf_like, targetgrid=None, flavor_rotation=None) output PDFs and their associated errors for the computed Q2grid """ # create pdfs - pdfs = np.zeros((len(output["inputpids"]), len(output["inputgrid"]))) - for j, pid in enumerate(output["inputpids"]): + pdfs = np.zeros((len(eko.rotations.inputpids), len(eko.rotations.inputgrid))) + for j, pid in enumerate(eko.rotations.inputpids): if not lhapdf_like.hasFlavor(pid): continue pdfs[j] = np.array( - [ - lhapdf_like.xfxQ2(pid, x, output["q2_ref"]) / x - for x in output["inputgrid"] - ] + [lhapdf_like.xfxQ2(pid, x, eko.Q02) / x for x in eko.rotations.inputgrid] ) # build output out_grid = {} - for q2, elem in output["Q2grid"].items(): - pdf_final = np.einsum("ajbk,bk", elem["operators"], pdfs) - error_final = np.einsum("ajbk,bk", elem["operator_errors"], pdfs) + for q2, elem in eko.items(): + pdf_final = np.einsum("ajbk,bk", elem.operator, pdfs) + error_final = np.einsum("ajbk,bk", elem.error, pdfs) out_grid[q2] = { - "pdfs": dict(zip(output["targetpids"], pdf_final)), - "errors": dict(zip(output["targetpids"], error_final)), + "pdfs": dict(zip(eko.rotations.targetpids, pdf_final)), + "errors": dict(zip(eko.rotations.targetpids, error_final)), } # rotate to evolution basis @@ -90,7 +87,9 @@ def apply_pdf_flavor(output, lhapdf_like, targetgrid=None, flavor_rotation=None) # rotate/interpolate to target grid if targetgrid is not None: - b = interpolation.InterpolatorDispatcher.from_dict(output, False) + b = interpolation.InterpolatorDispatcher.from_dict( + eko.configs.raw | dict(xgrid=eko.xgrid), False + ) rot = b.get_interpolation(targetgrid) for evpdf in out_grid.values(): for pdf_label in evpdf["pdfs"]: diff --git a/tests/ekomark/test_apply.py b/tests/ekomark/test_apply.py index 8869d8201..47b342bdf 100644 --- a/tests/ekomark/test_apply.py +++ b/tests/ekomark/test_apply.py @@ -9,18 +9,20 @@ class TestApply: def test_apply(self, fake_output, fake_pdf): q2_out = list(fake_output["Q2grid"].keys())[0] # create object - o = output.Output(fake_output) + o = output.EKO.from_dict(fake_output) + for q2, op in fake_output["Q2grid"].items(): + o[q2] = output.Operator.from_dict(op) # fake pdfs pdf_grid = apply.apply_pdf(o, fake_pdf) assert len(pdf_grid) == len(fake_output["Q2grid"]) pdfs = pdf_grid[q2_out]["pdfs"] - assert list(pdfs.keys()) == fake_output["targetpids"] - ref_pid1 = fake_output["Q2grid"][q2_out]["operators"][0, :, 1, :] @ np.ones( - len(fake_output["interpolation_xgrid"]) + assert list(pdfs.keys()) == fake_output["rotations"]["targetpids"] + ref_pid1 = fake_output["Q2grid"][q2_out]["operator"][0, :, 1, :] @ np.ones( + len(fake_output["xgrid"]) ) np.testing.assert_allclose(pdfs[0], ref_pid1) - ref_pid2 = fake_output["Q2grid"][q2_out]["operators"][1, :, 1, :] @ np.ones( - len(fake_output["interpolation_xgrid"]) + ref_pid2 = fake_output["Q2grid"][q2_out]["operator"][1, :, 1, :] @ np.ones( + len(fake_output["xgrid"]) ) np.testing.assert_allclose(pdfs[1], ref_pid2) # rotate to target_grid @@ -28,18 +30,21 @@ def test_apply(self, fake_output, fake_pdf): pdf_grid = apply.apply_pdf(o, fake_pdf, target_grid) assert len(pdf_grid) == 1 pdfs = pdf_grid[q2_out]["pdfs"] - assert list(pdfs.keys()) == fake_output["targetpids"] + assert list(pdfs.keys()) == fake_output["rotations"]["targetpids"] def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): q2_out = list(fake_output["Q2grid"].keys())[0] # create object - o = output.Output(fake_output) + o = output.EKO.from_dict(fake_output) + for q2, op in fake_output["Q2grid"].items(): + o[q2] = output.Operator.from_dict(op) # fake pdfs monkeypatch.setattr( "eko.basis_rotation.rotate_flavor_to_evolution", np.ones((2, 2)) ) monkeypatch.setattr( - "eko.basis_rotation.flavor_basis_pids", fake_output["targetpids"] + "eko.basis_rotation.flavor_basis_pids", + fake_output["rotations"]["targetpids"], ) fake_evol_basis = ("a", "b") monkeypatch.setattr("eko.basis_rotation.evol_basis", fake_evol_basis) @@ -48,7 +53,7 @@ def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): pdfs = pdf_grid[q2_out]["pdfs"] assert list(pdfs.keys()) == list(fake_evol_basis) ref_a = ( - fake_output["Q2grid"][q2_out]["operators"][0, :, 1, :] - + fake_output["Q2grid"][q2_out]["operators"][1, :, 1, :] - ) @ np.ones(len(fake_output["interpolation_xgrid"])) + fake_output["Q2grid"][q2_out]["operator"][0, :, 1, :] + + fake_output["Q2grid"][q2_out]["operator"][1, :, 1, :] + ) @ np.ones(len(fake_output["xgrid"])) np.testing.assert_allclose(pdfs["a"], ref_a) From d289c4f956c9721febb2323f6ec976a00f687329 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:48:30 +0200 Subject: [PATCH 012/148] Recover everything but runner --- benchmarks/eko/benchmark_evol_to_unity.py | 22 +++++--- src/eko/evolution_operator/grid.py | 14 ++--- src/eko/interpolation.py | 10 ++-- src/eko/output/struct.py | 1 + src/ekomark/apply.py | 4 +- tests/eko/test_ev_op_grid.py | 22 +++++--- tests/eko/test_interpolation.py | 68 +++++++++++++++-------- tests/eko/test_ome.py | 52 ++++++++++------- 8 files changed, 118 insertions(+), 75 deletions(-) diff --git a/benchmarks/eko/benchmark_evol_to_unity.py b/benchmarks/eko/benchmark_evol_to_unity.py index 4254327c8..e0d38dd70 100644 --- a/benchmarks/eko/benchmark_evol_to_unity.py +++ b/benchmarks/eko/benchmark_evol_to_unity.py @@ -44,15 +44,19 @@ class BenchmarkBackwardForward: "Q2grid": [10], # here you need a very dense grid "xgrid": np.linspace(1e-1, 1, 30), - # "interpolation_xgrid": make_grid(30,30, x_min=1e-3), - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": False, - "debug_skip_non_singlet": False, - "ev_op_max_order": 1, - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + # "xgrid": make_grid(30,30, x_min=1e-3), + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": 1, + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": { + "skip_singlet": False, + "skip_non_singlet": False, + }, } new_theory, new_operators = compatibility.update(theory_card, operators_card) g = OperatorGrid.from_dict( diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index db235f3d1..e8206073f 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -57,7 +57,7 @@ def __init__( # check order = config["order"] method = config["method"] - if not method in [ + if method not in [ "iterate-exact", "iterate-expanded", "truncated", @@ -119,13 +119,13 @@ def from_dict( } method = mod_ev2method.get(method, method) config["method"] = method - config["backward_inversion"] = operators_card["backward_inversion"] + config["backward_inversion"] = operators_card["configs"]["backward_inversion"] config["fact_to_ren"] = (theory_card["fact_to_ren_scale_ratio"]) ** 2 - config["ev_op_max_order"] = operators_card["ev_op_max_order"] - config["ev_op_iterations"] = operators_card["ev_op_iterations"] - config["debug_skip_singlet"] = operators_card["debug_skip_singlet"] - config["debug_skip_non_singlet"] = operators_card["debug_skip_non_singlet"] - config["n_integration_cores"] = operators_card["n_integration_cores"] + config["ev_op_max_order"] = operators_card["configs"]["ev_op_max_order"] + config["ev_op_iterations"] = operators_card["configs"]["ev_op_iterations"] + config["n_integration_cores"] = operators_card["configs"]["n_integration_cores"] + config["debug_skip_singlet"] = operators_card["debug"]["skip_singlet"] + config["debug_skip_non_singlet"] = operators_card["debug"]["skip_non_singlet"] config["HQ"] = theory_card["HQ"] config["ModSV"] = theory_card["ModSV"] q2_grid = np.array(operators_card["Q2grid"], np.float_) diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index 8c5d0c513..09f63f7dd 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -534,8 +534,8 @@ def from_dict(cls, operators_card, mode_N=True): xgrid = make_grid(*xgrid[1:]) elif xgrid[0] == "make_lambert_grid": xgrid = make_lambert_grid(*xgrid[1:]) - is_log_interpolation = bool(operators_card["interpolation_is_log"]) - polynom_rank = operators_card["interpolation_polynomial_degree"] + is_log_interpolation = bool(operators_card["configs"]["interpolation_is_log"]) + polynom_rank = operators_card["configs"]["interpolation_polynomial_degree"] return cls( xgrid, polynom_rank, @@ -608,8 +608,10 @@ def to_dict(self): """ ret = { "xgrid": self.xgrid_raw, - "interpolation_polynomial_degree": self.polynomial_degree, - "interpolation_is_log": self.log, + "configs": { + "interpolation_polynomial_degree": self.polynomial_degree, + "interpolation_is_log": self.log, + }, } return ret diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 61772572f..c01ca7838 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -53,6 +53,7 @@ class Configs(DictLike): interpolation_polynomial_degree: int interpolation_is_log: bool backward_inversion: Literal["exact", "expanded"] + n_integration_cores: int = 1 @dataclass diff --git a/src/ekomark/apply.py b/src/ekomark/apply.py index f91db4659..d8dfa3f45 100644 --- a/src/ekomark/apply.py +++ b/src/ekomark/apply.py @@ -87,9 +87,7 @@ def apply_pdf_flavor(eko, lhapdf_like, targetgrid=None, flavor_rotation=None): # rotate/interpolate to target grid if targetgrid is not None: - b = interpolation.InterpolatorDispatcher.from_dict( - eko.configs.raw | dict(xgrid=eko.xgrid), False - ) + b = interpolation.InterpolatorDispatcher.from_dict(eko.raw, False) rot = b.get_interpolation(targetgrid) for evpdf in out_grid.values(): for pdf_label in evpdf["pdfs"]: diff --git a/tests/eko/test_ev_op_grid.py b/tests/eko/test_ev_op_grid.py index 0b6fdbbc0..86913e6ac 100644 --- a/tests/eko/test_ev_op_grid.py +++ b/tests/eko/test_ev_op_grid.py @@ -41,15 +41,19 @@ def _get_setup(self, use_FFNS): } operators_card = { "Q2grid": [1, 100**2], - "interpolation_xgrid": [0.1, 1.0], - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": True, - "debug_skip_non_singlet": False, - "ev_op_max_order": (2, 0), - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + "xgrid": [0.1, 1.0], + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": (2, 0), + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": { + "skip_singlet": True, + "skip_non_singlet": False, + }, } if use_FFNS: theory_card["FNS"] = "FFNS" diff --git a/tests/eko/test_interpolation.py b/tests/eko/test_interpolation.py index c4e06b7b6..91e4905ad 100644 --- a/tests/eko/test_interpolation.py +++ b/tests/eko/test_interpolation.py @@ -102,9 +102,11 @@ def test_init(self): def test_from_dict(self): d = { - "interpolation_xgrid": ["make_grid", 3, 3], - "interpolation_is_log": False, - "interpolation_polynomial_degree": 1, + "xgrid": ["make_grid", 3, 3], + "configs": { + "interpolation_is_log": False, + "interpolation_polynomial_degree": 1, + }, } a = interpolation.InterpolatorDispatcher.from_dict(d) np.testing.assert_array_almost_equal( @@ -112,17 +114,21 @@ def test_from_dict(self): ) assert a.polynomial_degree == 1 dd = { - "interpolation_xgrid": ["make_lambert_grid", 20], - "interpolation_is_log": False, - "interpolation_polynomial_degree": 1, + "xgrid": ["make_lambert_grid", 20], + "configs": { + "interpolation_is_log": False, + "interpolation_polynomial_degree": 1, + }, } aa = interpolation.InterpolatorDispatcher.from_dict(dd) assert len(aa.xgrid) == 20 with pytest.raises(ValueError): d = { - "interpolation_xgrid": [], - "interpolation_is_log": False, - "interpolation_polynomial_degree": 1, + "xgrid": [], + "configs": { + "interpolation_is_log": False, + "interpolation_polynomial_degree": 1, + }, } interpolation.InterpolatorDispatcher.from_dict(d) @@ -211,14 +217,20 @@ def test_eval_N(self): p0_cs_ref = [1, -1] for act_c, res_c in zip(p0N.areas[0], p0_cs_ref): assert_almost_equal(act_c, res_c) - p0Nref = lambda N, lnx: (1 / N - 1 / (N + 1)) * np.exp(-N * lnx) + + def p0Nref(N, lnx): + return (1 / N - 1 / (N + 1)) * np.exp(-N * lnx) + # p_1(x) = x -> \tilde p_1(N) = 1/(N+1) p1N = inter_N[1] assert len(p1N.areas) == 1 p1_cs_ref = [0, 1] for act_c, res_c in zip(p1N.areas[0], p1_cs_ref): assert_almost_equal(act_c, res_c) - p1Nref = lambda N, lnx: (1 / (N + 1)) * np.exp(-N * lnx) + + def p1Nref(N, lnx): + return (1 / (N + 1)) * np.exp(-N * lnx) + # iterate configurations for N in [1.0, 2.0, complex(1.0, 1.0)]: # check skip @@ -238,22 +250,34 @@ def test_log_eval_N(self): p0_cs_ref = [0, -1] for act_c, res_c in zip(p0N.areas[0], p0_cs_ref): assert_almost_equal(act_c, res_c) - # Full -> \tilde p_0(N) = exp(-N)(exp(N)-1-N)/N^2 - # MMa: Integrate[x^(n-1) (-Log[x]),{x,1/E,1}] - p0Nref_full = lambda N, lnx: ((np.exp(N) - 1 - N) / N**2) * np.exp( - -N * (lnx + 1) - ) - # partial = lower bound is neglected; - p0Nref_partial = lambda N, lnx: (1 / N**2) * np.exp(-N * lnx) + + def p0Nref_full(N, lnx): + r""" + Full -> \tilde p_0(N) = exp(-N)(exp(N)-1-N)/N^2 + MMa: Integrate[x^(n-1) (-Log[x]),{x,1/E,1}] + """ + return ((np.exp(N) - 1 - N) / N**2) * np.exp(-N * (lnx + 1)) + + def p0Nref_partial(N, lnx): + "partial = lower bound is neglected" + return (1 / N**2) * np.exp(-N * lnx) + p1N = inter_N[1] assert len(p1N.areas) == 1 p1_cs_ref = [1, 1] for act_c, res_c in zip(p1N.areas[0], p1_cs_ref): assert_almost_equal(act_c, res_c) - # p_1(x) = 1+\ln(x) -> \tilde p_1(N) = (exp(-N)-1+N)/N^2 - # MMa: Integrate[x^(n-1) (1+Log[x]),{x,1/E,1}] - p1Nref_full = lambda N, lnx: ((np.exp(-N) - 1 + N) / N**2) * np.exp(-N * lnx) - p1Nref_partial = lambda N, lnx: (1 / N - 1 / N**2) * np.exp(-N * lnx) + + def p1Nref_full(N, lnx): + r""" + p_1(x) = 1+\ln(x) -> \tilde p_1(N) = (exp(-N)-1+N)/N^2 + MMa: Integrate[x^(n-1) (1+Log[x]),{x,1/E,1}] + """ + return ((np.exp(-N) - 1 + N) / N**2) * np.exp(-N * lnx) + + def p1Nref_partial(N, lnx): + return (1 / N - 1 / N**2) * np.exp(-N * lnx) + # iterate configurations for N in [1.0, 2.0, complex(1.0, 1.0)]: # check skip diff --git a/tests/eko/test_ome.py b/tests/eko/test_ome.py index 6d9be8aad..178058522 100644 --- a/tests/eko/test_ome.py +++ b/tests/eko/test_ome.py @@ -330,15 +330,19 @@ def test_labels(self): for skip_ns in [True, False]: operators_card = { "Q2grid": [1, 10], - "interpolation_xgrid": [0.1, 1.0], - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": skip_singlet, - "debug_skip_non_singlet": skip_ns, - "ev_op_max_order": (2, 0), - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + "xgrid": [0.1, 1.0], + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": (2, 0), + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": { + "skip_singlet": skip_singlet, + "skip_non_singlet": skip_ns, + }, } g = OperatorGrid.from_dict( self.theory_card, @@ -415,8 +419,10 @@ def test_compute_n3lo(self): np.testing.assert_allclose(mat, np.triu(mat)) def test_compute_lo(self): - self.theory_card.update({"order": (1, 0)}) - self.operators_card.update({"debug_skip_singlet": False}) + self.theory_card.update({"PTO": (1, 0)}) + self.operators_card.update( + dict(debug={"debug_skip_singlet": False, "skip_non_singlet": False}) + ) g = OperatorGrid.from_dict( self.theory_card, self.operators_card, @@ -463,15 +469,19 @@ def test_compute_lo(self): def test_compute_nlo(self): operators_card = { "Q2grid": [20], - "interpolation_xgrid": [0.001, 0.01, 0.1, 1.0], - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": False, - "debug_skip_non_singlet": False, - "ev_op_max_order": (1, 0), - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + "xgrid": [0.001, 0.01, 0.1, 1.0], + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": (2, 0), + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": { + "skip_singlet": False, + "skip_non_singlet": False, + }, } t = copy.deepcopy(self.theory_card) t["order"] = (1, 0) @@ -493,7 +503,7 @@ def test_compute_nlo(self): ) o.compute() - dim = len(operators_card["interpolation_xgrid"]) + dim = len(operators_card["xgrid"]) shape = (dim, dim) for indices in [(100, br.matching_hplus_pid), (200, br.matching_hminus_pid)]: assert o.op_members[(indices[0], indices[0])].value.shape == shape From dde03e4684e70e20190ca2911a6abf883aac4743 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:52:20 +0200 Subject: [PATCH 013/148] Recover all runner tests --- src/eko/output/struct.py | 15 ++++++++- src/eko/runner.py | 49 +++++++++++++++++------------ tests/eko/test_runner.py | 66 ++++++++++++---------------------------- 3 files changed, 62 insertions(+), 68 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index c01ca7838..5717e54b0 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -94,6 +94,8 @@ def __setitem__(self, q2, op): # TODO: autodump if isinstance(op, dict): op = Operator.from_dict(op) + if not isinstance(op, Operator): + raise ValueError("Only operators can be stored.") self._operators[q2] = op def items(self): @@ -110,10 +112,21 @@ def from_dict(cls, dictionary): Q02=dictionary["Q0"] ** 2, _operators={q2: None for q2 in dictionary["Q2grid"]}, configs=Configs.from_dict(dictionary["configs"]), - rotations=Rotations.from_dict(dictionary["rotations"]), + rotations=Rotations.from_dict(dictionary.get("rotations", {})), debug=Debug.from_dict(dictionary.get("debug", {})), ) + @classmethod + def load(cls, path): + return cls( + xgrid=None, + Q02=None, + _operators={q2: None for q2 in ()}, + configs=None, + rotations=None, + debug=None, + ) + @property def raw(self): return dict( diff --git a/src/eko/runner.py b/src/eko/runner.py index 45cc42222..face895a8 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -11,7 +11,7 @@ from . import compatibility, interpolation, msbar_masses from .couplings import Couplings from .evolution_operator.grid import OperatorGrid -from .output import EKO +from .output import EKO, manipulate from .thresholds import ThresholdsAtlas logger = logging.getLogger(__name__) @@ -49,14 +49,13 @@ def __init__(self, theory_card, operators_card): # setup basis grid bfd = interpolation.InterpolatorDispatcher.from_dict(new_operators) - self.out.update(bfd.to_dict()) + # setup the Threshold path, compute masses if necessary masses = None if new_theory["HQ"] == "MSBAR": masses = msbar_masses.compute(new_theory) tc = ThresholdsAtlas.from_dict(new_theory, masses=masses) - self.out["q2_ref"] = float(tc.q2_ref) # strong coupling sc = Couplings.from_dict(new_theory, masses=masses) # setup operator grid @@ -67,15 +66,22 @@ def __init__(self, theory_card, operators_card): sc, bfd, ) - self.out["inputgrid"] = bfd.xgrid_raw - self.out["targetgrid"] = bfd.xgrid_raw + + rot = operators_card.get("rotations", {}) self.post_process = dict( - inputgrid=new_operators.get("inputgrid", bfd.xgrid_raw), - targetgrid=new_operators.get("targetgrid", bfd.xgrid_raw), - inputbasis=new_operators.get("inputbasis"), - targetbasis=new_operators.get("targetbasis"), + inputgrid=rot.get("inputgrid", bfd.xgrid_raw), + targetgrid=rot.get("targetgrid", bfd.xgrid_raw), + inputbasis=rot.get("inputbasis"), + targetbasis=rot.get("targetbasis"), ) + if "rotations" not in operators_card: + operators_card["rotations"] = {} + operators_card["rotations"]["inputgrid"] = bfd.xgrid_raw + operators_card["rotations"]["targetgrid"] = bfd.xgrid_raw + + self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref)) | operators_card) + def get_output(self): """ Collects all data for output (to run the evolution) @@ -86,27 +92,28 @@ def get_output(self): output instance """ # add all operators - Q2grid = {} - self.out["inputpids"] = br.flavor_basis_pids - self.out["targetpids"] = br.flavor_basis_pids - self.out["inputgrid"] = self.out["interpolation_xgrid"] - self.out["targetgrid"] = self.out["interpolation_xgrid"] + self.out.rotations.inputpids = np.array(br.flavor_basis_pids) + self.out.rotations.targetpids = np.array(br.flavor_basis_pids) + self.out.rotations.inputgrid = self.out.xgrid + self.out.rotations.targetgrid = self.out.xgrid for final_scale, op in self.op_grid.compute().items(): - Q2grid[float(final_scale)] = op - self.out["Q2grid"] = Q2grid + self.out[float(final_scale)] = op + # reshape xgrid inputgrid = ( self.post_process["inputgrid"] - if self.post_process["inputgrid"] is not self.out["interpolation_xgrid"] + if self.post_process["inputgrid"] is not self.out.xgrid else None ) targetgrid = ( self.post_process["targetgrid"] - if self.post_process["targetgrid"] is not self.out["interpolation_xgrid"] + if self.post_process["targetgrid"] is not self.out.xgrid else None ) if inputgrid is not None or targetgrid is not None: - self.out.xgrid_reshape(targetgrid=targetgrid, inputgrid=inputgrid) + manipulate.xgrid_reshape( + self.out, targetgrid=targetgrid, inputgrid=inputgrid + ) # reshape flavors inputbasis = self.post_process["inputbasis"] @@ -116,5 +123,7 @@ def get_output(self): if targetbasis is not None: targetbasis = np.array(targetbasis) if inputbasis is not None or targetbasis is not None: - self.out.flavor_reshape(targetbasis=targetbasis, inputbasis=inputbasis) + manipulate.flavor_reshape( + self.out, targetbasis=targetbasis, inputbasis=inputbasis + ) return copy.deepcopy(self.out) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 6b5c66d5f..23ada38f6 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -37,7 +37,7 @@ } operators_card = { "Q2grid": [10, 100], - "interpolation_xgrid": [0.01, 0.1, 1.0], + "xgrid": [0.01, 0.1, 1.0], "configs": { "interpolation_polynomial_degree": 1, "interpolation_is_log": True, @@ -56,13 +56,7 @@ def test_raw(): oc = copy.deepcopy(operators_card) r = eko.runner.Runner(tc, oc) o = r.get_output() - check_shapes( - o, - o["interpolation_xgrid"], - o["interpolation_xgrid"], - tc, - oc, - ) + check_shapes(o, o.xgrid, o.xgrid, tc, oc) def test_targetgrid(): @@ -70,16 +64,10 @@ def test_targetgrid(): tc = copy.deepcopy(theory_card) oc = copy.deepcopy(operators_card) tgrid = [0.1, 1.0] - oc["targetgrid"] = tgrid + oc["rotations"] = dict(targetgrid=tgrid) r = eko.runner.Runner(tc, oc) o = r.get_output() - check_shapes( - o, - tgrid, - o["interpolation_xgrid"], - tc, - oc, - ) + check_shapes(o, tgrid, o.xgrid, tc, oc) def test_targetbasis(): @@ -90,37 +78,27 @@ def test_targetbasis(): oc["inputbasis"] = np.eye(14) + 0.1 * np.random.rand(14, 14) r = eko.runner.Runner(tc, oc) o = r.get_output() - check_shapes( - o, - o["interpolation_xgrid"], - o["interpolation_xgrid"], - tc, - oc, - ) + check_shapes(o, o.xgrid, o.xgrid, tc, oc) def check_shapes(o, txs, ixs, theory_card, operators_card): - tpids = len(o["targetpids"]) - ipids = len(o["inputpids"]) + tpids = len(o.rotations.targetpids) + ipids = len(o.rotations.inputpids) op_shape = (tpids, len(txs), ipids, len(ixs)) # check output = input - np.testing.assert_allclose( - o["interpolation_xgrid"], operators_card["interpolation_xgrid"] - ) - np.testing.assert_allclose(o["targetgrid"], txs) - np.testing.assert_allclose(o["inputgrid"], ixs) + np.testing.assert_allclose(o.xgrid, operators_card["xgrid"]) + np.testing.assert_allclose(o.rotations.targetgrid, txs) + np.testing.assert_allclose(o.rotations.inputgrid, ixs) for k in ["interpolation_polynomial_degree", "interpolation_is_log"]: - assert o[k] == operators_card[k] - np.testing.assert_allclose(o["q2_ref"], theory_card["Q0"] ** 2) + assert getattr(o.configs, k) == operators_card["configs"][k] + np.testing.assert_allclose(o.Q02, theory_card["Q0"] ** 2) # check available operators - assert len(o["Q2grid"]) == len(operators_card["Q2grid"]) - assert list(o["Q2grid"].keys()) == operators_card["Q2grid"] - for ops in o["Q2grid"].values(): - assert "operators" in ops - assert "operator_errors" in ops - assert ops["operators"].shape == op_shape - assert ops["operator_errors"].shape == op_shape + assert len(o.Q2grid) == len(operators_card["Q2grid"]) + assert list(o.Q2grid) == operators_card["Q2grid"] + for _, ops in o.items(): + assert ops.operator.shape == op_shape + assert ops.error.shape == op_shape def test_vfns(): @@ -130,14 +108,8 @@ def test_vfns(): tc["kcThr"] = 1.0 tc["kbThr"] = 1.0 tc["order"] = (3, 0) - oc["debug_skip_non_singlet"] = False + oc["debug"]["skip_non_singlet"] = False # tc,oc = compatibility.update(theory_card,operators_card) r = eko.runner.Runner(tc, oc) o = r.get_output() - check_shapes( - o, - o["interpolation_xgrid"], - o["interpolation_xgrid"], - tc, - oc, - ) + check_shapes(o, o.xgrid, o.xgrid, tc, oc) From 0ce9ead9d6c752b9aeda738855cfc1dec1f7ae68 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:53:19 +0200 Subject: [PATCH 014/148] Recover all tests --- tests/eko/test_ev_op_grid.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/eko/test_ev_op_grid.py b/tests/eko/test_ev_op_grid.py index 86913e6ac..0fee639e3 100644 --- a/tests/eko/test_ev_op_grid.py +++ b/tests/eko/test_ev_op_grid.py @@ -112,14 +112,10 @@ def test_compute_q2grid(self): # we can also pass a single number opg = opgrid.compute() assert len(opg) == 2 - assert all( - k in op for k in ["operators", "operator_errors"] for op in opg.values() - ) + assert all(k in op for k in ["operator", "error"] for op in opg.values()) opg = opgrid.compute(3) assert len(opg) == 1 - assert all( - k in op for k in ["operators", "operator_errors"] for op in opg.values() - ) + assert all(k in op for k in ["operator", "error"] for op in opg.values()) def test_grid_computation_VFNS(self): """Checks that the grid can be computed""" @@ -144,5 +140,5 @@ def test_mod_expanded(self): ) sv_opg = sv_opgrid.compute(3) np.testing.assert_allclose( - opg[3]["operators"], sv_opg[3]["operators"], atol=0.7 * epsilon + opg[3]["operator"], sv_opg[3]["operator"], atol=0.7 * epsilon ) From 42e1b90f0ea2fec075848a79250c8c6b07473db1 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 17:10:10 +0100 Subject: [PATCH 015/148] Move ekobox loading to legacy --- src/ekobox/evol_pdf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index ca019c69b..af2e425fe 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -2,6 +2,7 @@ import pathlib import eko +import eko.output.legacy from eko import basis_rotation as br from ekomark import apply @@ -60,9 +61,9 @@ def evolve_pdfs( ops_id = f"o{operators_card['hash'][:6]}_t{theory_card['hash'][:6]}.tar" ops_id_path = pathlib.Path(ops_id) outpath = my_path / ops_id_path.relative_to(ops_id_path.anchor) - eko_output = eko.output.Output.load_tar(outpath) + eko_output = eko.output.legacy.load_tar(outpath) else: - eko_output = eko.output.Output.load_tar(my_path) + eko_output = eko.output.legacy.load_tar(my_path) else: eko_output = eko.run_dglap(theory_card, operators_card) if store_path is not None: From 47832326547a5f25560aec69ef9834772954bb54 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 17:19:10 +0100 Subject: [PATCH 016/148] Replace bleeding-edge syntax for dict union --- src/eko/runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/runner.py b/src/eko/runner.py index face895a8..1cd76e87a 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -80,7 +80,7 @@ def __init__(self, theory_card, operators_card): operators_card["rotations"]["inputgrid"] = bfd.xgrid_raw operators_card["rotations"]["targetgrid"] = bfd.xgrid_raw - self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref)) | operators_card) + self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref), **operators_card)) def get_output(self): """ From fe2f5553a903f5366e5b493b5cd3d288fd8df0d2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:54:14 +0200 Subject: [PATCH 017/148] Update ekobox benchmarks --- benchmarks/ekobox/benchmark_evol_pdf.py | 24 +++++++---------- pyproject.toml | 2 +- src/ekobox/gen_op.py | 35 ++++++++++++++----------- src/ekomark/data/operators.py | 26 ++++++++++-------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/benchmarks/ekobox/benchmark_evol_pdf.py b/benchmarks/ekobox/benchmark_evol_pdf.py index a4233171f..485b86618 100644 --- a/benchmarks/ekobox/benchmark_evol_pdf.py +++ b/benchmarks/ekobox/benchmark_evol_pdf.py @@ -5,8 +5,8 @@ import numpy as np import pytest +import eko.output.legacy from eko import basis_rotation as br -from eko import output from ekobox import evol_pdf as ev_p from ekobox import gen_op as g_o from ekobox import gen_theory as g_t @@ -56,11 +56,11 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): all_blocks = (load.load_blocks_from_file("EvPDF", 0))[1] info = load.load_info_from_file("EvPDF") ev_pdf = lhapdf.mkPDF("EvPDF", 0) - assert info["XMin"] == op["interpolation_xgrid"][0] + assert info["XMin"] == op["xgrid"][0] assert info["SetDesc"] == "MyEvolvedPDF" assert info["MZ"] == theory["MZ"] assert info["Debug"] == "Debug" - xgrid = op["interpolation_xgrid"] + xgrid = op["xgrid"] for Q2 in [20.0, 100.0, 10000.0]: for x in xgrid[10:40]: for pid in [21, 1, -1, 2, -2, 3, -3]: @@ -80,9 +80,7 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): @pytest.mark.isolated def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): - op = g_o.gen_op_card( - [10, 100], update={"interpolation_xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} - ) + op = g_o.gen_op_card([10, 100], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) theory = g_t.gen_theory_card(0, 1.0) with lhapdf_path(test_pdf): pdfs = lhapdf.mkPDFs("myMSTW2008nlo90cl") @@ -96,7 +94,7 @@ def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): new_pdf_1 = lhapdf.mkPDF("Debug", 0) new_pdf_2 = lhapdf.mkPDF("Debug", 1) info = load.load_info_from_file("Debug") - assert info["XMin"] == op["interpolation_xgrid"][0] + assert info["XMin"] == op["xgrid"][0] assert len(pdfs) == len(new_pdfs) for Q2 in [10, 100]: for x in [1e-7, 0.01, 0.1, 0.2, 0.3]: @@ -106,22 +104,18 @@ def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): @pytest.mark.isolated def benchmark_gen_and_dump_out(tmp_path): - op = g_o.gen_op_card( - [100.0], update={"interpolation_xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} - ) + op = g_o.gen_op_card([100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) theory = g_t.gen_theory_card(0, 1.0) out = ev_p.gen_out(theory, op, path=tmp_path) ops_id = f"o{op['hash'][:6]}_t{theory['hash'][:6]}" outpath = f"{tmp_path}/{ops_id}.tar" - loaded_out = output.Output.load_tar(outpath) - for el, load_el in zip( - out["interpolation_xgrid"], loaded_out["interpolation_xgrid"] - ): + loaded_out = eko.output.legacy.load_tar(outpath) + for el, load_el in zip(out["xgrid"], loaded_out.xgrid): assert el == load_el for el, load_el in zip( - out["Q2grid"][100.0]["operators"], loaded_out["Q2grid"][100.0]["operators"] + out["Q2grid"][100.0]["operators"], loaded_out[100.0].operator ): np.testing.assert_allclose( out["Q2grid"][100.0]["operators"], diff --git a/pyproject.toml b/pyproject.toml index ebcbe3e55..732eba80d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,7 @@ genpdf = "ekobox.genpdf.cli:cli" test = "pytest tests" coverage = "$BROWSER htmlcov/index.html" test-cov = ["test", "coverage"] -bench = "pytest benchmarks" +bench = ["bench-iso", "bench-run"] bench-iso.cmd = "pytest benchmarks -m isolated" bench-iso.env.NUMBA_DISABLE_JIT.default = "0" bench-run.cmd = "pytest benchmarks -m 'not isolated'" diff --git a/src/ekobox/gen_op.py b/src/ekobox/gen_op.py index 9193d2c15..5eba6ca35 100644 --- a/src/ekobox/gen_op.py +++ b/src/ekobox/gen_op.py @@ -8,23 +8,26 @@ def gen_op_card(Q2grid, update=None, name=None): - """ + """Generate operators card. + Generates an operator card with some mandatory user choice (in this case only the Q2 grid) and some default values which - can be changed by the update input dict + can be changed by the update input dict. Parameters ---------- - Q2grid : list(float) - grid for Q2 - update : dict - dictionary of info to update in op. card - name : str - name of exported op.card (if name not None) + Q2grid : list(float) + grid for Q2 + update : dict + dictionary of info to update in op. card + name : str + name of exported op.card (if name not None) + Returns ------- - : dict - operator card + dict + operator card + """ # Constructing the dictionary with some default value def_op = copy.deepcopy(operators.default_card) @@ -43,16 +46,16 @@ def gen_op_card(Q2grid, update=None, name=None): def export_op_card(name, op): - """ - Export the operators card in the current directory + """Export the operators card in the current directory Parameters ---------- - name : str - name of the op. card to export + name : str + name of the op. card to export + + op : dict + op card - op : dict - op card """ target = f"{name}.yaml" with open(target, "w", encoding="utf-8") as out: diff --git a/src/ekomark/data/operators.py b/src/ekomark/data/operators.py index 1a532e5e5..146ed70c8 100644 --- a/src/ekomark/data/operators.py +++ b/src/ekomark/data/operators.py @@ -9,19 +9,23 @@ from . import db default_card = dict( - interpolation_xgrid=interpolation.make_grid(30, 20).tolist(), - interpolation_polynomial_degree=4, - interpolation_is_log=True, - debug_skip_non_singlet=False, - debug_skip_singlet=False, - ev_op_max_order=10, - ev_op_iterations=10, - backward_inversion="expanded", - n_integration_cores=0, - Q2grid=[100], + sorted( + dict( + xgrid=interpolation.make_grid(30, 20).tolist(), + configs=dict( + interpolation_polynomial_degree=4, + interpolation_is_log=True, + ev_op_max_order=10, + ev_op_iterations=10, + backward_inversion="expanded", + n_integration_cores=0, + ), + debug=dict(skip_non_singlet=False, skip_singlet=False), + Q2grid=[100], + ).items() + ) ) -default_card = dict(sorted(default_card.items())) lhapdf_config = { # "ev_op_max_order": [10], From cfc2288e7b5d0fcce0d9e776ccdd241e2c5280b8 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 17 Mar 2022 18:49:10 +0100 Subject: [PATCH 018/148] Replace remaining interpolation_xgrids --- benchmarks/HERA20_bench.py | 2 +- benchmarks/ekobox/benchmark_gen_info.py | 4 ++-- benchmarks/ekobox/benchmark_utils.py | 4 ++-- benchmarks/lha_paper_bench.py | 4 ++-- src/ekobox/evol_pdf.py | 2 +- src/ekobox/gen_info.py | 4 ++-- src/ekomark/benchmark/external/apfel_utils.py | 2 +- src/ekomark/benchmark/external/lhapdf_utils.py | 2 +- src/ekomark/benchmark/external/pegasus_utils.py | 2 +- src/ekomark/data/db.py | 2 +- src/ekomark/navigator/navigator.py | 2 +- src/ekomark/plots.py | 2 +- tests/conftest.py | 10 +++++----- tests/eko/test_output.py | 12 +++++------- 14 files changed, 26 insertions(+), 28 deletions(-) diff --git a/benchmarks/HERA20_bench.py b/benchmarks/HERA20_bench.py index a09d83f3d..79f17c92b 100644 --- a/benchmarks/HERA20_bench.py +++ b/benchmarks/HERA20_bench.py @@ -22,7 +22,7 @@ } # LHAPDF x-range is smaller -base_op = {"interpolation_xgrid": interpolation.make_lambert_grid(50, 1.0e-6)} +base_op = {"xgrid": interpolation.make_lambert_grid(50, 1.0e-6)} class BenchmarkHERA20(Runner): diff --git a/benchmarks/ekobox/benchmark_gen_info.py b/benchmarks/ekobox/benchmark_gen_info.py index 8e8f65566..7eb10d3d3 100644 --- a/benchmarks/ekobox/benchmark_gen_info.py +++ b/benchmarks/ekobox/benchmark_gen_info.py @@ -22,5 +22,5 @@ def benchmark_create_info_file(): assert info["NumMembers"] == 4 assert info["MTop"] == theory["mt"] np.testing.assert_allclose(info["QMin"], math.sqrt(op["Q2grid"][0]), rtol=1e-5) - assert info["XMin"] == op["interpolation_xgrid"][0] - assert info["XMax"] == op["interpolation_xgrid"][-1] == 1.0 + assert info["XMin"] == op["xgrid"][0] + assert info["XMax"] == op["xgrid"][-1] == 1.0 diff --git a/benchmarks/ekobox/benchmark_utils.py b/benchmarks/ekobox/benchmark_utils.py index 0b8ac5664..d29986eb0 100644 --- a/benchmarks/ekobox/benchmark_utils.py +++ b/benchmarks/ekobox/benchmark_utils.py @@ -12,13 +12,13 @@ def benchmark_ekos_product(): # Generating two ekos op1 = g_o.gen_op_card( - [60.0, 80.0, 100.0], update={"interpolation_xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} + [60.0, 80.0, 100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} ) theory1 = g_t.gen_theory_card(0, 5.0) op2 = g_o.gen_op_card( [80.0, 100.0, 120.0], - update={"interpolation_xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}, + update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}, ) theory2 = g_t.gen_theory_card(0, 10.0) theory_err = g_t.gen_theory_card(0, 5.0) diff --git a/benchmarks/lha_paper_bench.py b/benchmarks/lha_paper_bench.py index 65bc64fb8..9d3bc36d1 100644 --- a/benchmarks/lha_paper_bench.py +++ b/benchmarks/lha_paper_bench.py @@ -112,8 +112,8 @@ def run_lha(self, theory_updates): [ { "Q2grid": [1e4], - "ev_op_iterations": 10, - "interpolation_xgrid": make_lambert_grid(60).tolist(), + "configs": {"ev_op_iterations": 10}, + "xgrid": make_lambert_grid(60).tolist(), } ], ["ToyLH"], diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index af2e425fe..28914b182 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -74,7 +74,7 @@ def evolve_pdfs( evolved_PDF_list.append(apply.apply_pdf(eko_output, initial_PDF, targetgrid)) if targetgrid is None: - targetgrid = operators_card["interpolation_xgrid"] + targetgrid = operators_card["xgrid"] if info_update is None: info_update = {} info_update["XMin"] = targetgrid[0] diff --git a/src/ekobox/gen_info.py b/src/ekobox/gen_info.py index 7deab82e9..757ae4c7c 100644 --- a/src/ekobox/gen_info.py +++ b/src/ekobox/gen_info.py @@ -32,8 +32,8 @@ def create_info_file(theory_card, operators_card, num_members, info_update): template_info.update(info_update) template_info["NumFlavors"] = 14 template_info["Flavors"] = [-6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 21, 22] - template_info["XMin"] = operators_card["interpolation_xgrid"][0] - template_info["XMax"] = operators_card["interpolation_xgrid"][-1] + template_info["XMin"] = operators_card["xgrid"][0] + template_info["XMax"] = operators_card["xgrid"][-1] template_info["NumMembers"] = num_members template_info["OrderQCD"] = theory_card["PTO"] template_info["QMin"] = round(math.sqrt(operators_card["Q2grid"][0]), 4) diff --git a/src/ekomark/benchmark/external/apfel_utils.py b/src/ekomark/benchmark/external/apfel_utils.py index dbc94a9de..3bb1016c8 100644 --- a/src/ekomark/benchmark/external/apfel_utils.py +++ b/src/ekomark/benchmark/external/apfel_utils.py @@ -36,7 +36,7 @@ def compute_apfel_data( output containing: target_xgrid, values """ - target_xgrid = operators["interpolation_xgrid"] + target_xgrid = operators["xgrid"] pdf_name = pdf.set().name # Load apfel diff --git a/src/ekomark/benchmark/external/lhapdf_utils.py b/src/ekomark/benchmark/external/lhapdf_utils.py index c3533cda6..c623070d2 100644 --- a/src/ekomark/benchmark/external/lhapdf_utils.py +++ b/src/ekomark/benchmark/external/lhapdf_utils.py @@ -28,7 +28,7 @@ def compute_LHAPDF_data(operators, pdf, skip_pdfs, rotate_to_evolution_basis=Fal output containing: target_xgrid, values """ - target_xgrid = operators["interpolation_xgrid"] + target_xgrid = operators["xgrid"] out_tabs = {} for q2 in operators["Q2grid"]: diff --git a/src/ekomark/benchmark/external/pegasus_utils.py b/src/ekomark/benchmark/external/pegasus_utils.py index 07f145848..87fb2f807 100644 --- a/src/ekomark/benchmark/external/pegasus_utils.py +++ b/src/ekomark/benchmark/external/pegasus_utils.py @@ -29,7 +29,7 @@ def compute_pegasus_data(theory, operators, skip_pdfs, rotate_to_evolution_basis """ import pegasus # pylint:disable=import-error,import-outside-toplevel - target_xgrid = operators["interpolation_xgrid"] + target_xgrid = operators["xgrid"] # init pegasus nf = theory["NfFF"] diff --git a/src/ekomark/data/db.py b/src/ekomark/data/db.py index 67328c522..98d754751 100644 --- a/src/ekomark/data/db.py +++ b/src/ekomark/data/db.py @@ -16,7 +16,7 @@ class Operator(Base): # pylint: disable=too-few-public-methods interpolation_is_log = Column(Text) interpolation_polynomial_degree = Column(Integer) - interpolation_xgrid = Column(Text) + xgrid = Column(Text) debug_skip_non_singlet = Column(Boolean) debug_skip_singlet = Column(Boolean) ev_op_max_order = Column(Integer) diff --git a/src/ekomark/navigator/navigator.py b/src/ekomark/navigator/navigator.py index 286e43f86..fca3b746a 100644 --- a/src/ekomark/navigator/navigator.py +++ b/src/ekomark/navigator/navigator.py @@ -71,7 +71,7 @@ def fill_operators(self, op, obj): obj : dict to be updated pandas record """ - xgrid = op["interpolation_xgrid"] + xgrid = op["xgrid"] obj["xgrid"] = ( f"{len(xgrid)}pts: " + f"{'log' if op['interpolation_is_log'] else 'x'}" diff --git a/src/ekomark/plots.py b/src/ekomark/plots.py index d7612d014..2fcff96f8 100644 --- a/src/ekomark/plots.py +++ b/src/ekomark/plots.py @@ -253,7 +253,7 @@ def save_operators_to_pdf(path, theory, ops, me, skip_pdfs, change_lab=False): new_op = {} new_op_err = {} # loop on xgrid point - for j in range(len(me["interpolation_xgrid"])): + for j in range(len(me["xgrid"])): # loop on pid in for label_in, val, val_err in zip(ops_names, res[j], res_err[j]): if label_in in skip_pdfs: diff --git a/tests/conftest.py b/tests/conftest.py index 03f38fa79..35a8a9213 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -35,18 +35,18 @@ def mk_g(self, q2s, lpids, lx): def fake_output(self): # build data - interpolation_xgrid = np.array([0.5, 1.0]) + xgrid = np.array([0.5, 1.0]) interpolation_polynomial_degree = 1 interpolation_is_log = False pids = [0, 1] q2_ref = 1 q2_out = 2 - Q2grid = self.mk_g([q2_out], len(pids), len(interpolation_xgrid)) + Q2grid = self.mk_g([q2_out], len(pids), len(xgrid)) d = dict( - xgrid=interpolation_xgrid, + xgrid=xgrid, rotations=dict( - targetgrid=interpolation_xgrid, - inputgrid=interpolation_xgrid, + targetgrid=xgrid, + inputgrid=xgrid, inputpids=pids, targetpids=pids, ), diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 138fd9248..f058551cb 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -225,21 +225,19 @@ def test_flavor_reshape(self, fake_output): manipulate.flavor_reshape(copy.deepcopy(o1)) def test_to_evol(self, fake_factory): - interpolation_xgrid = np.array([0.5, 1.0]) + xgrid = np.array([0.5, 1.0]) interpolation_polynomial_degree = 1 interpolation_is_log = False q2_ref = 1 q2_out = 2 - Q2grid = fake_factory.mk_g( - [q2_out], len(br.flavor_basis_pids), len(interpolation_xgrid) - ) + Q2grid = fake_factory.mk_g([q2_out], len(br.flavor_basis_pids), len(xgrid)) d = dict( - xgrid=interpolation_xgrid, + xgrid=xgrid, Q0=np.sqrt(q2_ref), Q2grid=Q2grid, rotations=dict( - targetgrid=interpolation_xgrid, - inputgrid=interpolation_xgrid, + targetgrid=xgrid, + inputgrid=xgrid, inputpids=br.flavor_basis_pids, targetpids=br.flavor_basis_pids, ), From b8566825b4fb69857f138cba4dff7a6c095429f2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:55:24 +0200 Subject: [PATCH 019/148] Recover ekobox but product --- benchmarks/ekobox/benchmark_evol_pdf.py | 11 ++++------ benchmarks/ekobox/benchmark_gen_op.py | 10 +++++---- benchmarks/ekobox/benchmark_utils.py | 22 ++++++++------------ src/eko/output/legacy.py | 6 +++++- src/ekobox/evol_pdf.py | 2 +- src/ekobox/gen_op.py | 7 ++++++- src/ekobox/utils.py | 27 +++++++++++++++---------- 7 files changed, 46 insertions(+), 39 deletions(-) diff --git a/benchmarks/ekobox/benchmark_evol_pdf.py b/benchmarks/ekobox/benchmark_evol_pdf.py index 485b86618..464416c38 100644 --- a/benchmarks/ekobox/benchmark_evol_pdf.py +++ b/benchmarks/ekobox/benchmark_evol_pdf.py @@ -112,12 +112,9 @@ def benchmark_gen_and_dump_out(tmp_path): ops_id = f"o{op['hash'][:6]}_t{theory['hash'][:6]}" outpath = f"{tmp_path}/{ops_id}.tar" loaded_out = eko.output.legacy.load_tar(outpath) - for el, load_el in zip(out["xgrid"], loaded_out.xgrid): - assert el == load_el - for el, load_el in zip( - out["Q2grid"][100.0]["operators"], loaded_out[100.0].operator - ): + assert list(out.xgrid) == list(loaded_out.xgrid) + for el, load_el in zip(out[100.0].operator, loaded_out[100.0].operator): np.testing.assert_allclose( - out["Q2grid"][100.0]["operators"], - loaded_out["Q2grid"][100.0]["operators"], + out[100.0].operator, + loaded_out[100.0].operator, ) diff --git a/benchmarks/ekobox/benchmark_gen_op.py b/benchmarks/ekobox/benchmark_gen_op.py index cacc832cd..bd3b613ed 100644 --- a/benchmarks/ekobox/benchmark_gen_op.py +++ b/benchmarks/ekobox/benchmark_gen_op.py @@ -8,15 +8,17 @@ def benchmark_gen_op_card(): op = g_o.gen_op_card([10, 100]) assert op["Q2grid"] == [10, 100] - assert op["interpolation_polynomial_degree"] == 4 + assert op["configs"]["interpolation_polynomial_degree"] == 4 up_err = {"Prova": "Prova"} with pytest.raises(ValueError): op = g_o.gen_op_card([10], update=up_err) - up = {"interpolation_polynomial_degree": 2, "interpolation_is_log": False} + up = { + "configs": {"interpolation_polynomial_degree": 2, "interpolation_is_log": False} + } op = g_o.gen_op_card([100], update=up) assert op["Q2grid"] == [100] - assert op["interpolation_polynomial_degree"] == 2 - assert op["interpolation_is_log"] is False + assert op["configs"]["interpolation_polynomial_degree"] == 2 + assert op["configs"]["interpolation_is_log"] is False @pytest.mark.isolated diff --git a/benchmarks/ekobox/benchmark_utils.py b/benchmarks/ekobox/benchmark_utils.py index d29986eb0..37a5661f0 100644 --- a/benchmarks/ekobox/benchmark_utils.py +++ b/benchmarks/ekobox/benchmark_utils.py @@ -31,20 +31,14 @@ def benchmark_ekos_product(): _ = utils.ekos_product(eko_ini, eko_fin_err) # product is copied eko_res = utils.ekos_product(eko_ini, eko_fin, in_place=False) + + assert eko_res.Q02 == eko_ini.Q02 + np.testing.assert_allclose(eko_res.Q2grid[1:], eko_fin.Q2grid) + np.testing.assert_allclose(eko_ini[80.0].operator, eko_res[80.0].operator) + # product overwrites initial eko_res2 = utils.ekos_product(eko_ini, eko_fin) - np.testing.assert_allclose( - eko_res["Q2grid"][80.0]["operators"], eko_res2["Q2grid"][80.0]["operators"] - ) - np.testing.assert_allclose(eko_res2["q2_ref"], eko_ini["q2_ref"]) - np.testing.assert_allclose( - list(eko_res2["Q2grid"].keys()), list(eko_fin["Q2grid"].keys()) - ) - np.testing.assert_allclose( - eko_ini["Q2grid"][80.0]["operators"], eko_res2["Q2grid"][80.0]["operators"] - ) - np.testing.assert_allclose(eko_res["q2_ref"], eko_ini["q2_ref"]) - np.testing.assert_allclose( - list(eko_res["Q2grid"].keys()), list(eko_fin["Q2grid"].keys()) - ) + assert eko_res2.Q02 == eko_ini.Q02 + np.testing.assert_allclose(eko_res2.Q2grid[1:], eko_fin.Q2grid) + np.testing.assert_allclose(eko_res[80.0].operator, eko_res2[80.0].operator) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 44d03836c..deefa7fd7 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -259,4 +259,8 @@ def load_tar(tarname): operator_grid[q2] = dict(zip(grids.keys(), slices)) metadata["Q2grid"] = operator_grid - return struct.EKO.from_dict(metadata) + eko = struct.EKO.from_dict(metadata) + for q2, op in metadata["Q2grid"].items(): + eko[q2] = op + + return eko diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index 28914b182..60c4ed24c 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -129,5 +129,5 @@ def gen_out(theory_card, op_card, path=None): if path is not None: ops_id = f"o{op_card['hash'][:6]}_t{theory_card['hash'][:6]}" path = f"{path}/{ops_id}.tar" - eko_output.dump_tar(path) + eko.output.legacy.dump_tar(eko_output, path) return eko_output diff --git a/src/ekobox/gen_op.py b/src/ekobox/gen_op.py index 5eba6ca35..6ce7d9034 100644 --- a/src/ekobox/gen_op.py +++ b/src/ekobox/gen_op.py @@ -37,7 +37,12 @@ def gen_op_card(Q2grid, update=None, name=None): for k in update.keys(): if k not in def_op.keys(): raise ValueError("Provided key not in operators card") - def_op.update(update) + for key, value in update.items(): + # properly update sections + if isinstance(value, dict): + def_op[key].update(value) + else: + def_op[key] = value serialized = sql.serialize(def_op) def_op["hash"] = (sql.add_hash(serialized))[-1] if name is not None: diff --git a/src/ekobox/utils.py b/src/ekobox/utils.py index e228d7cc4..a5b759252 100644 --- a/src/ekobox/utils.py +++ b/src/ekobox/utils.py @@ -3,10 +3,12 @@ import numpy as np +from eko.output import EKO + # TODO: add a control on the theory (but before we need to implement another # kind of output which includes the theory and operator runcards) -def ekos_product(eko_ini, eko_fin, in_place=True): +def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: """Returns the product of two ekos Parameters @@ -24,19 +26,20 @@ def ekos_product(eko_ini, eko_fin, in_place=True): eko operator """ - if eko_fin["q2_ref"] not in eko_ini["Q2grid"].keys(): + # TODO: check if it's close, instead of checking identity + if eko_fin.Q02 not in eko_ini.Q2grid: raise ValueError( "Initial Q2 of final eko operator does not match any final Q2 in" " the initial eko operator" ) - ope1 = eko_ini["Q2grid"][eko_fin["q2_ref"]]["operators"] - ope1_error = eko_ini["Q2grid"][eko_fin["q2_ref"]]["operator_errors"] + ope1 = eko_ini[eko_fin.Q02].operator + ope1_error = eko_ini[eko_fin.Q02].error ope2_dict = {} ope2_error_dict = {} - for q2, op in eko_fin["Q2grid"].items(): - ope2_dict[q2] = op["operators"] - ope2_error_dict[q2] = op["operator_errors"] + for q2, op in eko_fin.items(): + ope2_dict[q2] = op.operator + ope2_error_dict[q2] = op.error final_op_dict = {} final_op_error_dict = {} @@ -49,14 +52,16 @@ def ekos_product(eko_ini, eko_fin, in_place=True): ) + np.einsum("ajbk,bkcl -> ajcl", ope1_error, op2) final_dict[q2] = { - "operators": final_op_dict[q2], - "operator_errors": final_op_error_dict[q2], + "operator": final_op_dict[q2], + "error": final_op_error_dict[q2], } - final_eko = None if in_place is False: final_eko = copy.deepcopy(eko_ini) else: final_eko = eko_ini - final_eko["Q2grid"] = final_dict + + for q2, op in final_dict.items(): + final_eko[q2] = op + return final_eko From e36ccdb3dd0efe2c85f9728aa1b61c6f7301d149 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:56:07 +0200 Subject: [PATCH 020/148] Simplify ekobox product --- src/ekobox/utils.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/ekobox/utils.py b/src/ekobox/utils.py index a5b759252..504cbb329 100644 --- a/src/ekobox/utils.py +++ b/src/ekobox/utils.py @@ -32,8 +32,8 @@ def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: "Initial Q2 of final eko operator does not match any final Q2 in" " the initial eko operator" ) - ope1 = eko_ini[eko_fin.Q02].operator - ope1_error = eko_ini[eko_fin.Q02].error + ope1 = eko_ini[eko_fin.Q02].operator.copy() + ope1_error = eko_ini[eko_fin.Q02].error.copy() ope2_dict = {} ope2_error_dict = {} @@ -61,7 +61,14 @@ def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: else: final_eko = eko_ini - for q2, op in final_dict.items(): - final_eko[q2] = op + for q2, op2 in eko_fin.items(): + op = np.einsum("ajbk,bkcl -> ajcl", ope1, op2.operator) + + error = np.einsum("ajbk,bkcl -> ajcl", ope1, op2.error) + np.einsum( + "ajbk,bkcl -> ajcl", ope1_error, op2.operator + ) + + alphas = eko_fin[q2].alphas + final_eko[q2] = dict(operator=op, error=error, alphas=alphas) return final_eko From 9bf11608b399d13bd04587f387392a510d747ed4 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 18 Mar 2022 09:29:11 +0100 Subject: [PATCH 021/148] Upgrade product: do not recompute existing operators --- src/ekobox/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ekobox/utils.py b/src/ekobox/utils.py index 504cbb329..6538a59d1 100644 --- a/src/ekobox/utils.py +++ b/src/ekobox/utils.py @@ -62,6 +62,9 @@ def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: final_eko = eko_ini for q2, op2 in eko_fin.items(): + if q2 in eko_ini: + continue + op = np.einsum("ajbk,bkcl -> ajcl", ope1, op2.operator) error = np.einsum("ajbk,bkcl -> ajcl", ope1, op2.error) + np.einsum( From c72157c548bb04dd4ee8af7fa0c7e3c55b183d44 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:57:46 +0200 Subject: [PATCH 022/148] Introduce xgrid type --- src/eko/evolution_operator/__init__.py | 10 +--- src/eko/interpolation.py | 82 +++++++++++++++++--------- src/eko/output/struct.py | 5 +- src/eko/runner.py | 16 ++--- 4 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index a03a27fa1..4d2a17678 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -435,13 +435,9 @@ def run_op_integration( ) temp_dict[label] = res[:2] column.append(temp_dict) - logger.info( - "%s: computing operators - %u/%u took: %6f s", - self.log_label, - k + 1, - self.grid_size, - (time.perf_counter() - start_time), + f"{self.log_label}: computing operators: - {k+1}/{self.grid_size}" + f" took: {(time.perf_counter() - start_time):6f} s" ) return column @@ -499,7 +495,7 @@ def integrate( # run integration in parallel for each grid point # or avoid opening a single pool - args = (self.run_op_integration, enumerate(np.log(self.int_disp.xgrid_raw))) + args = (self.run_op_integration, enumerate(np.log(self.int_disp.xgrid.raw))) if self.n_pools == 1: res = map(*args) else: diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index 09f63f7dd..665a3bebe 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -414,6 +414,44 @@ def __call__(self, *args, **kwargs): return self.callable(*args, **kwargs) +class XGrid: + def __init__(self, xgrid: np.ndarray, log: bool = True): + ugrid = np.array(np.unique(xgrid), np.float_) + if len(xgrid) != len(ugrid): + raise ValueError(f"xgrid is not unique: {xgrid}") + if len(xgrid) < 2: + raise ValueError(f"xgrid needs at least 2 points, received {len(xgrid)}") + + self.log = log + + # henceforth ugrid might no longer be the input! + # which is ok, because for most of the code this is all we need to do + # to distinguish log and non-log + if log: + self._raw = ugrid + ugrid = np.log(ugrid) + + self.grid = ugrid + + def __len__(self) -> int: + return len(self.grid) + + def __eq__(self, other) -> bool: + """Checks equality""" + return all([len(self) == len(other), np.allclose(self.raw, other.raw)]) + + @property + def raw(self) -> np.ndarray: + return self.grid if not self.log else self._raw + + @property + def size(self) -> int: + return self.grid.size + + def tolist(self) -> list[float]: + return self.raw.tolist() + + class InterpolatorDispatcher: """ Setups the interpolator. @@ -426,7 +464,7 @@ class InterpolatorDispatcher: Parameters ---------- - xgrid_in : numpy.ndarray + xgrid : numpy.ndarray Grid in x-space from which the interpolators are constructed polynomial_degree : int degree of the interpolation polynomial @@ -437,30 +475,17 @@ class InterpolatorDispatcher: """ def __init__(self, xgrid, polynomial_degree, log=True, mode_N=True): + xgrid = XGrid(xgrid, log=log) # sanity checks - xgrid_size = len(xgrid) - ugrid = np.array(np.unique(xgrid), np.float_) - if xgrid_size != len(ugrid): - raise ValueError(f"xgrid is not unique: {xgrid}") - xgrid = ugrid - if xgrid_size < 2: - raise ValueError(f"xgrid needs at least 2 points, received {xgrid_size}") if polynomial_degree < 1: raise ValueError( f"need at least polynomial_degree 1, received {polynomial_degree}" ) - if xgrid_size <= polynomial_degree: + if len(xgrid) <= polynomial_degree: raise ValueError( f"to interpolate with degree {polynomial_degree} " " we need at least that much points + 1" ) - # keep a true copy of grid - self.xgrid_raw = xgrid - # henceforth xgrid might no longer be the input! - # which is ok, because for most of the code this is all we need to do - # to distinguish log and non-log - if log: - xgrid = np.log(xgrid) # Save the different variables self.xgrid = xgrid @@ -468,7 +493,7 @@ def __init__(self, xgrid, polynomial_degree, log=True, mode_N=True): self.log = log logger.info( "Interpolation: number of points = %d, polynomial degree = %d, logarithmic = %s", - xgrid_size, + len(xgrid), polynomial_degree, log, ) @@ -482,20 +507,20 @@ def __init__(self, xgrid, polynomial_degree, log=True, mode_N=True): if polynomial_degree % 2 == 0: po2 -= 1 # iterate areas: there is 1 less then number of points - for i in range(xgrid_size - 1): + for i in range(len(xgrid) - 1): kmin = max(0, i - po2) kmax = kmin + polynomial_degree - if kmax >= xgrid_size: - kmax = xgrid_size - 1 + if kmax >= len(xgrid): + kmax = len(xgrid) - 1 kmin = kmax - polynomial_degree b = (kmin, kmax) list_of_blocks.append(b) # Generate the basis functions basis_functions = [] - for i in range(xgrid_size): + for i in range(len(xgrid)): new_basis = BasisFunction( - xgrid, i, list_of_blocks, mode_log=log, mode_N=mode_N + xgrid.grid, i, list_of_blocks, mode_log=log, mode_N=mode_N ) basis_functions.append(new_basis) self.basis = basis_functions @@ -546,12 +571,11 @@ def from_dict(cls, operators_card, mode_N=True): def __eq__(self, other): """Checks equality""" checks = [ - len(self.xgrid_raw) == len(other.xgrid_raw), self.log == other.log, self.polynomial_degree == other.polynomial_degree, + self.xgrid == other.xgrid, ] - # check elements after shape - return all(checks) and np.allclose(self.xgrid_raw, other.xgrid_raw) + return all(checks) def __iter__(self): # return iter(self.basis) @@ -581,10 +605,10 @@ def get_interpolation(self, targetgrid): """ # trivial? - if len(targetgrid) == len(self.xgrid_raw) and np.allclose( - targetgrid, self.xgrid_raw + if len(targetgrid) == len(self.xgrid) and np.allclose( + targetgrid, self.xgrid.raw ): - return np.eye(len(self.xgrid_raw)) + return np.eye(len(self.xgrid)) # compute map out = [] for x in targetgrid: @@ -607,7 +631,7 @@ def to_dict(self): full grid configuration """ ret = { - "xgrid": self.xgrid_raw, + "xgrid": self.xgrid.tolist(), "configs": { "interpolation_polynomial_degree": self.polynomial_degree, "interpolation_is_log": self.log, diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 5717e54b0..021344763 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -4,6 +4,7 @@ import numpy as np +from .. import interpolation from .. import version as vmod @@ -71,7 +72,7 @@ class EKO: to PDFs and dumping to file. """ - xgrid: np.ndarray + xgrid: interpolation.XGrid Q02: float _operators: Dict[float, Optional[Operator]] configs: Configs @@ -108,7 +109,7 @@ def Q2grid(self): @classmethod def from_dict(cls, dictionary): return cls( - xgrid=np.array(dictionary["xgrid"]), + xgrid=interpolation.XGrid(dictionary["xgrid"]), Q02=dictionary["Q0"] ** 2, _operators={q2: None for q2 in dictionary["Q2grid"]}, configs=Configs.from_dict(dictionary["configs"]), diff --git a/src/eko/runner.py b/src/eko/runner.py index 1cd76e87a..7947eebea 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -69,16 +69,16 @@ def __init__(self, theory_card, operators_card): rot = operators_card.get("rotations", {}) self.post_process = dict( - inputgrid=rot.get("inputgrid", bfd.xgrid_raw), - targetgrid=rot.get("targetgrid", bfd.xgrid_raw), + inputgrid=rot.get("inputgrid", bfd.xgrid.raw), + targetgrid=rot.get("targetgrid", bfd.xgrid.raw), inputbasis=rot.get("inputbasis"), targetbasis=rot.get("targetbasis"), ) if "rotations" not in operators_card: operators_card["rotations"] = {} - operators_card["rotations"]["inputgrid"] = bfd.xgrid_raw - operators_card["rotations"]["targetgrid"] = bfd.xgrid_raw + operators_card["rotations"]["inputgrid"] = bfd.xgrid.raw + operators_card["rotations"]["targetgrid"] = bfd.xgrid.raw self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref), **operators_card)) @@ -94,20 +94,20 @@ def get_output(self): # add all operators self.out.rotations.inputpids = np.array(br.flavor_basis_pids) self.out.rotations.targetpids = np.array(br.flavor_basis_pids) - self.out.rotations.inputgrid = self.out.xgrid - self.out.rotations.targetgrid = self.out.xgrid + self.out.rotations.inputgrid = self.out.xgrid.grid + self.out.rotations.targetgrid = self.out.xgrid.grid for final_scale, op in self.op_grid.compute().items(): self.out[float(final_scale)] = op # reshape xgrid inputgrid = ( self.post_process["inputgrid"] - if self.post_process["inputgrid"] is not self.out.xgrid + if self.post_process["inputgrid"] is not self.out.xgrid.grid else None ) targetgrid = ( self.post_process["targetgrid"] - if self.post_process["targetgrid"] is not self.out.xgrid + if self.post_process["targetgrid"] is not self.out.xgrid.grid else None ) if inputgrid is not None or targetgrid is not None: From a2379713a56bd08c4ce1c17f7245ca96123924fd Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 12:58:56 +0200 Subject: [PATCH 023/148] Recover tests involving xgrid --- src/eko/interpolation.py | 3 ++- tests/eko/test_ev_operator.py | 2 +- tests/eko/test_interpolation.py | 4 ++-- tests/eko/test_output.py | 14 +++++++------- tests/eko/test_runner.py | 9 +++++---- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index 665a3bebe..f00909b70 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -438,7 +438,8 @@ def __len__(self) -> int: def __eq__(self, other) -> bool: """Checks equality""" - return all([len(self) == len(other), np.allclose(self.raw, other.raw)]) + # check shape before comparing values + return len(self) == len(other) and np.allclose(self.raw, other.raw) @property def raw(self) -> np.ndarray: diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 795712c6c..5fe5b3b68 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -337,7 +337,7 @@ def quad_ker_pegasus( mode0 = br.non_singlet_pids_map["ns+"] mode1 = 0 method = "" - logxs = np.log(int_disp.xgrid_raw) + logxs = np.log(int_disp.xgrid.raw) a1 = 1 a0 = 2 nf = 3 diff --git a/tests/eko/test_interpolation.py b/tests/eko/test_interpolation.py index 91e4905ad..3c377300f 100644 --- a/tests/eko/test_interpolation.py +++ b/tests/eko/test_interpolation.py @@ -24,7 +24,7 @@ def check_is_interpolator(interpolator): assert_almost_equal(one, 1.0) # polynoms need to be "orthogonal" at grid points - for j, (basis_j, xj) in enumerate(zip(interpolator, interpolator.xgrid_raw)): + for j, (basis_j, xj) in enumerate(zip(interpolator, interpolator.xgrid.raw)): one = basis_j(xj) assert_almost_equal( one, @@ -110,7 +110,7 @@ def test_from_dict(self): } a = interpolation.InterpolatorDispatcher.from_dict(d) np.testing.assert_array_almost_equal( - a.xgrid, np.array([1e-7, 1e-4, 1e-1, 0.55, 1.0]) + a.xgrid.grid, np.array([1e-7, 1e-4, 1e-1, 0.55, 1.0]) ) assert a.polynomial_degree == 1 dd = { diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index f058551cb..cad0993ef 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -41,8 +41,8 @@ def test_io(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid, fake_output["xgrid"]) - np.testing.assert_almost_equal(o2.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o1.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o2.xgrid.raw, fake_output["xgrid"]) # fake output files m_out = mock.mock_open(read_data="") with mock.patch("builtins.open", m_out) as mock_file: @@ -56,14 +56,14 @@ def test_io(self, fake_output): fn = "test.yaml" o3 = legacy.load_yaml_from_file(fn) mock_file.assert_called_with(fn, encoding="utf-8") - np.testing.assert_almost_equal(o3.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o3.xgrid.raw, fake_output["xgrid"]) # repeat for tar fn = "test.tar" with tempfile.TemporaryDirectory() as folder: fp = pathlib.Path(folder) / fn legacy.dump_tar(o1, fp) o4 = legacy.load_tar(fp) - np.testing.assert_almost_equal(o4.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o4.xgrid.raw, fake_output["xgrid"]) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): legacy.dump_tar(o1, fn) @@ -85,7 +85,7 @@ def test_rename_issue81(self, fake_output): shutil.move(fp1, fp2) # reload o4 = legacy.load_tar(fp2) - np.testing.assert_almost_equal(o4.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o4.xgrid.raw, fake_output["xgrid"]) def test_io_bin(self, fake_output): # create object @@ -98,8 +98,8 @@ def test_io_bin(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid, fake_output["xgrid"]) - np.testing.assert_almost_equal(o2.xgrid, fake_output["xgrid"]) + np.testing.assert_almost_equal(o1.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o2.xgrid.raw, fake_output["xgrid"]) class TestManipulate: diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 23ada38f6..c4e54c22f 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -4,6 +4,7 @@ import numpy as np import eko +import eko.interpolation from eko import compatibility theory_card = { @@ -67,7 +68,7 @@ def test_targetgrid(): oc["rotations"] = dict(targetgrid=tgrid) r = eko.runner.Runner(tc, oc) o = r.get_output() - check_shapes(o, tgrid, o.xgrid, tc, oc) + check_shapes(o, eko.interpolation.XGrid(np.array(tgrid)), o.xgrid, tc, oc) def test_targetbasis(): @@ -87,9 +88,9 @@ def check_shapes(o, txs, ixs, theory_card, operators_card): op_shape = (tpids, len(txs), ipids, len(ixs)) # check output = input - np.testing.assert_allclose(o.xgrid, operators_card["xgrid"]) - np.testing.assert_allclose(o.rotations.targetgrid, txs) - np.testing.assert_allclose(o.rotations.inputgrid, ixs) + np.testing.assert_allclose(o.xgrid.raw, operators_card["xgrid"]) + np.testing.assert_allclose(o.rotations.targetgrid, txs.raw) + np.testing.assert_allclose(o.rotations.inputgrid, ixs.raw) for k in ["interpolation_polynomial_degree", "interpolation_is_log"]: assert getattr(o.configs, k) == operators_card["configs"][k] np.testing.assert_allclose(o.Q02, theory_card["Q0"] ** 2) From 6b4b37d7690b2a22cf15ec7705e2b7762de18cea Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 18 Mar 2022 11:28:20 +0100 Subject: [PATCH 024/148] Remove bleeding edge syntax --- src/eko/interpolation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index f00909b70..afe56a458 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -449,7 +449,7 @@ def raw(self) -> np.ndarray: def size(self) -> int: return self.grid.size - def tolist(self) -> list[float]: + def tolist(self) -> list: return self.raw.tolist() From cd557a19576ffadc386f74dcc3cc12ae5f81da81 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 25 Mar 2022 20:14:15 +0100 Subject: [PATCH 025/148] Add runcards classes --- src/eko/runcards.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/eko/runcards.py diff --git a/src/eko/runcards.py b/src/eko/runcards.py new file mode 100644 index 000000000..e93fecc89 --- /dev/null +++ b/src/eko/runcards.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from dataclasses import dataclass + + +@dataclass +class TheoryCard: + pto: int + + +@dataclass +class OperatorCard: + xgrid: list[float] From 148fcf0404b11fcd862404b3e3bf20158e3f1a30 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 13:01:23 +0200 Subject: [PATCH 026/148] Reshuffle files --- benchmarks/ekobox/benchmark_evol_pdf.py | 21 +---- benchmarks/ekobox/genpdf/benchmark_flavors.py | 88 ------------------- benchmarks/ekobox/genpdf/benchmark_init.py | 26 ------ tests/ekobox/test_evol_pdf.py | 25 ++++++ .../ekobox/test_gen_info.py | 2 - .../ekobox/test_gen_op.py | 2 - .../ekobox/test_gen_theory.py | 2 - tests/ekobox/test_genpdf.py | 30 +++++++ tests/ekobox/test_genpdf_flavors.py | 87 ++++++++++++++++++ .../ekobox/test_utils.py | 3 +- 10 files changed, 144 insertions(+), 142 deletions(-) rename benchmarks/ekobox/benchmark_gen_info.py => tests/ekobox/test_gen_info.py (95%) rename benchmarks/ekobox/benchmark_gen_op.py => tests/ekobox/test_gen_op.py (95%) rename benchmarks/ekobox/benchmark_gen_theory.py => tests/ekobox/test_gen_theory.py (95%) create mode 100644 tests/ekobox/test_genpdf.py create mode 100644 tests/ekobox/test_genpdf_flavors.py rename benchmarks/ekobox/benchmark_utils.py => tests/ekobox/test_utils.py (96%) diff --git a/benchmarks/ekobox/benchmark_evol_pdf.py b/benchmarks/ekobox/benchmark_evol_pdf.py index 464416c38..a3a4b16b7 100644 --- a/benchmarks/ekobox/benchmark_evol_pdf.py +++ b/benchmarks/ekobox/benchmark_evol_pdf.py @@ -1,11 +1,9 @@ # -*- coding: utf-8 -*- import pathlib -import lhapdf import numpy as np import pytest -import eko.output.legacy from eko import basis_rotation as br from ekobox import evol_pdf as ev_p from ekobox import gen_op as g_o @@ -13,6 +11,7 @@ from ekobox.genpdf import load test_pdf = pathlib.Path(__file__).parent / "fakepdf" +lhapdf = pytest.importorskip("lhapdf") @pytest.mark.isolated @@ -100,21 +99,3 @@ def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): for x in [1e-7, 0.01, 0.1, 0.2, 0.3]: for pid in [21, 1, 2]: assert new_pdf_1.xfxQ2(pid, x, Q2) != new_pdf_2.xfxQ2(pid, x, Q2) - - -@pytest.mark.isolated -def benchmark_gen_and_dump_out(tmp_path): - op = g_o.gen_op_card([100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) - theory = g_t.gen_theory_card(0, 1.0) - - out = ev_p.gen_out(theory, op, path=tmp_path) - - ops_id = f"o{op['hash'][:6]}_t{theory['hash'][:6]}" - outpath = f"{tmp_path}/{ops_id}.tar" - loaded_out = eko.output.legacy.load_tar(outpath) - assert list(out.xgrid) == list(loaded_out.xgrid) - for el, load_el in zip(out[100.0].operator, loaded_out[100.0].operator): - np.testing.assert_allclose( - out[100.0].operator, - loaded_out[100.0].operator, - ) diff --git a/benchmarks/ekobox/genpdf/benchmark_flavors.py b/benchmarks/ekobox/genpdf/benchmark_flavors.py index 010dc7408..9dc6913f5 100644 --- a/benchmarks/ekobox/genpdf/benchmark_flavors.py +++ b/benchmarks/ekobox/genpdf/benchmark_flavors.py @@ -13,38 +13,6 @@ lhapdf = pytest.importorskip("lhapdf") -@pytest.mark.isolated -def benchmark_is_evolution(): - assert genpdf.flavors.is_evolution_labels(["V", "T3"]) - assert not genpdf.flavors.is_evolution_labels(["21", "2"]) - - -@pytest.mark.isolated -def benchmark_is_pids(): - assert not genpdf.flavors.is_pid_labels(["V", "T3"]) - assert not genpdf.flavors.is_pid_labels(["35", "9"]) - assert not genpdf.flavors.is_pid_labels({}) - assert genpdf.flavors.is_pid_labels([21, 2]) - - -@pytest.mark.isolated -def benchmark_flavors_pid_to_flavor(): - flavs = genpdf.flavors.pid_to_flavor([1, 2, 21, -3]) - for f in flavs: - for g in flavs: - if not np.allclose(f, g): - assert f @ g == 0 - - -@pytest.mark.isolated -def benchmark_flavors_evol_to_flavor(): - flavs = genpdf.flavors.evol_to_flavor(["S", "g", "T3", "V8"]) - for f in flavs: - for g in flavs: - if not np.allclose(f, g): - assert f @ g == 0 - - @pytest.mark.isolated def benchmark_flavors_pids_ct14(tmp_path, cd): with cd(tmp_path): @@ -101,59 +69,3 @@ def benchmark_flavors_evol_ct14(tmp_path, cd): np.testing.assert_allclose( pdf.xfxQ2(21, x, Q2), gonly.xfxQ2(21, x, Q2) ) - - -@pytest.mark.isolated -def benchmark_flavors_evol_raw(): - blocks = [ - { - "Q2grid": np.array([1, 2]), - "xgrid": np.array([0.1, 1.0]), - "pids": np.array([-1, 21, 1]), - "data": np.array([[0.1, 0.2, 0.1]] * 4), - } - ] - gonly = genpdf.flavors.project(blocks, genpdf.flavors.evol_to_flavor(["g"])) - assert len(gonly) == 1 - np.testing.assert_allclose( - gonly[0]["data"], - np.array( - [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] * 4 - ), - ) - Sonly = genpdf.flavors.project(blocks, genpdf.flavors.evol_to_flavor(["S"])) - assert len(Sonly) == 1 - for i in [0, 1, 2, 3]: - # g and gamma are zero - np.testing.assert_allclose(Sonly[0]["data"][i][7], 0) - np.testing.assert_allclose(Sonly[0]["data"][i][0], 0) - # quark are all equal and equal to anti-quarks - for pid in [2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13]: - np.testing.assert_allclose(Sonly[0]["data"][i][pid], Sonly[0]["data"][i][1]) - - -@pytest.mark.isolated -def benchmark_flavors_evol_nodata(): - # try with a block without data - blocks = [ - { - "Q2grid": np.array([1, 2]), - "xgrid": np.array([0.1, 1.0]), - "pids": np.array([-1, 21, 1]), - "data": np.array([]), - }, - { - "Q2grid": np.array([1, 2]), - "xgrid": np.array([0.1, 1.0]), - "pids": np.array([-1, 21, 1]), - "data": np.array([[0.1, 0.2, 0.1]] * 4), - }, - ] - gonly = genpdf.flavors.project(blocks, genpdf.flavors.evol_to_flavor(["g"])) - assert len(gonly) == 2 - np.testing.assert_allclose( - gonly[1]["data"], - np.array( - [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] * 4 - ), - ) diff --git a/benchmarks/ekobox/genpdf/benchmark_init.py b/benchmarks/ekobox/genpdf/benchmark_init.py index 3e23e8006..29a9b0b05 100644 --- a/benchmarks/ekobox/genpdf/benchmark_init.py +++ b/benchmarks/ekobox/genpdf/benchmark_init.py @@ -14,32 +14,6 @@ lhapdf = pytest.importorskip("lhapdf") -@pytest.mark.isolated -def benchmark_genpdf_exceptions(tmp_path, cd): - # using a wrong label and then a wrong parent pdf - with cd(tmp_path): - with pytest.raises(TypeError): - genpdf.generate_pdf( - "test_genpdf_exceptions1", - ["f"], - { - 21: lambda x, Q2: 3.0 * x * (1.0 - x), - 2: lambda x, Q2: 4.0 * x * (1.0 - x), - }, - ) - with pytest.raises(ValueError): - genpdf.generate_pdf( - "test_genpdf_exceptions2", - ["g"], - 10, - ) - with pytest.raises(FileExistsError): - genpdf.install_pdf("foo") - - with pytest.raises(TypeError): - genpdf.generate_pdf("debug", [21], info_update=(10, 15, 20)) - - @pytest.mark.isolated def benchmark_genpdf_no_parent_and_install(tmp_path, cd): with cd(tmp_path): diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index e69de29bb..bf1f9b0ce 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +import numpy as np + +import eko.output.legacy +from ekobox import evol_pdf as ev_p +from ekobox import gen_op as g_o +from ekobox import gen_theory as g_t + + +def benchmark_gen_and_dump_out(tmp_path): + op = g_o.gen_op_card([100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) + theory = g_t.gen_theory_card(0, 1.0) + + out = ev_p.gen_out(theory, op, path=tmp_path) + + ops_id = f"o{op['hash'][:6]}_t{theory['hash'][:6]}" + outpath = f"{tmp_path}/{ops_id}.tar" + loaded_out = eko.output.legacy.load_tar(outpath) + assert list(out.xgrid) == list(loaded_out.xgrid) + for el, load_el in zip(out[100.0].operator, loaded_out[100.0].operator): + np.testing.assert_allclose( + out[100.0].operator, + loaded_out[100.0].operator, + ) diff --git a/benchmarks/ekobox/benchmark_gen_info.py b/tests/ekobox/test_gen_info.py similarity index 95% rename from benchmarks/ekobox/benchmark_gen_info.py rename to tests/ekobox/test_gen_info.py index 7eb10d3d3..554fb7872 100644 --- a/benchmarks/ekobox/benchmark_gen_info.py +++ b/tests/ekobox/test_gen_info.py @@ -2,14 +2,12 @@ import math import numpy as np -import pytest from ekobox import gen_info as g_i from ekobox import gen_op as g_o from ekobox import gen_theory as g_t -@pytest.mark.isolated def benchmark_create_info_file(): op = g_o.gen_op_card([10, 100]) theory = g_t.gen_theory_card(1, 10.0, update={"alphas": 0.2}) diff --git a/benchmarks/ekobox/benchmark_gen_op.py b/tests/ekobox/test_gen_op.py similarity index 95% rename from benchmarks/ekobox/benchmark_gen_op.py rename to tests/ekobox/test_gen_op.py index bd3b613ed..05fbbc389 100644 --- a/benchmarks/ekobox/benchmark_gen_op.py +++ b/tests/ekobox/test_gen_op.py @@ -4,7 +4,6 @@ from ekobox import gen_op as g_o -@pytest.mark.isolated def benchmark_gen_op_card(): op = g_o.gen_op_card([10, 100]) assert op["Q2grid"] == [10, 100] @@ -21,7 +20,6 @@ def benchmark_gen_op_card(): assert op["configs"]["interpolation_is_log"] is False -@pytest.mark.isolated def benchmark_export_load_op_card(tmp_path, cd): with cd(tmp_path): op = g_o.gen_op_card([100], name="debug_op") diff --git a/benchmarks/ekobox/benchmark_gen_theory.py b/tests/ekobox/test_gen_theory.py similarity index 95% rename from benchmarks/ekobox/benchmark_gen_theory.py rename to tests/ekobox/test_gen_theory.py index 07320fa5e..c353403eb 100644 --- a/benchmarks/ekobox/benchmark_gen_theory.py +++ b/tests/ekobox/test_gen_theory.py @@ -4,7 +4,6 @@ from ekobox import gen_theory as g_t -@pytest.mark.isolated def benchmark_gen_theory_card(): theory = g_t.gen_theory_card(0, 1.0) assert theory["PTO"] == 0 @@ -19,7 +18,6 @@ def benchmark_gen_theory_card(): assert theory["mb"] == 132.3 -@pytest.mark.isolated def benchmark_export_load_theory_card(tmp_path, cd): with cd(tmp_path): theory = g_t.gen_theory_card(2, 12.3, name="debug_theory") diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py new file mode 100644 index 000000000..f4881e9fc --- /dev/null +++ b/tests/ekobox/test_genpdf.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +import pytest + +from ekobox import genpdf + + +def benchmark_genpdf_exceptions(tmp_path, cd): + # using a wrong label and then a wrong parent pdf + with cd(tmp_path): + with pytest.raises(TypeError): + genpdf.generate_pdf( + "test_genpdf_exceptions1", + ["f"], + { + 21: lambda x, Q2: 3.0 * x * (1.0 - x), + 2: lambda x, Q2: 4.0 * x * (1.0 - x), + }, + ) + with pytest.raises(ValueError): + genpdf.generate_pdf( + "test_genpdf_exceptions2", + ["g"], + 10, + ) + with pytest.raises(FileExistsError): + genpdf.install_pdf("foo") + + with pytest.raises(TypeError): + genpdf.generate_pdf("debug", [21], info_update=(10, 15, 20)) diff --git a/tests/ekobox/test_genpdf_flavors.py b/tests/ekobox/test_genpdf_flavors.py new file mode 100644 index 000000000..adddce9fa --- /dev/null +++ b/tests/ekobox/test_genpdf_flavors.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +import numpy as np + +from ekobox import genpdf + + +def benchmark_is_evolution(): + assert genpdf.flavors.is_evolution_labels(["V", "T3"]) + assert not genpdf.flavors.is_evolution_labels(["21", "2"]) + + +def benchmark_is_pids(): + assert not genpdf.flavors.is_pid_labels(["V", "T3"]) + assert not genpdf.flavors.is_pid_labels(["35", "9"]) + assert not genpdf.flavors.is_pid_labels({}) + assert genpdf.flavors.is_pid_labels([21, 2]) + + +def benchmark_flavors_pid_to_flavor(): + flavs = genpdf.flavors.pid_to_flavor([1, 2, 21, -3]) + for f in flavs: + for g in flavs: + if not np.allclose(f, g): + assert f @ g == 0 + + +def benchmark_flavors_evol_to_flavor(): + flavs = genpdf.flavors.evol_to_flavor(["S", "g", "T3", "V8"]) + for f in flavs: + for g in flavs: + if not np.allclose(f, g): + assert f @ g == 0 + + +def benchmark_flavors_evol_raw(): + blocks = [ + { + "Q2grid": np.array([1, 2]), + "xgrid": np.array([0.1, 1.0]), + "pids": np.array([-1, 21, 1]), + "data": np.array([[0.1, 0.2, 0.1]] * 4), + } + ] + gonly = genpdf.flavors.project(blocks, genpdf.flavors.evol_to_flavor(["g"])) + assert len(gonly) == 1 + np.testing.assert_allclose( + gonly[0]["data"], + np.array( + [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] * 4 + ), + ) + Sonly = genpdf.flavors.project(blocks, genpdf.flavors.evol_to_flavor(["S"])) + assert len(Sonly) == 1 + for i in [0, 1, 2, 3]: + # g and gamma are zero + np.testing.assert_allclose(Sonly[0]["data"][i][7], 0) + np.testing.assert_allclose(Sonly[0]["data"][i][0], 0) + # quark are all equal and equal to anti-quarks + for pid in [2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13]: + np.testing.assert_allclose(Sonly[0]["data"][i][pid], Sonly[0]["data"][i][1]) + + +def benchmark_flavors_evol_nodata(): + # try with a block without data + blocks = [ + { + "Q2grid": np.array([1, 2]), + "xgrid": np.array([0.1, 1.0]), + "pids": np.array([-1, 21, 1]), + "data": np.array([]), + }, + { + "Q2grid": np.array([1, 2]), + "xgrid": np.array([0.1, 1.0]), + "pids": np.array([-1, 21, 1]), + "data": np.array([[0.1, 0.2, 0.1]] * 4), + }, + ] + gonly = genpdf.flavors.project(blocks, genpdf.flavors.evol_to_flavor(["g"])) + assert len(gonly) == 2 + np.testing.assert_allclose( + gonly[1]["data"], + np.array( + [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] * 4 + ), + ) diff --git a/benchmarks/ekobox/benchmark_utils.py b/tests/ekobox/test_utils.py similarity index 96% rename from benchmarks/ekobox/benchmark_utils.py rename to tests/ekobox/test_utils.py index 37a5661f0..69fd86d05 100644 --- a/benchmarks/ekobox/benchmark_utils.py +++ b/tests/ekobox/test_utils.py @@ -8,8 +8,7 @@ from ekobox import utils -@pytest.mark.isolated -def benchmark_ekos_product(): +def test_ekos_product(): # Generating two ekos op1 = g_o.gen_op_card( [60.0, 80.0, 100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} From 442f3d796fdbf3749890c736a14f64c2af349bd3 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 21 Mar 2022 11:54:07 +0100 Subject: [PATCH 027/148] Fix tests to run --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 732eba80d..f6a03d93d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,8 @@ markers = ["isolated: marks benchmarks as isolated"] # extensions not to check extension-pkg-whitelist = ["numpy", "numba", "lhapdf", "pegasus"] ignore-paths = ["benchmarks/", "doc/", "tests/"] -jobs = 1 # has to be 1 as pylint is NOT threadsafe +# has to be 1 as pylint is NOT threadsafe +jobs = 1 [tool.pylint.messages_control] disable = ["invalid-name", "fixme"] [tool.pylint.reports] From 039a055086cc5e1776c74a0072c493bf6febf054 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 21 Mar 2022 11:54:07 +0100 Subject: [PATCH 028/148] Fix tests to run --- pyproject.toml | 2 +- tests/conftest.py | 19 +++++++++++++++++++ tests/ekobox/test_evol_pdf.py | 10 +++++----- tests/ekobox/test_gen_info.py | 2 +- tests/ekobox/test_gen_op.py | 4 ++-- tests/ekobox/test_gen_theory.py | 4 ++-- tests/ekobox/test_genpdf.py | 2 +- tests/ekobox/test_genpdf_flavors.py | 12 ++++++------ 8 files changed, 37 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f6a03d93d..4811a1c66 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,7 @@ markers = ["isolated: marks benchmarks as isolated"] # extensions not to check extension-pkg-whitelist = ["numpy", "numba", "lhapdf", "pegasus"] ignore-paths = ["benchmarks/", "doc/", "tests/"] -# has to be 1 as pylint is NOT threadsafe +# jobs has to be 1 as pylint is NOT threadsafe jobs = 1 [tool.pylint.messages_control] disable = ["invalid-name", "fixme"] diff --git a/tests/conftest.py b/tests/conftest.py index 35a8a9213..5d7b8b435 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,27 @@ # -*- coding: utf-8 -*- +import os +from contextlib import contextmanager + import numpy as np import pytest +@pytest.fixture +def cd(): + # thanks https://stackoverflow.com/questions/431684/ + # how-do-i-change-the-working-directory-in-python/24176022#24176022 + @contextmanager + def wrapped(newdir): + prevdir = os.getcwd() + os.chdir(os.path.expanduser(newdir)) + try: + yield + finally: + os.chdir(prevdir) + + return wrapped + + class FakePDF: def hasFlavor(self, pid): return pid == 1 diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index bf1f9b0ce..dabb8616a 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -8,7 +8,7 @@ from ekobox import gen_theory as g_t -def benchmark_gen_and_dump_out(tmp_path): +def test_gen_and_dump_out(tmp_path): op = g_o.gen_op_card([100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) theory = g_t.gen_theory_card(0, 1.0) @@ -17,9 +17,9 @@ def benchmark_gen_and_dump_out(tmp_path): ops_id = f"o{op['hash'][:6]}_t{theory['hash'][:6]}" outpath = f"{tmp_path}/{ops_id}.tar" loaded_out = eko.output.legacy.load_tar(outpath) - assert list(out.xgrid) == list(loaded_out.xgrid) - for el, load_el in zip(out[100.0].operator, loaded_out[100.0].operator): + assert out.xgrid.tolist() == loaded_out.xgrid.tolist() + for el, loaded_el in zip(out[100.0].operator, loaded_out[100.0].operator): np.testing.assert_allclose( - out[100.0].operator, - loaded_out[100.0].operator, + el, + loaded_el, ) diff --git a/tests/ekobox/test_gen_info.py b/tests/ekobox/test_gen_info.py index 554fb7872..f82e76c76 100644 --- a/tests/ekobox/test_gen_info.py +++ b/tests/ekobox/test_gen_info.py @@ -8,7 +8,7 @@ from ekobox import gen_theory as g_t -def benchmark_create_info_file(): +def test_create_info_file(): op = g_o.gen_op_card([10, 100]) theory = g_t.gen_theory_card(1, 10.0, update={"alphas": 0.2}) info = g_i.create_info_file( diff --git a/tests/ekobox/test_gen_op.py b/tests/ekobox/test_gen_op.py index 05fbbc389..bc0821d26 100644 --- a/tests/ekobox/test_gen_op.py +++ b/tests/ekobox/test_gen_op.py @@ -4,7 +4,7 @@ from ekobox import gen_op as g_o -def benchmark_gen_op_card(): +def test_gen_op_card(): op = g_o.gen_op_card([10, 100]) assert op["Q2grid"] == [10, 100] assert op["configs"]["interpolation_polynomial_degree"] == 4 @@ -20,7 +20,7 @@ def benchmark_gen_op_card(): assert op["configs"]["interpolation_is_log"] is False -def benchmark_export_load_op_card(tmp_path, cd): +def test_export_load_op_card(tmp_path, cd): with cd(tmp_path): op = g_o.gen_op_card([100], name="debug_op") g_o.export_op_card("debug_op_two", op) diff --git a/tests/ekobox/test_gen_theory.py b/tests/ekobox/test_gen_theory.py index c353403eb..468b3c401 100644 --- a/tests/ekobox/test_gen_theory.py +++ b/tests/ekobox/test_gen_theory.py @@ -4,7 +4,7 @@ from ekobox import gen_theory as g_t -def benchmark_gen_theory_card(): +def test_gen_theory_card(): theory = g_t.gen_theory_card(0, 1.0) assert theory["PTO"] == 0 assert theory["Q0"] == 1.0 @@ -18,7 +18,7 @@ def benchmark_gen_theory_card(): assert theory["mb"] == 132.3 -def benchmark_export_load_theory_card(tmp_path, cd): +def test_export_load_theory_card(tmp_path, cd): with cd(tmp_path): theory = g_t.gen_theory_card(2, 12.3, name="debug_theory") g_t.export_theory_card("debug_theory_two", theory) diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index f4881e9fc..55837a928 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -5,7 +5,7 @@ from ekobox import genpdf -def benchmark_genpdf_exceptions(tmp_path, cd): +def test_genpdf_exceptions(tmp_path, cd): # using a wrong label and then a wrong parent pdf with cd(tmp_path): with pytest.raises(TypeError): diff --git a/tests/ekobox/test_genpdf_flavors.py b/tests/ekobox/test_genpdf_flavors.py index adddce9fa..f6a767019 100644 --- a/tests/ekobox/test_genpdf_flavors.py +++ b/tests/ekobox/test_genpdf_flavors.py @@ -5,19 +5,19 @@ from ekobox import genpdf -def benchmark_is_evolution(): +def test_is_evolution(): assert genpdf.flavors.is_evolution_labels(["V", "T3"]) assert not genpdf.flavors.is_evolution_labels(["21", "2"]) -def benchmark_is_pids(): +def test_is_pids(): assert not genpdf.flavors.is_pid_labels(["V", "T3"]) assert not genpdf.flavors.is_pid_labels(["35", "9"]) assert not genpdf.flavors.is_pid_labels({}) assert genpdf.flavors.is_pid_labels([21, 2]) -def benchmark_flavors_pid_to_flavor(): +def test_flavors_pid_to_flavor(): flavs = genpdf.flavors.pid_to_flavor([1, 2, 21, -3]) for f in flavs: for g in flavs: @@ -25,7 +25,7 @@ def benchmark_flavors_pid_to_flavor(): assert f @ g == 0 -def benchmark_flavors_evol_to_flavor(): +def test_flavors_evol_to_flavor(): flavs = genpdf.flavors.evol_to_flavor(["S", "g", "T3", "V8"]) for f in flavs: for g in flavs: @@ -33,7 +33,7 @@ def benchmark_flavors_evol_to_flavor(): assert f @ g == 0 -def benchmark_flavors_evol_raw(): +def test_flavors_evol_raw(): blocks = [ { "Q2grid": np.array([1, 2]), @@ -61,7 +61,7 @@ def benchmark_flavors_evol_raw(): np.testing.assert_allclose(Sonly[0]["data"][i][pid], Sonly[0]["data"][i][1]) -def benchmark_flavors_evol_nodata(): +def test_flavors_evol_nodata(): # try with a block without data blocks = [ { From 579dffce786f2a164b877fab39347fb0dc537ae9 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 21 Mar 2022 12:16:07 +0100 Subject: [PATCH 029/148] Remove unreachable code, improve tests --- src/ekobox/genpdf/flavors.py | 2 -- tests/ekobox/test_genpdf_cli.py | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/ekobox/test_genpdf_cli.py diff --git a/src/ekobox/genpdf/flavors.py b/src/ekobox/genpdf/flavors.py index 116443419..b892deb99 100644 --- a/src/ekobox/genpdf/flavors.py +++ b/src/ekobox/genpdf/flavors.py @@ -129,8 +129,6 @@ def is_pid_labels(labels): except (ValueError, TypeError): return False for label in labels: - if not isinstance(label, np.int_): - return False if label not in br.flavor_basis_pids: return False return True diff --git a/tests/ekobox/test_genpdf_cli.py b/tests/ekobox/test_genpdf_cli.py new file mode 100644 index 000000000..25850c1d4 --- /dev/null +++ b/tests/ekobox/test_genpdf_cli.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +from click.testing import CliRunner + +from ekobox.genpdf.cli import cli + + +def test_genpdf_CLI_messages(): + runner = CliRunner() + result = runner.invoke(cli, ["generate"]) + assert "Error: Missing argument 'NAME'." in result.output + result = runner.invoke(cli, ["install"]) + assert "Error: Missing argument 'NAME'." in result.output + result = runner.invoke(cli, ["generate", "TestEmptyLabels"]) + assert result.exception and "Labels must contain at least one element" in str( + result.exception + ) + result = runner.invoke(cli, ["generate", "--help"]) + assert "-p, --parent-pdf-set TEXT" in result.output + assert "-m, --members" in result.output + assert "-i, --install" in result.output From eea43a2679dbc52f7bb7de499c52cb5f3b6ac201 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 21 Mar 2022 12:31:14 +0100 Subject: [PATCH 030/148] Add ekobox.mock test --- tests/ekobox/test_mock.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/ekobox/test_mock.py diff --git a/tests/ekobox/test_mock.py b/tests/ekobox/test_mock.py new file mode 100644 index 000000000..0d9311086 --- /dev/null +++ b/tests/ekobox/test_mock.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +import numpy as np + +from ekobox import mock + + +def test_eko_identity(): + for s in ((1, 2, 2, 2, 2), (1, 3, 3, 3, 3)): + i = mock.eko_identity(s) + assert s == i.shape + # is identity? + f = np.random.rand(*s[-2:]) + g = np.einsum("qkbja,ja->qkb", i, f) + np.testing.assert_allclose(g[0], f) From 406b2b4665898f0f6c67c50b60e893776de23330 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 21 Mar 2022 14:30:43 +0100 Subject: [PATCH 031/148] Start mocking lhapdf, revert flavors --- src/ekobox/genpdf/flavors.py | 3 +++ src/ekobox/genpdf/load.py | 5 +---- tests/conftest.py | 17 +++++++++++++++++ tests/ekobox/test_genpdf_load.py | 22 ++++++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 tests/ekobox/test_genpdf_load.py diff --git a/src/ekobox/genpdf/flavors.py b/src/ekobox/genpdf/flavors.py index b892deb99..121d28c15 100644 --- a/src/ekobox/genpdf/flavors.py +++ b/src/ekobox/genpdf/flavors.py @@ -129,6 +129,9 @@ def is_pid_labels(labels): except (ValueError, TypeError): return False for label in labels: + # label might still be a list (for general projection) + if not isinstance(label, np.int_): + return False if label not in br.flavor_basis_pids: return False return True diff --git a/src/ekobox/genpdf/load.py b/src/ekobox/genpdf/load.py index 4c1112b2d..f52f8b0e3 100644 --- a/src/ekobox/genpdf/load.py +++ b/src/ekobox/genpdf/load.py @@ -55,13 +55,10 @@ def load_blocks_from_file(pdfset_name, member): """ import lhapdf # pylint: disable=import-error, import-outside-toplevel - pdf = lhapdf.mkPDF(pdfset_name, member) src = pathlib.Path(lhapdf.paths()[0]) / pdfset_name # read actual file cnt = [] - with open( - src / f"{pdfset_name}_{pdf.memberID:04d}.dat", "r", encoding="utf-8" - ) as o: + with open(src / f"{pdfset_name}_{member:04d}.dat", "r", encoding="utf-8") as o: cnt = o.readlines() # file head head = cnt[0] diff --git a/tests/conftest.py b/tests/conftest.py index 5d7b8b435..0c91cb023 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- import os +import pathlib +import sys from contextlib import contextmanager import numpy as np @@ -91,3 +93,18 @@ def fake_factory(): @pytest.fixture def fake_output(): return FakeOutput().fake_output() + + +# Fake LHAPDF +# Thanks https://stackoverflow.com/questions/43162722/mocking-a-module-import-in-pytest + +test_pdf = pathlib.Path(__file__).parents[1] / "benchmarks" / "ekobox" / "fakepdf" + + +def lhapdf_paths(): + return [test_pdf] + + +module = type(sys)("lhapdf") +module.paths = lhapdf_paths +sys.modules["lhapdf"] = module diff --git a/tests/ekobox/test_genpdf_load.py b/tests/ekobox/test_genpdf_load.py new file mode 100644 index 000000000..d0e3e3555 --- /dev/null +++ b/tests/ekobox/test_genpdf_load.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +import numpy as np + +from ekobox import genpdf + + +def test_load_info(): + info = genpdf.load.load_info_from_file("myCT14llo_NF3") + assert "SetDesc" in info + assert "fake" in info["SetDesc"] + assert sorted(info["Flavors"]) == sorted([-3, -2, -1, 21, 1, 2, 3]) + + +def test_load_data_ct14(): + blocks = genpdf.load.load_blocks_from_file("myCT14llo_NF3", 0)[1] + assert len(blocks) == 1 + b0 = blocks[0] + assert isinstance(b0, dict) + assert sorted(b0.keys()) == sorted(["pids", "xgrid", "Q2grid", "data"]) + assert sorted(b0["pids"]) == sorted([-3, -2, -1, 21, 1, 2, 3]) + assert len(b0["data"].T) == 7 + np.testing.assert_allclose(b0["xgrid"][0], 1e-9) From 9903dca14a261e5f3d306b46da0f24e5c3d472ca Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 22 Mar 2022 16:24:02 +0100 Subject: [PATCH 032/148] Add genpdf.export tests --- src/ekobox/genpdf/export.py | 4 +- tests/ekobox/test_genpdf_export.py | 92 +++++++++++++++++++++++++++++ tests/ekobox/test_genpdf_flavors.py | 2 + 3 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 tests/ekobox/test_genpdf_export.py diff --git a/src/ekobox/genpdf/export.py b/src/ekobox/genpdf/export.py index 3a7a02de3..8f906513f 100644 --- a/src/ekobox/genpdf/export.py +++ b/src/ekobox/genpdf/export.py @@ -126,9 +126,7 @@ def dump_set(name, info, member_blocks, pdf_type_list=None): """ dump_info(name, info) for mem, blocks in enumerate(member_blocks): - if not isinstance(pdf_type_list, list): - dump_blocks(name, mem, blocks) - elif len(pdf_type_list) == 0: + if not isinstance(pdf_type_list, list) or len(pdf_type_list) == 0: dump_blocks(name, mem, blocks) else: dump_blocks(name, mem, blocks, pdf_type=pdf_type_list[mem]) diff --git a/tests/ekobox/test_genpdf_export.py b/tests/ekobox/test_genpdf_export.py new file mode 100644 index 000000000..578308136 --- /dev/null +++ b/tests/ekobox/test_genpdf_export.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +import numpy as np +import yaml + +from ekobox import genpdf + + +def test_list_to_str(): + a = genpdf.export.list_to_str([1, 2]) + assert isinstance(a, str) + assert "1." in a + b = genpdf.export.list_to_str([1.0, 2.0]) + assert isinstance(b, str) + assert "1." in a + + +def test_array_to_str(): + s = (2, 2) + a = genpdf.export.array_to_str(np.arange(4).reshape(s)) + assert isinstance(a, str) + assert "1." in a + b = np.array([e.split() for e in a.splitlines()]) + assert b.shape == s + + +def test_dump_info(tmp_path): + n = "test" + p = tmp_path / n + f = p / f"{n}.info" + i = {"a": "b", "c": 2} + genpdf.export.dump_info(p, i) + assert p.exists() + assert f.exists() + # the files might not be perfect yaml, but should be yaml compatible + with open(f, "r", encoding="utf-8") as o: + ii = yaml.safe_load(o) + for k, v in i.items(): + assert k in ii + assert ii[k] == v + + +def fake_blocks(n_blocks, n_x, n_q2, n_pids): + bs = [] + for _ in range(n_blocks): + bs.append( + { + "xgrid": np.linspace(0.0, 1.0, n_x), + "Q2grid": np.geomspace(1.0, 1e3, n_q2), + "pids": np.arange(n_pids), + "data": np.random.rand(n_x * n_q2, n_pids), + } + ) + return bs + + +def test_dump_blocks(tmp_path): + n = "test" + p = tmp_path / n + nb = 2 + for m in range(3): + f = p / f"{n}_{m:04d}.dat" + is_my_type = m > 1 + pdf_type = "Bla: blub" if is_my_type else None + genpdf.export.dump_blocks(p, m, fake_blocks(nb, 2, 2, 2), pdf_type=pdf_type) + assert p.exists() + assert f.exists() + cnt = f.read_text() + if is_my_type: + assert "Bla: blub" in cnt + else: + assert ("central" in cnt) == (m == 0) + assert "Format" in cnt + assert cnt.count("---") == nb + 1 + + +def test_dump_set(tmp_path): + n = "test" + p = tmp_path / n + i = {"a": "b", "c": 2} + nmem = 2 + for pdf_type_list in (None, ["Bla: Blub"] * nmem): + genpdf.export.dump_set( + p, i, [fake_blocks(2, 2, 2, 2) for _ in range(nmem)], pdf_type_list + ) + assert p.exists() + f = p / f"{n}.info" + assert f.exists() + for m in range(nmem): + f = p / f"{n}_{m:04d}.dat" + assert f.exists() + if pdf_type_list is not None: + assert "Bla: Blub" in f.read_text() diff --git a/tests/ekobox/test_genpdf_flavors.py b/tests/ekobox/test_genpdf_flavors.py index f6a767019..19013e2ce 100644 --- a/tests/ekobox/test_genpdf_flavors.py +++ b/tests/ekobox/test_genpdf_flavors.py @@ -7,6 +7,7 @@ def test_is_evolution(): assert genpdf.flavors.is_evolution_labels(["V", "T3"]) + assert not genpdf.flavors.is_evolution_labels([[1, 2]]) assert not genpdf.flavors.is_evolution_labels(["21", "2"]) @@ -14,6 +15,7 @@ def test_is_pids(): assert not genpdf.flavors.is_pid_labels(["V", "T3"]) assert not genpdf.flavors.is_pid_labels(["35", "9"]) assert not genpdf.flavors.is_pid_labels({}) + assert not genpdf.flavors.is_pid_labels([[1, 2]]) assert genpdf.flavors.is_pid_labels([21, 2]) From 3b6717fed0817af7389c679630431fd453189480 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 22 Mar 2022 16:57:09 +0100 Subject: [PATCH 033/148] Improve lhapdf fixtures --- tests/conftest.py | 28 ++++++++++++++++++++-------- tests/ekobox/test_genpdf.py | 13 ++++++++++++- tests/ekobox/test_genpdf_load.py | 8 ++++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0c91cb023..cee5c8df4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os import pathlib +import shutil import sys from contextlib import contextmanager @@ -95,16 +96,27 @@ def fake_output(): return FakeOutput().fake_output() -# Fake LHAPDF -# Thanks https://stackoverflow.com/questions/43162722/mocking-a-module-import-in-pytest +@pytest.fixture +def fake_lhapdf(tmp_path): + def lhapdf_paths(): + return [tmp_path] + + # Thanks https://stackoverflow.com/questions/43162722/mocking-a-module-import-in-pytest + module = type(sys)("lhapdf") + module.paths = lhapdf_paths + sys.modules["lhapdf"] = module -test_pdf = pathlib.Path(__file__).parents[1] / "benchmarks" / "ekobox" / "fakepdf" + yield tmp_path -def lhapdf_paths(): - return [test_pdf] +fakepdf = pathlib.Path(__file__).parents[1] / "benchmarks" / "ekobox" / "fakepdf" -module = type(sys)("lhapdf") -module.paths = lhapdf_paths -sys.modules["lhapdf"] = module +@pytest.fixture +def fake_ct14(fake_lhapdf): + n = "myCT14llo_NF3" + src = fakepdf / n + dst = fake_lhapdf / n + shutil.copytree(src, dst) + yield n + shutil.rmtree(dst) diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 55837a928..5167595f3 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- - +import numpy as np import pytest from ekobox import genpdf @@ -28,3 +28,14 @@ def test_genpdf_exceptions(tmp_path, cd): with pytest.raises(TypeError): genpdf.generate_pdf("debug", [21], info_update=(10, 15, 20)) + + +def test_generate_block(): + xg = np.linspace(0.0, 1.0, 5) + q2s = np.geomspace(1.0, 1e3, 5) + pids = np.arange(3) + b = genpdf.generate_block(lambda pid, x, q2: pid * x * q2, xg, q2s, pids) + assert isinstance(b, dict) + assert sorted(b.keys()) == sorted(["data", "Q2grid", "xgrid", "pids"]) + assert isinstance(b["data"], np.ndarray) + assert b["data"].shape == (len(xg) * len(q2s), len(pids)) diff --git a/tests/ekobox/test_genpdf_load.py b/tests/ekobox/test_genpdf_load.py index d0e3e3555..00e823001 100644 --- a/tests/ekobox/test_genpdf_load.py +++ b/tests/ekobox/test_genpdf_load.py @@ -4,15 +4,15 @@ from ekobox import genpdf -def test_load_info(): - info = genpdf.load.load_info_from_file("myCT14llo_NF3") +def test_load_info(fake_ct14): + info = genpdf.load.load_info_from_file(fake_ct14) assert "SetDesc" in info assert "fake" in info["SetDesc"] assert sorted(info["Flavors"]) == sorted([-3, -2, -1, 21, 1, 2, 3]) -def test_load_data_ct14(): - blocks = genpdf.load.load_blocks_from_file("myCT14llo_NF3", 0)[1] +def test_load_data_ct14(fake_ct14): + blocks = genpdf.load.load_blocks_from_file(fake_ct14, 0)[1] assert len(blocks) == 1 b0 = blocks[0] assert isinstance(b0, dict) From 9cb64072481de64dba5abf0e1638ef2e18c56875 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 22 Mar 2022 17:21:28 +0100 Subject: [PATCH 034/148] Improve generate_pdf --- src/ekobox/genpdf/__init__.py | 52 +++++++++++++++++++++-------------- tests/conftest.py | 3 +- tests/ekobox/test_genpdf.py | 26 ++++++++++++++++-- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/ekobox/genpdf/__init__.py b/src/ekobox/genpdf/__init__.py index 496ef9d0b..c1cf515fb 100644 --- a/src/ekobox/genpdf/__init__.py +++ b/src/ekobox/genpdf/__init__.py @@ -11,20 +11,37 @@ from . import export, flavors, load -def take_data(parent_pdf_set=None, members=False): +def take_data(parent_pdf_set=None, members=False, xgrid=None, Q2grid=None): """ - Auxiliary function for generate_pdf. It provides the info, the heads - of the member files and the blocks to be generated to generate_pdf. + Auxiliary function for `generate_pdf`. + + It provides the info, the heads of the member files and the blocks + to be generated to `generate_pdf`. Parameters ---------- - parent_pdf_set : - the PDF set to be used as parent + parent_pdf_set : None or str or dict + the PDF set to be used as parent set members : bool - if true every member of the parent are loaded + if true every member of the parent is loaded + xgrid : list(float) + produced x grid if given + Q2grid : list(float) + produced Q2 grid if given + + Returns + ------- + info : dict + info dictionary + heads : list(str) + heads of member files if necessary + blocks : list(dict) + data blocks """ - xgrid = np.geomspace(1e-9, 1, 240) - Q2grid = np.geomspace(1.3, 1e5, 35) + if xgrid is None: + xgrid = np.geomspace(1e-9, 1, 240) + if Q2grid is None: + Q2grid = np.geomspace(1.3, 1e5, 35) # collect blocks all_blocks = [] info = None @@ -44,9 +61,9 @@ def take_data(parent_pdf_set=None, members=False): info = load.load_info_from_file(parent_pdf_set) # iterate on members for m in range(int(info["NumMembers"])): - dat = load.load_blocks_from_file(parent_pdf_set, m) - heads.append(dat[0]) - all_blocks.append(dat[1]) + head, blocks = load.load_blocks_from_file(parent_pdf_set, m) + heads.append(head) + all_blocks.append(blocks) if not members: break elif isinstance(parent_pdf_set, dict): @@ -65,7 +82,7 @@ def take_data(parent_pdf_set=None, members=False): ) else: raise ValueError("Unknown parent pdf type") - return heads, info, all_blocks + return info, heads, all_blocks def generate_pdf( @@ -147,7 +164,7 @@ def generate_pdf( flavor_combinations = flavors.pid_to_flavor(labels) # labels = verify_labels(args.labels) - heads, info, all_blocks = take_data(parent_pdf_set=parent_pdf_set, members=members) + info, heads, all_blocks = take_data(parent_pdf_set=parent_pdf_set, members=members) # filter the PDF new_all_blocks = [] @@ -156,10 +173,7 @@ def generate_pdf( # changing info file according to user choice if info_update is not None: - if isinstance(info_update, dict): - info.update(info_update) - else: - raise TypeError("Info to update are not in a dictionary format") + info.update(info_update) # write info["Flavors"] = [int(pid) for pid in br.flavor_basis_pids] info["NumFlavors"] = len(br.flavor_basis_pids) @@ -190,9 +204,7 @@ def install_pdf(name): print(f"install_pdf {name}") target = pathlib.Path(lhapdf.paths()[0]) src = pathlib.Path(name) - if not src.exists(): - raise FileExistsError(src) - shutil.move(str(src), str(target)) + shutil.move(src, target) def generate_block(xfxQ2, xgrid, Q2grid, pids): diff --git a/tests/conftest.py b/tests/conftest.py index cee5c8df4..e72161d1b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,8 +11,7 @@ @pytest.fixture def cd(): - # thanks https://stackoverflow.com/questions/431684/ - # how-do-i-change-the-working-directory-in-python/24176022#24176022 + # thanks https://stackoverflow.com/questions/431684/how-do-i-change-the-working-directory-in-python/24176022#24176022 @contextmanager def wrapped(newdir): prevdir = os.getcwd() diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 5167595f3..27aa43463 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -6,8 +6,8 @@ def test_genpdf_exceptions(tmp_path, cd): - # using a wrong label and then a wrong parent pdf with cd(tmp_path): + # wrong label with pytest.raises(TypeError): genpdf.generate_pdf( "test_genpdf_exceptions1", @@ -17,15 +17,16 @@ def test_genpdf_exceptions(tmp_path, cd): 2: lambda x, Q2: 4.0 * x * (1.0 - x), }, ) + # wrong parent pdf with pytest.raises(ValueError): genpdf.generate_pdf( "test_genpdf_exceptions2", ["g"], 10, ) - with pytest.raises(FileExistsError): + # non-existant PDF set + with pytest.raises(FileNotFoundError): genpdf.install_pdf("foo") - with pytest.raises(TypeError): genpdf.generate_pdf("debug", [21], info_update=(10, 15, 20)) @@ -39,3 +40,22 @@ def test_generate_block(): assert sorted(b.keys()) == sorted(["data", "Q2grid", "xgrid", "pids"]) assert isinstance(b["data"], np.ndarray) assert b["data"].shape == (len(xg) * len(q2s), len(pids)) + + +def test_install_pdf(fake_lhapdf, tmp_path, cd): + mytmp = tmp_path / "install" + mytmp.mkdir() + n = "bla" + p = mytmp / n + i = "test.info" + with cd(mytmp): + with pytest.raises(FileNotFoundError): + genpdf.install_pdf(p) + p.mkdir() + (p / i).write_text("Bla") + genpdf.install_pdf(p) + pp = tmp_path / n + assert not p.exists() + assert pp.exists() + assert (pp / i).exists() + assert "Bla" == (pp / i).read_text() From 65ab083ba0919b70e32123e443eaf8711bc91959 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 22 Mar 2022 18:04:48 +0100 Subject: [PATCH 035/148] Add first genpdf test and improve former --- src/ekobox/genpdf/__init__.py | 37 ++++++++++++++++-------- tests/ekobox/test_genpdf.py | 53 +++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/ekobox/genpdf/__init__.py b/src/ekobox/genpdf/__init__.py index c1cf515fb..f58687cea 100644 --- a/src/ekobox/genpdf/__init__.py +++ b/src/ekobox/genpdf/__init__.py @@ -86,33 +86,40 @@ def take_data(parent_pdf_set=None, members=False, xgrid=None, Q2grid=None): def generate_pdf( - name, labels, parent_pdf_set=None, members=False, info_update=None, install=False + name, + labels, + parent_pdf_set=None, + members=False, + info_update=None, + install=False, + xgrid=None, + Q2grid=None, ): """ Generate a new PDF from a parent PDF with a set of flavors. - If parent_pdf_set is the name of an available PDF set, + If `parent_pdf_set` is the name of an available PDF set, it will be used as parent. In order to use the toy PDF - as parent, it is enough to set parent_pdf_set = "toy" or "toylh". - If parent_pdf_set is not specified, a debug PDF constructed as - x * (1-x) for every flavor will be usedas parent. + as parent, it is enough to set `parent_pdf_set` to "toy" or "toylh". + If `parent_pdf_set` is not specified, a debug PDF constructed as + x * (1-x) for every flavor will be used as parent. It is also possible to provide custom functions for each flavor - in the form of a dictionary: {pid: f(x,Q2)}. + in the form of a dictionary: `{pid: f(x,Q2)}`. - In labels it is possible to pass a list of PIDs or evolution basis + With `labels` it is possible to pass a list of PIDs or evolution basis combinations to keep in the generated PDF. In order to project on custom combinations of PIDs, it is also possible to pass a list containing the desired factors for each flavor. The default behaviour is to generate only one member for a PDF set - (the zero member) but it can be changed setting to True the member flag. + (the zero member) but it can be changed setting to True the `members` flag. - The info_update argument is a dictionary and provide to the user a way - to change the info file of the generated PDF set. If a key of info_update + The `info_update` argument is a dictionary and provide to the user as a way + to change the info file of the generated PDF set. If a key of `info_update` matches with one key of the standard info file, the information are updated, otherwise they are simply added. - Turning True the value of the install flag, it is possible to autmatically + Turning True the value of the `install` flag, it is possible to automatically install the generated PDF to the lhapdf directory. By default install is False. Parameters @@ -127,6 +134,10 @@ def generate_pdf( iterate on members install : bool install on LHAPDF path + xgrid : list(float) + produced x grid if given + Q2grid : list(float) + produced Q2 grid if given Examples -------- @@ -164,7 +175,9 @@ def generate_pdf( flavor_combinations = flavors.pid_to_flavor(labels) # labels = verify_labels(args.labels) - info, heads, all_blocks = take_data(parent_pdf_set=parent_pdf_set, members=members) + info, heads, all_blocks = take_data( + parent_pdf_set, members, xgrid=xgrid, Q2grid=Q2grid + ) # filter the PDF new_all_blocks = [] diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 27aa43463..66b9d5861 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -45,7 +45,7 @@ def test_generate_block(): def test_install_pdf(fake_lhapdf, tmp_path, cd): mytmp = tmp_path / "install" mytmp.mkdir() - n = "bla" + n = "test_install_pdf" p = mytmp / n i = "test.info" with cd(mytmp): @@ -57,5 +57,52 @@ def test_install_pdf(fake_lhapdf, tmp_path, cd): pp = tmp_path / n assert not p.exists() assert pp.exists() - assert (pp / i).exists() - assert "Bla" == (pp / i).read_text() + ppi = pp / i + assert ppi.exists() + assert "Bla" == ppi.read_text() + + +def test_generate_pdf_debug(fake_lhapdf, tmp_path, cd): + mytmp = tmp_path / "install" + mytmp.mkdir() + n = "test_generate_pdf_debug" + xg = np.linspace(0.0, 1.0, 5) + q2s = np.geomspace(1.0, 1e3, 5) + p = mytmp / n + i = f"{n}.info" + with cd(mytmp): + genpdf.generate_pdf( + n, + [21], + None, + info_update={"Debug": "debug"}, + install=True, + xgrid=xg, + Q2grid=q2s, + ) + pp = tmp_path / n + assert not p.exists() + assert pp.exists() + # check info file + ppi = pp / i + assert ppi.exists() + assert "Debug: debug" in ppi.read_text() + ii = genpdf.load.load_info_from_file(n) + assert "Debug" in ii + assert ii["Debug"] == "debug" + # check member file + ppm = pp / f"{n}_0000.dat" + assert ppm.exists() + assert "PdfType: central" in ppm.read_text() + head, blocks = genpdf.load.load_blocks_from_file(n, 0) + assert "PdfType: central" in head + assert len(blocks) == 1 + b = blocks[0] + assert 21 in b["pids"] + for k, line in enumerate(b["data"]): + for pid, f in zip(b["pids"], line): + # the gluon is non-zero in the bulk + if pid == 21 and k > len(xg) - 1 and k < len(b["data"]) - len(xg): + assert not f == 0.0 + else: + assert f == 0.0 From 726808febb0d3ac53a0df80fa60a88e37c134e5a Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 11:32:55 +0100 Subject: [PATCH 036/148] Recover shutil.move in py3.8 and lower --- src/ekobox/genpdf/__init__.py | 4 +++- tests/ekobox/test_genpdf.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ekobox/genpdf/__init__.py b/src/ekobox/genpdf/__init__.py index f58687cea..3bb4e901e 100644 --- a/src/ekobox/genpdf/__init__.py +++ b/src/ekobox/genpdf/__init__.py @@ -217,7 +217,9 @@ def install_pdf(name): print(f"install_pdf {name}") target = pathlib.Path(lhapdf.paths()[0]) src = pathlib.Path(name) - shutil.move(src, target) + # shutil.move only accepts paths since 3.9 so we need to cast + # https://docs.python.org/3/library/shutil.html?highlight=shutil#shutil.move + shutil.move(str(src), str(target)) def generate_block(xfxQ2, xgrid, Q2grid, pids): diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 66b9d5861..3bf3cf639 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -43,6 +43,7 @@ def test_generate_block(): def test_install_pdf(fake_lhapdf, tmp_path, cd): + # move into subdir to be able to move mytmp = tmp_path / "install" mytmp.mkdir() n = "test_install_pdf" From 8f3c854a155846d9b1cd6ae49a0316398653aee8 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 12:33:49 +0100 Subject: [PATCH 037/148] Improve genpdf tests --- tests/conftest.py | 19 ++++++++++-- tests/ekobox/test_genpdf.py | 61 ++++++++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e72161d1b..679284613 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -111,11 +111,24 @@ def lhapdf_paths(): fakepdf = pathlib.Path(__file__).parents[1] / "benchmarks" / "ekobox" / "fakepdf" -@pytest.fixture -def fake_ct14(fake_lhapdf): - n = "myCT14llo_NF3" +def fake_pdf(fake_lhapdf, n): src = fakepdf / n dst = fake_lhapdf / n shutil.copytree(src, dst) yield n shutil.rmtree(dst) + + +@pytest.fixture +def fake_ct14(fake_lhapdf): + yield from fake_pdf(fake_lhapdf, "myCT14llo_NF3") + + +@pytest.fixture +def fake_nn31(fake_lhapdf): + yield from fake_pdf(fake_lhapdf, "myNNPDF31_nlo_as_0118") + + +@pytest.fixture +def fake_mstw(fake_lhapdf): + yield from fake_pdf(fake_lhapdf, "myMSTW2008nlo90cl") diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 3bf3cf639..6a7cf80f9 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -63,12 +63,12 @@ def test_install_pdf(fake_lhapdf, tmp_path, cd): assert "Bla" == ppi.read_text() -def test_generate_pdf_debug(fake_lhapdf, tmp_path, cd): - mytmp = tmp_path / "install" +def test_generate_pdf_debug_pid(fake_lhapdf, cd): + mytmp = fake_lhapdf / "install" mytmp.mkdir() - n = "test_generate_pdf_debug" + n = "test_generate_pdf_debug_pid" xg = np.linspace(0.0, 1.0, 5) - q2s = np.geomspace(1.0, 1e3, 5) + q2s = np.geomspace(1.0, 1e3, 7) p = mytmp / n i = f"{n}.info" with cd(mytmp): @@ -81,7 +81,7 @@ def test_generate_pdf_debug(fake_lhapdf, tmp_path, cd): xgrid=xg, Q2grid=q2s, ) - pp = tmp_path / n + pp = fake_lhapdf / n assert not p.exists() assert pp.exists() # check info file @@ -102,8 +102,55 @@ def test_generate_pdf_debug(fake_lhapdf, tmp_path, cd): assert 21 in b["pids"] for k, line in enumerate(b["data"]): for pid, f in zip(b["pids"], line): - # the gluon is non-zero in the bulk - if pid == 21 and k > len(xg) - 1 and k < len(b["data"]) - len(xg): + # the gluon is non-zero in the bulk - x=0 is included here + if ( + pid == 21 + and k > len(b["Q2grid"]) - 1 + and k < len(b["data"]) - len(b["Q2grid"]) + ): assert not f == 0.0 else: assert f == 0.0 + + +def test_generate_pdf_pdf_evol(fake_lhapdf, fake_nn31, fake_mstw, cd): + # iterate pdfs with their error type and number of blocks + for fake_pdf, err_type, nb in ((fake_nn31, "replica", 2), (fake_mstw, "error", 3)): + n = f"test_generate_pdf_{err_type}_evol" + p = fake_lhapdf / n + i = f"{n}.info" + pi = p / i + with cd(fake_lhapdf): + genpdf.generate_pdf( + n, + ["S"], + fake_pdf, + members=True, + ) + assert p.exists() + # check info file + assert pi.exists() + # check member file + for m in range(1 + 1): + pm = p / f"{n}_{m:04d}.dat" + assert pm.exists() + head, blocks = genpdf.load.load_blocks_from_file(n, m) + assert ("PdfType: central" if m == 0 else f"PdfType: {err_type}") in head + assert len(blocks) == nb + for b in blocks: + for k, line in enumerate(b["data"]): + for pid, f in zip(b["pids"], line): + # the singlet is non-zero in the bulk - x >= 0 + if abs(pid) in range(1, 6 + 1) and k < len(b["data"]) - len( + b["Q2grid"] + ): + assert not f == 0.0 + assert not np.isclose(f, 0.0, atol=1e-15) + else: + # MSTW is not 0 at the end, but only almost + if err_type == "error" and k >= len(b["data"]) - len( + b["Q2grid"] + ): + np.testing.assert_allclose(f, 0.0, atol=1e-15) + else: + assert f == 0.0 From 275e0caa834e3cb6ced6620ec34e30576ecfbf8e Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 12:49:17 +0100 Subject: [PATCH 038/148] Complete genpdf.generate_pdf test --- src/ekobox/genpdf/__init__.py | 2 +- tests/ekobox/test_genpdf.py | 56 +++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/ekobox/genpdf/__init__.py b/src/ekobox/genpdf/__init__.py index 3bb4e901e..ebaf696b1 100644 --- a/src/ekobox/genpdf/__init__.py +++ b/src/ekobox/genpdf/__init__.py @@ -154,7 +154,7 @@ def generate_pdf( through the pure-singlet contributions (starting at |NNLO|) >>> from eko import basis_rotation as br - >>> from ekobox.tools import genpdf + >>> from ekobox import genpdf >>> import numpy as np >>> anti_qed_singlet = np.zeros_like(br.flavor_basis_pids, dtype=np.float_) >>> anti_qed_singlet[br.flavor_basis_pids.index(1)] = -4 diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 6a7cf80f9..76b9e7e1e 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -2,6 +2,7 @@ import numpy as np import pytest +from eko import basis_rotation as br from ekobox import genpdf @@ -113,9 +114,13 @@ def test_generate_pdf_debug_pid(fake_lhapdf, cd): assert f == 0.0 -def test_generate_pdf_pdf_evol(fake_lhapdf, fake_nn31, fake_mstw, cd): +def test_generate_pdf_pdf_evol(fake_lhapdf, fake_nn31, fake_mstw, fake_ct14, cd): # iterate pdfs with their error type and number of blocks - for fake_pdf, err_type, nb in ((fake_nn31, "replica", 2), (fake_mstw, "error", 3)): + for fake_pdf, err_type, nmem, nb in ( + (fake_nn31, "replica", 1, 2), + (fake_mstw, "error", 1, 3), + (fake_ct14, "", 0, 1), + ): n = f"test_generate_pdf_{err_type}_evol" p = fake_lhapdf / n i = f"{n}.info" @@ -125,13 +130,13 @@ def test_generate_pdf_pdf_evol(fake_lhapdf, fake_nn31, fake_mstw, cd): n, ["S"], fake_pdf, - members=True, + members=nmem > 0, ) assert p.exists() # check info file assert pi.exists() - # check member file - for m in range(1 + 1): + # check member files + for m in range(nmem + 1): pm = p / f"{n}_{m:04d}.dat" assert pm.exists() head, blocks = genpdf.load.load_blocks_from_file(n, m) @@ -154,3 +159,44 @@ def test_generate_pdf_pdf_evol(fake_lhapdf, fake_nn31, fake_mstw, cd): np.testing.assert_allclose(f, 0.0, atol=1e-15) else: assert f == 0.0 + + +def test_generate_pdf_toy_antiqed(fake_lhapdf, cd): + # iterate pdfs with their error type and number of blocks + n = "test_generate_pdf_toy_antiqed" + xg = np.linspace(1e-5, 1.0, 5) + q2s = np.geomspace(1.0, 1e3, 7) + anti_qed_singlet = np.zeros_like(br.flavor_basis_pids, dtype=np.float_) + anti_qed_singlet[br.flavor_basis_pids.index(1)] = -4 + anti_qed_singlet[br.flavor_basis_pids.index(-1)] = -4 + anti_qed_singlet[br.flavor_basis_pids.index(2)] = 1 + anti_qed_singlet[br.flavor_basis_pids.index(-2)] = 1 + p = fake_lhapdf / n + i = f"{n}.info" + pi = p / i + with cd(fake_lhapdf): + genpdf.generate_pdf( + n, + [anti_qed_singlet], + "toy", + xgrid=xg, + Q2grid=q2s, + ) + assert p.exists() + # check info file + assert pi.exists() + # check member files + pm = p / f"{n}_0000.dat" + assert pm.exists() + assert "PdfType: central" in pm.read_text() + head, blocks = genpdf.load.load_blocks_from_file(n, 0) + assert "PdfType: central" in head + assert len(blocks) == 1 + b = blocks[0] + for k, line in enumerate(b["data"]): + for pid, f in zip(b["pids"], line): + # the u and d are non-zero in the bulk - x=0 is not included here + if abs(pid) in [1, 2] and k < len(b["data"]) - len(b["Q2grid"]): + assert not f == 0.0 + else: + assert f == 0.0 From fbe9e9d057954daddc9af701467b227bbef294ab Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 12:52:59 +0100 Subject: [PATCH 039/148] Resolve fake_pdf name clash --- tests/conftest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 679284613..4969506dd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -111,7 +111,7 @@ def lhapdf_paths(): fakepdf = pathlib.Path(__file__).parents[1] / "benchmarks" / "ekobox" / "fakepdf" -def fake_pdf(fake_lhapdf, n): +def copy_lhapdf(fake_lhapdf, n): src = fakepdf / n dst = fake_lhapdf / n shutil.copytree(src, dst) @@ -121,14 +121,14 @@ def fake_pdf(fake_lhapdf, n): @pytest.fixture def fake_ct14(fake_lhapdf): - yield from fake_pdf(fake_lhapdf, "myCT14llo_NF3") + yield from copy_lhapdf(fake_lhapdf, "myCT14llo_NF3") @pytest.fixture def fake_nn31(fake_lhapdf): - yield from fake_pdf(fake_lhapdf, "myNNPDF31_nlo_as_0118") + yield from copy_lhapdf(fake_lhapdf, "myNNPDF31_nlo_as_0118") @pytest.fixture def fake_mstw(fake_lhapdf): - yield from fake_pdf(fake_lhapdf, "myMSTW2008nlo90cl") + yield from copy_lhapdf(fake_lhapdf, "myMSTW2008nlo90cl") From d47788ce1ef52d982ddf3e95c059992238dd09cd Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 14:16:09 +0100 Subject: [PATCH 040/148] Complete genpdf.cli tests --- tests/ekobox/test_genpdf_cli.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/ekobox/test_genpdf_cli.py b/tests/ekobox/test_genpdf_cli.py index 25850c1d4..c412bdb7a 100644 --- a/tests/ekobox/test_genpdf_cli.py +++ b/tests/ekobox/test_genpdf_cli.py @@ -19,3 +19,19 @@ def test_genpdf_CLI_messages(): assert "-p, --parent-pdf-set TEXT" in result.output assert "-m, --members" in result.output assert "-i, --install" in result.output + + +def test_genpdf_CLI(fake_lhapdf, cd): + n = "test_genpdf_CLI" + runner = CliRunner() + result = None + with cd(fake_lhapdf): + result = runner.invoke(cli, ["generate", n, "21"]) + assert result is not None + assert result.exception is None + p = fake_lhapdf / n + assert p.exists() + pi = p / f"{n}.info" + assert pi.exists() + pm = p / f"{n}_0000.dat" + assert pm.exists() From a96aca421a052bc0c988bf0d595f7d691d0748a4 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 15:08:25 +0100 Subject: [PATCH 041/148] Add ekobox install cli test --- tests/ekobox/test_genpdf.py | 6 +++--- tests/ekobox/test_genpdf_cli.py | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/ekobox/test_genpdf.py b/tests/ekobox/test_genpdf.py index 76b9e7e1e..3f68ae9b4 100644 --- a/tests/ekobox/test_genpdf.py +++ b/tests/ekobox/test_genpdf.py @@ -43,9 +43,9 @@ def test_generate_block(): assert b["data"].shape == (len(xg) * len(q2s), len(pids)) -def test_install_pdf(fake_lhapdf, tmp_path, cd): +def test_install_pdf(fake_lhapdf, cd): # move into subdir to be able to move - mytmp = tmp_path / "install" + mytmp = fake_lhapdf / "install" mytmp.mkdir() n = "test_install_pdf" p = mytmp / n @@ -56,7 +56,7 @@ def test_install_pdf(fake_lhapdf, tmp_path, cd): p.mkdir() (p / i).write_text("Bla") genpdf.install_pdf(p) - pp = tmp_path / n + pp = fake_lhapdf / n assert not p.exists() assert pp.exists() ppi = pp / i diff --git a/tests/ekobox/test_genpdf_cli.py b/tests/ekobox/test_genpdf_cli.py index c412bdb7a..ec973140e 100644 --- a/tests/ekobox/test_genpdf_cli.py +++ b/tests/ekobox/test_genpdf_cli.py @@ -22,13 +22,15 @@ def test_genpdf_CLI_messages(): def test_genpdf_CLI(fake_lhapdf, cd): + mytmp = fake_lhapdf / "install" + mytmp.mkdir() n = "test_genpdf_CLI" runner = CliRunner() - result = None - with cd(fake_lhapdf): - result = runner.invoke(cli, ["generate", n, "21"]) - assert result is not None - assert result.exception is None + with cd(mytmp): + result_gen = runner.invoke(cli, ["generate", n, "21"]) + assert result_gen.exception is None + result_inst = runner.invoke(cli, ["install", n]) + assert result_inst.exception is None p = fake_lhapdf / n assert p.exists() pi = p / f"{n}.info" From 2e19e5788fe03e147990e9814f8babd1cbb01877 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 23 Mar 2022 15:14:06 +0100 Subject: [PATCH 042/148] Rename ekobox info file --- src/ekobox/__init__.py | 2 +- src/ekobox/evol_pdf.py | 4 ++-- src/ekobox/gen_theory.py | 16 ++++++---------- src/ekobox/{gen_info.py => info_file.py} | 6 +++--- tests/ekobox/test_evol_pdf.py | 8 +++++++- .../{test_gen_info.py => test_info_file.py} | 6 +++--- 6 files changed, 22 insertions(+), 20 deletions(-) rename src/ekobox/{gen_info.py => info_file.py} (92%) rename tests/ekobox/{test_gen_info.py => test_info_file.py} (87%) diff --git a/src/ekobox/__init__.py b/src/ekobox/__init__.py index 3ce63bd8f..085acddf2 100644 --- a/src/ekobox/__init__.py +++ b/src/ekobox/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from ekomark import apply -from . import evol_pdf, gen_info, gen_op, gen_theory +from . import evol_pdf, gen_op, gen_theory, info_file diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index 60c4ed24c..ea6c1eee6 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -6,7 +6,7 @@ from eko import basis_rotation as br from ekomark import apply -from . import gen_info, genpdf +from . import genpdf, info_file def evolve_pdfs( @@ -79,7 +79,7 @@ def evolve_pdfs( info_update = {} info_update["XMin"] = targetgrid[0] info_update["XMax"] = targetgrid[-1] - info = gen_info.create_info_file( + info = info_file.build( theory_card, operators_card, len(evolved_PDF_list), diff --git a/src/ekobox/gen_theory.py b/src/ekobox/gen_theory.py index 69fba7cdd..cbc2d8267 100644 --- a/src/ekobox/gen_theory.py +++ b/src/ekobox/gen_theory.py @@ -12,26 +12,22 @@ def gen_theory_card(pto, initial_scale, update=None, name=None): Parameters ---------- - pto : int perturbation theory order initial_scale: float - initial scale of evolution + initial scale of evolution [GeV] update : dict info to update to default theory card name : str - name of exported theory card (if name not None ) + name of exported theory card (if name is not None ) Returns ------- - - : dict - theory card + dict : + theory card """ # Constructing the dictionary with some default values theory = copy.deepcopy(theories.default_card) - # delete unuseful member - del theory["FNS"] # Adding the mandatory inputs theory["PTO"] = pto theory["Q0"] = initial_scale @@ -42,7 +38,7 @@ def gen_theory_card(pto, initial_scale, update=None, name=None): raise ValueError("Provided key not in theory card") theory.update(update) serialized = sql.serialize(theory) - theory["hash"] = (sql.add_hash(serialized))[-1] + theory["hash"] = sql.add_hash(serialized)[-1] if name is not None: export_theory_card(name, theory) return theory @@ -76,7 +72,7 @@ def import_theory_card(path): Returns ------- - : dict + dict : theory card """ with open(path, "r", encoding="utf-8") as o: diff --git a/src/ekobox/gen_info.py b/src/ekobox/info_file.py similarity index 92% rename from src/ekobox/gen_info.py rename to src/ekobox/info_file.py index 757ae4c7c..626b52b67 100644 --- a/src/ekobox/gen_info.py +++ b/src/ekobox/info_file.py @@ -5,7 +5,7 @@ from .genpdf import load -def create_info_file(theory_card, operators_card, num_members, info_update): +def build(theory_card, operators_card, num_members, info_update): """ Generate a lhapdf info file from theory and operators card @@ -22,8 +22,8 @@ def create_info_file(theory_card, operators_card, num_members, info_update): Returns ------- - : dict - info file in lhapdf format + dict : + info file in lhapdf format """ template_info = copy.deepcopy(load.template_info) template_info["SetDesc"] = "Evolved PDF from " + str(theory_card["Q0"]) + " GeV" diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index dabb8616a..c9d745309 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -9,7 +9,13 @@ def test_gen_and_dump_out(tmp_path): - op = g_o.gen_op_card([100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) + op = g_o.gen_op_card( + [100.0], + update={ + "xgrid": [0.1, 0.5, 1.0], + "configs": {"interpolation_polynomial_degree": 1}, + }, + ) theory = g_t.gen_theory_card(0, 1.0) out = ev_p.gen_out(theory, op, path=tmp_path) diff --git a/tests/ekobox/test_gen_info.py b/tests/ekobox/test_info_file.py similarity index 87% rename from tests/ekobox/test_gen_info.py rename to tests/ekobox/test_info_file.py index f82e76c76..68d3e3dfc 100644 --- a/tests/ekobox/test_gen_info.py +++ b/tests/ekobox/test_info_file.py @@ -3,15 +3,15 @@ import numpy as np -from ekobox import gen_info as g_i from ekobox import gen_op as g_o from ekobox import gen_theory as g_t +from ekobox import info_file -def test_create_info_file(): +def test_build(): op = g_o.gen_op_card([10, 100]) theory = g_t.gen_theory_card(1, 10.0, update={"alphas": 0.2}) - info = g_i.create_info_file( + info = info_file.build( theory, op, 4, info_update={"SetDesc": "Prova", "NewArg": 15.3, "MTop": 1.0} ) assert info["AlphaS_MZ"] == 0.2 From 9ba039e7ef788bcfc82ef7d99c6255d352b8f065 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 13:02:10 +0200 Subject: [PATCH 043/148] Complete coverage --- pyproject.toml | 2 +- src/ekobox/evol_pdf.py | 40 ++------------------- tests/ekobox/test_evol_pdf.py | 65 ++++++++++++++++++++++------------- tests/ekobox/test_utils.py | 19 ++++++---- 4 files changed, 59 insertions(+), 67 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4811a1c66..a2130c1f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,7 +118,7 @@ python_classes = ['Test*', 'Benchmark*'] python_functions = ['test_*', 'benchmark_*'] addopts = [ '--cov=eko', - # '--cov=ekobox', + '--cov=ekobox', '--cov-report=html', '--cov-report=xml', '--strict-markers', diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index ea6c1eee6..55c622777 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -28,39 +28,28 @@ def evolve_pdfs( ---------- initial_PDF_list : list(lhapdf object) list of PDF members to be evolved - theory_card : dict theory card - operators_card : dict operators card - path : str path to cached eko output (if "None" it will be recomputed) - store_path : str path where the eko is stored (if "None" will not be saved) - targetgrid : list(float) target x-grid (if different from input x-grid) - install : bool set whether to install evolved PDF to lhapdf directory - name : str set name of evolved PDF - info_update : dict dict of info to add or update to default info file - """ eko_output = None if path is not None: my_path = pathlib.Path(path) if my_path.is_dir(): - ops_id = f"o{operators_card['hash'][:6]}_t{theory_card['hash'][:6]}.tar" - ops_id_path = pathlib.Path(ops_id) - outpath = my_path / ops_id_path.relative_to(ops_id_path.anchor) + outpath = my_path / ekofileid(theory_card, operators_card) eko_output = eko.output.legacy.load_tar(outpath) else: eko_output = eko.output.legacy.load_tar(my_path) @@ -106,28 +95,5 @@ def evolve_pdfs( genpdf.install_pdf(name) -def gen_out(theory_card, op_card, path=None): - """ - Generates EKO output from theory and operators cards and, if requested, - dumps it in tar format - - Parameters - ---------- - theory_card : dict - theory card - op_card : dict - operators card - path : str - path of dumped output (if "None" output is not dumped) - - Returns - ------- - : eko.output.Output - eko output - """ - eko_output = eko.run_dglap(theory_card, op_card) - if path is not None: - ops_id = f"o{op_card['hash'][:6]}_t{theory_card['hash'][:6]}" - path = f"{path}/{ops_id}.tar" - eko.output.legacy.dump_tar(eko_output, path) - return eko_output +def ekofileid(theory_card, operators_card): + return f"o{operators_card['hash'][:6]}_t{theory_card['hash'][:6]}.tar" diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index c9d745309..ff9999e81 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -1,31 +1,50 @@ # -*- coding: utf-8 -*- -import numpy as np +from banana import toy -import eko.output.legacy +import eko +import eko.output.legacy as out from ekobox import evol_pdf as ev_p from ekobox import gen_op as g_o from ekobox import gen_theory as g_t +op = g_o.gen_op_card( + [100.0], + update={ + "xgrid": [0.1, 0.5, 1.0], + "configs": {"interpolation_polynomial_degree": 1}, + }, +) +theory = g_t.gen_theory_card(0, 1.65) -def test_gen_and_dump_out(tmp_path): - op = g_o.gen_op_card( - [100.0], - update={ - "xgrid": [0.1, 0.5, 1.0], - "configs": {"interpolation_polynomial_degree": 1}, - }, - ) - theory = g_t.gen_theory_card(0, 1.0) - - out = ev_p.gen_out(theory, op, path=tmp_path) - - ops_id = f"o{op['hash'][:6]}_t{theory['hash'][:6]}" - outpath = f"{tmp_path}/{ops_id}.tar" - loaded_out = eko.output.legacy.load_tar(outpath) - assert out.xgrid.tolist() == loaded_out.xgrid.tolist() - for el, loaded_el in zip(out[100.0].operator, loaded_out[100.0].operator): - np.testing.assert_allclose( - el, - loaded_el, - ) + +def test_evolve_pdfs_run(fake_lhapdf, cd): + n = "test_evolve_pdfs_run" + mytmp = fake_lhapdf / "install" + mytmp.mkdir() + with cd(mytmp): + ev_p.evolve_pdfs([toy.mkPDF("", 0)], theory, op, install=True, name=n) + p = fake_lhapdf / n + assert p.exists() + + +def test_evolve_pdfs_dump_path(fake_lhapdf, cd): + n = "test_evolve_pdfs_dump_path" + peko = fake_lhapdf / ev_p.ekofileid(theory, op) + out.dump_tar(eko.run_dglap(theory, op), peko) + assert peko.exists() + with cd(fake_lhapdf): + ev_p.evolve_pdfs([toy.mkPDF("", 0)], theory, op, name=n, path=fake_lhapdf) + p = fake_lhapdf / n + assert p.exists() + + +def test_evolve_pdfs_dump_file(fake_lhapdf, cd): + n = "test_evolve_pdfs_dump_file" + peko = fake_lhapdf / ev_p.ekofileid(theory, op) + out.dump_tar(eko.run_dglap(theory, op), peko) + assert peko.exists() + with cd(fake_lhapdf): + ev_p.evolve_pdfs([toy.mkPDF("", 0)], theory, op, name=n, path=peko) + p = fake_lhapdf / n + assert p.exists() diff --git a/tests/ekobox/test_utils.py b/tests/ekobox/test_utils.py index 69fd86d05..a85606da9 100644 --- a/tests/ekobox/test_utils.py +++ b/tests/ekobox/test_utils.py @@ -2,7 +2,7 @@ import numpy as np import pytest -from ekobox import evol_pdf as ev_p +import eko from ekobox import gen_op as g_o from ekobox import gen_theory as g_t from ekobox import utils @@ -11,21 +11,28 @@ def test_ekos_product(): # Generating two ekos op1 = g_o.gen_op_card( - [60.0, 80.0, 100.0], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} + [60.0, 80.0, 100.0], + update={ + "xgrid": [0.1, 0.5, 1.0], + "configs": {"interpolation_polynomial_degree": 1}, + }, ) theory1 = g_t.gen_theory_card(0, 5.0) op2 = g_o.gen_op_card( [80.0, 100.0, 120.0], - update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}, + update={ + "xgrid": [0.1, 0.5, 1.0], + "configs": {"interpolation_polynomial_degree": 1}, + }, ) theory2 = g_t.gen_theory_card(0, 10.0) theory_err = g_t.gen_theory_card(0, 5.0) - eko_ini = ev_p.gen_out(theory1, op1) - eko_fin = ev_p.gen_out(theory2, op2) + eko_ini = eko.run_dglap(theory1, op1) + eko_fin = eko.run_dglap(theory2, op2) # Test_error - eko_fin_err = ev_p.gen_out(theory_err, op2) + eko_fin_err = eko.run_dglap(theory_err, op2) with pytest.raises(ValueError): _ = utils.ekos_product(eko_ini, eko_fin_err) # product is copied From 15a2161c85436a1826303fae6ec0b65e1f36247b Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 24 Mar 2022 11:02:49 +0100 Subject: [PATCH 044/148] Rename gen_theory --- benchmarks/ekobox/benchmark_evol_pdf.py | 6 +++--- src/ekobox/__init__.py | 2 +- src/ekobox/{gen_theory.py => theory_card.py} | 8 ++++---- tests/ekobox/test_evol_pdf.py | 4 ++-- tests/ekobox/test_info_file.py | 4 ++-- .../{test_gen_theory.py => test_theory_card.py} | 16 ++++++++-------- tests/ekobox/test_utils.py | 8 ++++---- 7 files changed, 24 insertions(+), 24 deletions(-) rename src/ekobox/{gen_theory.py => theory_card.py} (91%) rename tests/ekobox/{test_gen_theory.py => test_theory_card.py} (52%) diff --git a/benchmarks/ekobox/benchmark_evol_pdf.py b/benchmarks/ekobox/benchmark_evol_pdf.py index a3a4b16b7..d2b2cd724 100644 --- a/benchmarks/ekobox/benchmark_evol_pdf.py +++ b/benchmarks/ekobox/benchmark_evol_pdf.py @@ -7,7 +7,7 @@ from eko import basis_rotation as br from ekobox import evol_pdf as ev_p from ekobox import gen_op as g_o -from ekobox import gen_theory as g_t +from ekobox import theory_card as tc from ekobox.genpdf import load test_pdf = pathlib.Path(__file__).parent / "fakepdf" @@ -22,7 +22,7 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): 10000.0, ] op = g_o.gen_op_card(q2grid) - theory = g_t.gen_theory_card( + theory = tc.generate( 0, 5.0, update={ @@ -80,7 +80,7 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): @pytest.mark.isolated def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): op = g_o.gen_op_card([10, 100], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) - theory = g_t.gen_theory_card(0, 1.0) + theory = tc.generate(0, 1.0) with lhapdf_path(test_pdf): pdfs = lhapdf.mkPDFs("myMSTW2008nlo90cl") d = tmp_path / "sub" diff --git a/src/ekobox/__init__.py b/src/ekobox/__init__.py index 085acddf2..93c2e4e73 100644 --- a/src/ekobox/__init__.py +++ b/src/ekobox/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from ekomark import apply -from . import evol_pdf, gen_op, gen_theory, info_file +from . import evol_pdf, gen_op, info_file, theory_card diff --git a/src/ekobox/gen_theory.py b/src/ekobox/theory_card.py similarity index 91% rename from src/ekobox/gen_theory.py rename to src/ekobox/theory_card.py index cbc2d8267..ee9c71c3c 100644 --- a/src/ekobox/gen_theory.py +++ b/src/ekobox/theory_card.py @@ -5,7 +5,7 @@ from banana.data import sql, theories -def gen_theory_card(pto, initial_scale, update=None, name=None): +def generate(pto, initial_scale, update=None, name=None): """ Generates a theory card with some mandatory user choice and some default values which can be changed by the update input dict @@ -40,11 +40,11 @@ def gen_theory_card(pto, initial_scale, update=None, name=None): serialized = sql.serialize(theory) theory["hash"] = sql.add_hash(serialized)[-1] if name is not None: - export_theory_card(name, theory) + dump(name, theory) return theory -def export_theory_card(name, theory): +def dump(name, theory): """ Export the theory card in the current directory @@ -61,7 +61,7 @@ def export_theory_card(name, theory): yaml.safe_dump(theory, out) -def import_theory_card(path): +def load(path): """ Import the theory card specified by path diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index ff9999e81..f69ef5258 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -6,7 +6,7 @@ import eko.output.legacy as out from ekobox import evol_pdf as ev_p from ekobox import gen_op as g_o -from ekobox import gen_theory as g_t +from ekobox import theory_card as tc op = g_o.gen_op_card( [100.0], @@ -15,7 +15,7 @@ "configs": {"interpolation_polynomial_degree": 1}, }, ) -theory = g_t.gen_theory_card(0, 1.65) +theory = tc.generate(0, 1.65) def test_evolve_pdfs_run(fake_lhapdf, cd): diff --git a/tests/ekobox/test_info_file.py b/tests/ekobox/test_info_file.py index 68d3e3dfc..109983af4 100644 --- a/tests/ekobox/test_info_file.py +++ b/tests/ekobox/test_info_file.py @@ -4,13 +4,13 @@ import numpy as np from ekobox import gen_op as g_o -from ekobox import gen_theory as g_t from ekobox import info_file +from ekobox import theory_card as tc def test_build(): op = g_o.gen_op_card([10, 100]) - theory = g_t.gen_theory_card(1, 10.0, update={"alphas": 0.2}) + theory = tc.generate(1, 10.0, update={"alphas": 0.2}) info = info_file.build( theory, op, 4, info_update={"SetDesc": "Prova", "NewArg": 15.3, "MTop": 1.0} ) diff --git a/tests/ekobox/test_gen_theory.py b/tests/ekobox/test_theory_card.py similarity index 52% rename from tests/ekobox/test_gen_theory.py rename to tests/ekobox/test_theory_card.py index 468b3c401..322a01e95 100644 --- a/tests/ekobox/test_gen_theory.py +++ b/tests/ekobox/test_theory_card.py @@ -1,28 +1,28 @@ # -*- coding: utf-8 -*- import pytest -from ekobox import gen_theory as g_t +from ekobox import theory_card as tc def test_gen_theory_card(): - theory = g_t.gen_theory_card(0, 1.0) + theory = tc.generate(0, 1.0) assert theory["PTO"] == 0 assert theory["Q0"] == 1.0 assert theory["mt"] == 173.07 up_err = {"Prova": "Prova"} with pytest.raises(ValueError): - theory = g_t.gen_theory_card(0, 1.0, update=up_err) + theory = tc.generate(0, 1.0, update=up_err) up = {"mb": 132.3, "PTO": 2} - theory = g_t.gen_theory_card(0, 1.0, update=up) + theory = tc.generate(0, 1.0, update=up) assert theory["PTO"] == 2 assert theory["mb"] == 132.3 def test_export_load_theory_card(tmp_path, cd): with cd(tmp_path): - theory = g_t.gen_theory_card(2, 12.3, name="debug_theory") - g_t.export_theory_card("debug_theory_two", theory) - theory_loaded = g_t.import_theory_card("debug_theory.yaml") - theory_two_loaded = g_t.import_theory_card("debug_theory_two.yaml") + theory = tc.generate(2, 12.3, name="debug_theory") + tc.dump("debug_theory_two", theory) + theory_loaded = tc.load("debug_theory.yaml") + theory_two_loaded = tc.load("debug_theory_two.yaml") for key in theory.keys(): assert theory[key] == theory_loaded[key] == theory_two_loaded[key] diff --git a/tests/ekobox/test_utils.py b/tests/ekobox/test_utils.py index a85606da9..001ec0c68 100644 --- a/tests/ekobox/test_utils.py +++ b/tests/ekobox/test_utils.py @@ -4,7 +4,7 @@ import eko from ekobox import gen_op as g_o -from ekobox import gen_theory as g_t +from ekobox import theory_card as tc from ekobox import utils @@ -17,7 +17,7 @@ def test_ekos_product(): "configs": {"interpolation_polynomial_degree": 1}, }, ) - theory1 = g_t.gen_theory_card(0, 5.0) + theory1 = tc.generate(0, 5.0) op2 = g_o.gen_op_card( [80.0, 100.0, 120.0], @@ -26,8 +26,8 @@ def test_ekos_product(): "configs": {"interpolation_polynomial_degree": 1}, }, ) - theory2 = g_t.gen_theory_card(0, 10.0) - theory_err = g_t.gen_theory_card(0, 5.0) + theory2 = tc.generate(0, 10.0) + theory_err = tc.generate(0, 5.0) eko_ini = eko.run_dglap(theory1, op1) eko_fin = eko.run_dglap(theory2, op2) From dcf29c99379fbb14f89f38b09d0640efe7c26051 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 24 Mar 2022 11:08:55 +0100 Subject: [PATCH 045/148] Rename gen_op --- benchmarks/ekobox/benchmark_evol_pdf.py | 6 +-- src/ekobox/__init__.py | 2 +- src/ekobox/{gen_op.py => operators_card.py} | 49 +++++++++---------- tests/ekobox/test_evol_pdf.py | 4 +- tests/ekobox/test_info_file.py | 4 +- ...{test_gen_op.py => test_operators_card.py} | 20 ++++---- tests/ekobox/test_theory_card.py | 4 +- tests/ekobox/test_utils.py | 6 +-- 8 files changed, 47 insertions(+), 48 deletions(-) rename src/ekobox/{gen_op.py => operators_card.py} (65%) rename tests/ekobox/{test_gen_op.py => test_operators_card.py} (56%) diff --git a/benchmarks/ekobox/benchmark_evol_pdf.py b/benchmarks/ekobox/benchmark_evol_pdf.py index d2b2cd724..1b0c13d26 100644 --- a/benchmarks/ekobox/benchmark_evol_pdf.py +++ b/benchmarks/ekobox/benchmark_evol_pdf.py @@ -6,7 +6,7 @@ from eko import basis_rotation as br from ekobox import evol_pdf as ev_p -from ekobox import gen_op as g_o +from ekobox import operators_card as oc from ekobox import theory_card as tc from ekobox.genpdf import load @@ -21,7 +21,7 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): 100.0, 10000.0, ] - op = g_o.gen_op_card(q2grid) + op = oc.generate(q2grid) theory = tc.generate( 0, 5.0, @@ -79,7 +79,7 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): @pytest.mark.isolated def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): - op = g_o.gen_op_card([10, 100], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) + op = oc.generate([10, 100], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) theory = tc.generate(0, 1.0) with lhapdf_path(test_pdf): pdfs = lhapdf.mkPDFs("myMSTW2008nlo90cl") diff --git a/src/ekobox/__init__.py b/src/ekobox/__init__.py index 93c2e4e73..3a140e99a 100644 --- a/src/ekobox/__init__.py +++ b/src/ekobox/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from ekomark import apply -from . import evol_pdf, gen_op, info_file, theory_card +from . import evol_pdf, info_file, operators_card, theory_card diff --git a/src/ekobox/gen_op.py b/src/ekobox/operators_card.py similarity index 65% rename from src/ekobox/gen_op.py rename to src/ekobox/operators_card.py index 6ce7d9034..b11b1f455 100644 --- a/src/ekobox/gen_op.py +++ b/src/ekobox/operators_card.py @@ -7,27 +7,27 @@ from ekomark.data import operators -def gen_op_card(Q2grid, update=None, name=None): - """Generate operators card. +def generate(Q2grid, update=None, name=None): + """ + Generate operators card. - Generates an operator card with some mandatory user choice + Generates an operators card with some mandatory user choice (in this case only the Q2 grid) and some default values which can be changed by the update input dict. Parameters ---------- - Q2grid : list(float) - grid for Q2 - update : dict - dictionary of info to update in op. card - name : str - name of exported op.card (if name not None) + Q2grid : list(float) + grid for Q2 + update : dict + dictionary of info to update in op. card + name : str + name of exported op.card (if name not None) Returns ------- - dict - operator card - + dict : + operators card """ # Constructing the dictionary with some default value def_op = copy.deepcopy(operators.default_card) @@ -46,40 +46,39 @@ def gen_op_card(Q2grid, update=None, name=None): serialized = sql.serialize(def_op) def_op["hash"] = (sql.add_hash(serialized))[-1] if name is not None: - export_op_card(name, def_op) + dump(name, def_op) return def_op -def export_op_card(name, op): - """Export the operators card in the current directory +def dump(name, op): + """ + Export the operators card in the current directory Parameters ---------- - name : str - name of the op. card to export - - op : dict - op card - + name : str + name of the operators card to export + op : dict + operators card """ target = f"{name}.yaml" with open(target, "w", encoding="utf-8") as out: yaml.safe_dump(op, out) -def import_op_card(path): +def load(path): """ Import the operators card specified by path Parameters ---------- path : str - path to op. card in yaml format + path to operators card in yaml format Returns ------- - : dict - op card + dict : + operators card """ with open(path, "r", encoding="utf-8") as o: op = yaml.safe_load(o) diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index f69ef5258..fc833cd90 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -5,10 +5,10 @@ import eko import eko.output.legacy as out from ekobox import evol_pdf as ev_p -from ekobox import gen_op as g_o +from ekobox import operators_card as oc from ekobox import theory_card as tc -op = g_o.gen_op_card( +op = oc.generate( [100.0], update={ "xgrid": [0.1, 0.5, 1.0], diff --git a/tests/ekobox/test_info_file.py b/tests/ekobox/test_info_file.py index 109983af4..643d02745 100644 --- a/tests/ekobox/test_info_file.py +++ b/tests/ekobox/test_info_file.py @@ -3,13 +3,13 @@ import numpy as np -from ekobox import gen_op as g_o from ekobox import info_file +from ekobox import operators_card as oc from ekobox import theory_card as tc def test_build(): - op = g_o.gen_op_card([10, 100]) + op = oc.generate([10, 100]) theory = tc.generate(1, 10.0, update={"alphas": 0.2}) info = info_file.build( theory, op, 4, info_update={"SetDesc": "Prova", "NewArg": 15.3, "MTop": 1.0} diff --git a/tests/ekobox/test_gen_op.py b/tests/ekobox/test_operators_card.py similarity index 56% rename from tests/ekobox/test_gen_op.py rename to tests/ekobox/test_operators_card.py index bc0821d26..de196191c 100644 --- a/tests/ekobox/test_gen_op.py +++ b/tests/ekobox/test_operators_card.py @@ -1,30 +1,30 @@ # -*- coding: utf-8 -*- import pytest -from ekobox import gen_op as g_o +from ekobox import operators_card as oc -def test_gen_op_card(): - op = g_o.gen_op_card([10, 100]) +def test_generate_ocard(): + op = oc.generate([10, 100]) assert op["Q2grid"] == [10, 100] assert op["configs"]["interpolation_polynomial_degree"] == 4 up_err = {"Prova": "Prova"} with pytest.raises(ValueError): - op = g_o.gen_op_card([10], update=up_err) + op = oc.generate([10], update=up_err) up = { "configs": {"interpolation_polynomial_degree": 2, "interpolation_is_log": False} } - op = g_o.gen_op_card([100], update=up) + op = oc.generate([100], update=up) assert op["Q2grid"] == [100] assert op["configs"]["interpolation_polynomial_degree"] == 2 assert op["configs"]["interpolation_is_log"] is False -def test_export_load_op_card(tmp_path, cd): +def test_dump_load_op_card(tmp_path, cd): with cd(tmp_path): - op = g_o.gen_op_card([100], name="debug_op") - g_o.export_op_card("debug_op_two", op) - op_loaded = g_o.import_op_card("debug_op.yaml") - op_two_loaded = g_o.import_op_card("debug_op_two.yaml") + op = oc.generate([100], name="debug_op") + oc.dump("debug_op_two", op) + op_loaded = oc.load("debug_op.yaml") + op_two_loaded = oc.load("debug_op_two.yaml") for key in op.keys(): assert op[key] == op_loaded[key] == op_two_loaded[key] diff --git a/tests/ekobox/test_theory_card.py b/tests/ekobox/test_theory_card.py index 322a01e95..7b1f55ead 100644 --- a/tests/ekobox/test_theory_card.py +++ b/tests/ekobox/test_theory_card.py @@ -4,7 +4,7 @@ from ekobox import theory_card as tc -def test_gen_theory_card(): +def test_generate_theory_card(): theory = tc.generate(0, 1.0) assert theory["PTO"] == 0 assert theory["Q0"] == 1.0 @@ -18,7 +18,7 @@ def test_gen_theory_card(): assert theory["mb"] == 132.3 -def test_export_load_theory_card(tmp_path, cd): +def test_dump_load_theory_card(tmp_path, cd): with cd(tmp_path): theory = tc.generate(2, 12.3, name="debug_theory") tc.dump("debug_theory_two", theory) diff --git a/tests/ekobox/test_utils.py b/tests/ekobox/test_utils.py index 001ec0c68..ccf6198be 100644 --- a/tests/ekobox/test_utils.py +++ b/tests/ekobox/test_utils.py @@ -3,14 +3,14 @@ import pytest import eko -from ekobox import gen_op as g_o +from ekobox import operators_card as oc from ekobox import theory_card as tc from ekobox import utils def test_ekos_product(): # Generating two ekos - op1 = g_o.gen_op_card( + op1 = oc.generate( [60.0, 80.0, 100.0], update={ "xgrid": [0.1, 0.5, 1.0], @@ -19,7 +19,7 @@ def test_ekos_product(): ) theory1 = tc.generate(0, 5.0) - op2 = g_o.gen_op_card( + op2 = oc.generate( [80.0, 100.0, 120.0], update={ "xgrid": [0.1, 0.5, 1.0], From 0d7c49181feaf69f6f0429aca2f8f4b841f64108 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 25 Mar 2022 14:52:09 +0100 Subject: [PATCH 046/148] Apply some review suggestions --- src/eko/member.py | 2 +- src/ekobox/evol_pdf.py | 15 +++++++++++++++ src/ekobox/genpdf/__init__.py | 2 +- src/ekobox/genpdf/load.py | 2 +- src/ekobox/info_file.py | 2 +- src/ekobox/operators_card.py | 4 ++-- src/ekobox/theory_card.py | 4 ++-- 7 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/eko/member.py b/src/eko/member.py index e9f3b4576..f97c8cca1 100644 --- a/src/eko/member.py +++ b/src/eko/member.py @@ -243,7 +243,7 @@ def operator_multiply(left, right, operation): Returns ------- - dict : + dict new operator members dictionary """ # prepare paths diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index 55c622777..8504c2462 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -96,4 +96,19 @@ def evolve_pdfs( def ekofileid(theory_card, operators_card): + """ + Return a common filename composed by the hashes. + + Parameters + ---------- + theory_card : dict + theory card + operators_card : dict + operators card + + Returns + ------- + str + file name + """ return f"o{operators_card['hash'][:6]}_t{theory_card['hash'][:6]}.tar" diff --git a/src/ekobox/genpdf/__init__.py b/src/ekobox/genpdf/__init__.py index ebaf696b1..4df6caf96 100644 --- a/src/ekobox/genpdf/__init__.py +++ b/src/ekobox/genpdf/__init__.py @@ -239,7 +239,7 @@ def generate_block(xfxQ2, xgrid, Q2grid, pids): Returns ------- - dict : + dict PDF block """ block = dict(Q2grid=Q2grid, pids=pids, xgrid=xgrid) diff --git a/src/ekobox/genpdf/load.py b/src/ekobox/genpdf/load.py index f52f8b0e3..de5322288 100644 --- a/src/ekobox/genpdf/load.py +++ b/src/ekobox/genpdf/load.py @@ -23,7 +23,7 @@ def load_info_from_file(pdfset_name): Returns ------- - dict : + dict info dictionary """ import lhapdf # pylint: disable=import-error, import-outside-toplevel diff --git a/src/ekobox/info_file.py b/src/ekobox/info_file.py index 626b52b67..668ffdd5c 100644 --- a/src/ekobox/info_file.py +++ b/src/ekobox/info_file.py @@ -22,7 +22,7 @@ def build(theory_card, operators_card, num_members, info_update): Returns ------- - dict : + dict info file in lhapdf format """ template_info = copy.deepcopy(load.template_info) diff --git a/src/ekobox/operators_card.py b/src/ekobox/operators_card.py index b11b1f455..49de99240 100644 --- a/src/ekobox/operators_card.py +++ b/src/ekobox/operators_card.py @@ -26,7 +26,7 @@ def generate(Q2grid, update=None, name=None): Returns ------- - dict : + dict operators card """ # Constructing the dictionary with some default value @@ -77,7 +77,7 @@ def load(path): Returns ------- - dict : + dict operators card """ with open(path, "r", encoding="utf-8") as o: diff --git a/src/ekobox/theory_card.py b/src/ekobox/theory_card.py index ee9c71c3c..76118a3c9 100644 --- a/src/ekobox/theory_card.py +++ b/src/ekobox/theory_card.py @@ -23,7 +23,7 @@ def generate(pto, initial_scale, update=None, name=None): Returns ------- - dict : + dict theory card """ # Constructing the dictionary with some default values @@ -72,7 +72,7 @@ def load(path): Returns ------- - dict : + dict theory card """ with open(path, "r", encoding="utf-8") as o: From 0b1be6d9c7f08d934777d573a49e0b03fdd2754b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 1 Apr 2022 11:31:40 +0200 Subject: [PATCH 047/148] Remove bleeding edge type declaration syntax --- src/eko/runcards.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/runcards.py b/src/eko/runcards.py index e93fecc89..b9be63c4c 100644 --- a/src/eko/runcards.py +++ b/src/eko/runcards.py @@ -9,4 +9,4 @@ class TheoryCard: @dataclass class OperatorCard: - xgrid: list[float] + xgrid: list From f2f12b81a7a417df48745e1b3f0954e1ad082970 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 5 Apr 2022 19:01:19 +0200 Subject: [PATCH 048/148] Add typing, progress on benchmark failure --- src/eko/output/legacy.py | 87 +++++++++++++++++++++--------------- src/eko/output/manipulate.py | 1 + src/eko/runner.py | 3 +- 3 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index deefa7fd7..ec3cc9418 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- import dataclasses import io +import os import pathlib import tarfile import tempfile +from typing import Optional, TextIO, Union import lz4.frame import numpy as np @@ -13,9 +15,8 @@ from . import struct -def get_raw(eko, binarize=True, skip_q2_grid=False): - """ - Serialize result as dict/YAML. +def get_raw(eko: struct.EKO, binarize: bool = True, skip_q2_grid: bool = False): + """Serialize result as dict/YAML. This maps the original numpy matrices to lists. @@ -56,66 +57,76 @@ def get_raw(eko, binarize=True, skip_q2_grid=False): return out -def dump_yaml(obj, stream=None, binarize=True, skip_q2_grid=False): - """ - Serialize result as YAML. +def dump_yaml( + obj: struct.EKO, + stream: Optional[TextIO] = None, + binarize: bool = True, + skip_q2_grid: bool = False, +): + """Serialize result as YAML. Parameters ---------- - stream : None or stream - if given, dump is written on it - binarize : bool - dump in binary format (instead of list format) - skip_q2_grid : bool - avoid dumping Q2grid (i.e. the actual operators) into the yaml - file (default: ``False``) + stream : None or stream + if given, dump is written on it + binarize : bool + dump in binary format (instead of list format) + skip_q2_grid : bool + avoid dumping Q2grid (i.e. the actual operators) into the yaml + file (default: ``False``) Returns ------- - dump : any - result of dump(output, stream), i.e. a string, if no stream is given or - Null, if written successfully to stream + dump : any + result of dump(output, stream), i.e. a string, if no stream is given or + Null, if written successfully to stream + """ # TODO explicitly silence yaml out = get_raw(obj, binarize, skip_q2_grid=skip_q2_grid) return yaml.dump(out, stream) -def dump_yaml_to_file(obj, filename, binarize=True, skip_q2_grid=False): - """ - Writes YAML representation to a file. +def dump_yaml_to_file( + obj: struct.EKO, + filename: Union[str, os.PathLike], + binarize: bool = True, + skip_q2_grid: bool = False, +): + """Writes YAML representation to a file. Parameters ---------- - filename : str - target file name - binarize : bool - dump in binary format (instead of list format) - skip_q2_grid : bool - avoid dumping Q2grid (i.e. the actual operators) into the yaml - file (default: ``False``) + filename : str + target file name + binarize : bool + dump in binary format (instead of list format) + skip_q2_grid : bool + avoid dumping Q2grid (i.e. the actual operators) into the yaml + file (default: ``False``) Returns ------- - ret : any - result of dump(output, stream), i.e. Null if written successfully + ret : any + result of dump(output, stream), i.e. Null if written successfully + """ with open(filename, "w", encoding="utf-8") as f: ret = dump_yaml(obj, f, binarize, skip_q2_grid=skip_q2_grid) return ret -def dump_tar(obj, tarname): - """ - Writes representation into a tar archive containing: +def dump_tar(obj: struct.EKO, tarname: Union[str, os.PathLike]): + """Writes representation into a tar archive containing: - metadata (in YAML) - operator (in numpy ``.npy`` format) Parameters ---------- - tarname : str - target file name + tarname : str + target file name + """ tarpath = pathlib.Path(tarname) if tarpath.suffix != ".tar": @@ -150,13 +161,13 @@ def dump_tar(obj, tarname): tar.add(tmpdir, arcname=tarpath.stem) -def load_yaml(stream, skip_q2_grid=False): +def load_yaml(stream: TextIO, skip_q2_grid=False) -> struct.EKO: """ Load YAML representation from stream Parameters ---------- - stream : any + stream : TextIO source stream skip_q2_grid : bool avoid loading Q2grid (i.e. the actual operators) from the yaml @@ -190,7 +201,9 @@ def load_yaml(stream, skip_q2_grid=False): return struct.EKO.from_dict(obj) -def load_yaml_from_file(filename, skip_q2_grid=False): +def load_yaml_from_file( + filename: Union[str, os.PathLike], skip_q2_grid: bool = False +) -> struct.EKO: """ Load YAML representation from file @@ -213,7 +226,7 @@ def load_yaml_from_file(filename, skip_q2_grid=False): return obj -def load_tar(tarname): +def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: """ Load tar representation from file (compliant with :meth:`dump_tar` output). diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index f9e8b14cb..0f78496e9 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -23,6 +23,7 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): inputgrid : None or list xgrid for the input """ + __import__("pdb").set_trace() # calling with no arguments is an error if targetgrid is None and inputgrid is None: raise ValueError("Nor inputgrid nor targetgrid was given") diff --git a/src/eko/runner.py b/src/eko/runner.py index 7947eebea..76f2fec8a 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -82,7 +82,7 @@ def __init__(self, theory_card, operators_card): self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref), **operators_card)) - def get_output(self): + def get_output(self) -> EKO: """ Collects all data for output (to run the evolution) @@ -97,6 +97,7 @@ def get_output(self): self.out.rotations.inputgrid = self.out.xgrid.grid self.out.rotations.targetgrid = self.out.xgrid.grid for final_scale, op in self.op_grid.compute().items(): + __import__("pdb").set_trace() self.out[float(final_scale)] = op # reshape xgrid From 5d4aab21f23724526d92c109edccabf551fdbc33 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 13:04:57 +0200 Subject: [PATCH 049/148] Simplify and update runner layout --- src/eko/output/manipulate.py | 19 +++++++-- src/eko/output/struct.py | 50 ++++++++++++++++++---- src/eko/runner.py | 81 +++++++++++++++--------------------- tests/conftest.py | 1 + 4 files changed, 93 insertions(+), 58 deletions(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 0f78496e9..cba8409fb 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -1,16 +1,23 @@ # -*- coding: utf-8 -*- import logging import warnings +from typing import Optional import numpy as np from .. import basis_rotation as br from .. import interpolation +from .struct import EKO logger = logging.getLogger(__name__) -def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): +def xgrid_reshape( + eko: EKO, + targetgrid: Optional[np.ndarray] = None, + inputgrid: Optional[np.ndarray] = None, + inplace: bool = True, +): """ Changes the operators to have in the output targetgrid and/or in the input inputgrid. @@ -23,7 +30,6 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): inputgrid : None or list xgrid for the input """ - __import__("pdb").set_trace() # calling with no arguments is an error if targetgrid is None and inputgrid is None: raise ValueError("Nor inputgrid nor targetgrid was given") @@ -86,7 +92,12 @@ def xgrid_reshape(eko, targetgrid=None, inputgrid=None, inplace=True): elem.error = errs -def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): +def flavor_reshape( + eko: EKO, + targetbasis: Optional[np.ndarray] = None, + inputbasis: Optional[np.ndarray] = None, + inplace: bool = True, +): """ Changes the operators to have in the output targetbasis and/or in the input inputbasis. @@ -147,7 +158,7 @@ def flavor_reshape(eko, targetbasis=None, inputbasis=None, inplace=True): eko.rotations.targetpids = [0] * len(eko.rotations.targetpids) -def to_evol(eko, source=True, target=False, inplace=True): +def to_evol(eko: EKO, source: bool = True, target: bool = False, inplace: bool = True): """ Rotate the operator into evolution basis. diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 021344763..b6db7ff0b 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -4,6 +4,7 @@ import numpy as np +from .. import basis_rotation as br from .. import interpolation from .. import version as vmod @@ -64,6 +65,12 @@ class Rotations(DictLike): targetpids: Optional[np.ndarray] = None inputpids: Optional[np.ndarray] = None + @classmethod + def default(cls, xgrid: interpolation.XGrid, pids: np.ndarray): + return cls( + targetgrid=xgrid.raw, inputgrid=xgrid.raw, targetpids=pids, inputpids=pids + ) + @dataclass class EKO: @@ -78,6 +85,7 @@ class EKO: configs: Configs rotations: Rotations debug: Debug + pids: np.ndarray = np.array(br.flavor_basis_pids) version: str = vmod.__version__ data_version: str = vmod.__data_version__ @@ -107,14 +115,42 @@ def Q2grid(self): return np.array(list(self._operators)) @classmethod - def from_dict(cls, dictionary): + def from_dict(cls, runcard: dict): + """Make structure from runcard-like dictionary. + + This constructor is made to be used with loaded runcards, in order to + minimize the amount of code needed to init a new object (you just to + load the runcard and call this function). + + Note + ---- + An object is initialized with no rotations, since the role of rotations + is to keep the current state of the output object after manipulations + happened. + Since a new object is here in the process of being created, no rotation + has to be logged. + + Parameters + ---------- + runcard : dict + a dictionary containing the runcard's content + + Returns + ------- + EKO + the generated structure + + """ + xgrid = interpolation.XGrid(runcard["xgrid"]) + pids = runcard.get("pids", cls.pids) return cls( - xgrid=interpolation.XGrid(dictionary["xgrid"]), - Q02=dictionary["Q0"] ** 2, - _operators={q2: None for q2 in dictionary["Q2grid"]}, - configs=Configs.from_dict(dictionary["configs"]), - rotations=Rotations.from_dict(dictionary.get("rotations", {})), - debug=Debug.from_dict(dictionary.get("debug", {})), + xgrid=xgrid, + pids=pids, + Q02=runcard["Q0"] ** 2, + _operators={q2: None for q2 in runcard["Q2grid"]}, + configs=Configs.from_dict(runcard["configs"]), + rotations=Rotations.default(xgrid, pids), + debug=Debug.from_dict(runcard.get("debug", {})), ) @classmethod diff --git a/src/eko/runner.py b/src/eko/runner.py index 76f2fec8a..69128b578 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -4,14 +4,15 @@ """ import copy import logging +from typing import Optional import numpy as np -from . import basis_rotation as br from . import compatibility, interpolation, msbar_masses from .couplings import Couplings from .evolution_operator.grid import OperatorGrid from .output import EKO, manipulate +from .output.struct import Rotations from .thresholds import ThresholdsAtlas logger = logging.getLogger(__name__) @@ -39,7 +40,7 @@ class Runner: o888ooooood8 o888o o888o `Y8bood8P' """ - def __init__(self, theory_card, operators_card): + def __init__(self, theory_card: dict, operators_card: dict): self.out = EKO() new_theory, new_operators = compatibility.update(theory_card, operators_card) @@ -59,72 +60,58 @@ def __init__(self, theory_card, operators_card): # strong coupling sc = Couplings.from_dict(new_theory, masses=masses) # setup operator grid - self.op_grid = OperatorGrid.from_dict( - new_theory, - new_operators, - tc, - sc, - bfd, - ) + self.op_grid = OperatorGrid.from_dict(new_theory, new_operators, tc, sc, bfd) + # save bases manipulations for a post processing step rot = operators_card.get("rotations", {}) - self.post_process = dict( - inputgrid=rot.get("inputgrid", bfd.xgrid.raw), - targetgrid=rot.get("targetgrid", bfd.xgrid.raw), - inputbasis=rot.get("inputbasis"), - targetbasis=rot.get("targetbasis"), - ) - - if "rotations" not in operators_card: - operators_card["rotations"] = {} - operators_card["rotations"]["inputgrid"] = bfd.xgrid.raw - operators_card["rotations"]["targetgrid"] = bfd.xgrid.raw + self.post_process = { + key: rot.get(key, None) + for key in ("inputgrid", "targetgrid", "inputpids", "targetpids") + } self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref), **operators_card)) def get_output(self) -> EKO: - """ - Collects all data for output (to run the evolution) + """Run evolution and generate output operator. + + Two steps are applied sequentially: + + 1. evolution is performed, computing the evolution operator in an + internal flavor and x-space basis + 2. bases manipulations specified in the runcard are applied Returns ------- - ret : eko.output.EKO - output instance + EKO + output instance + """ # add all operators - self.out.rotations.inputpids = np.array(br.flavor_basis_pids) - self.out.rotations.targetpids = np.array(br.flavor_basis_pids) - self.out.rotations.inputgrid = self.out.xgrid.grid - self.out.rotations.targetgrid = self.out.xgrid.grid for final_scale, op in self.op_grid.compute().items(): - __import__("pdb").set_trace() self.out[float(final_scale)] = op + def similar_to_none(name: str) -> Optional[np.ndarray]: + grid = self.post_process[name] + + if grid is None or np.allclose(grid, getattr(Rotations, name), atol=1e-12): + return None + + return np.array(grid) + # reshape xgrid - inputgrid = ( - self.post_process["inputgrid"] - if self.post_process["inputgrid"] is not self.out.xgrid.grid - else None - ) - targetgrid = ( - self.post_process["targetgrid"] - if self.post_process["targetgrid"] is not self.out.xgrid.grid - else None - ) + inputgrid = similar_to_none("inputgrid") + targetgrid = similar_to_none("targetgrid") if inputgrid is not None or targetgrid is not None: manipulate.xgrid_reshape( self.out, targetgrid=targetgrid, inputgrid=inputgrid ) # reshape flavors - inputbasis = self.post_process["inputbasis"] - if inputbasis is not None: - inputbasis = np.array(inputbasis) - targetbasis = self.post_process["targetbasis"] - if targetbasis is not None: - targetbasis = np.array(targetbasis) - if inputbasis is not None or targetbasis is not None: + inputpids = similar_to_none("inputpids") + targetpids = similar_to_none("targetpids") + if inputpids is not None or targetpids is not None: manipulate.flavor_reshape( - self.out, targetbasis=targetbasis, inputbasis=inputbasis + self.out, targetbasis=targetpids, inputbasis=inputpids ) + return copy.deepcopy(self.out) diff --git a/tests/conftest.py b/tests/conftest.py index 4969506dd..37fb098b2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -65,6 +65,7 @@ def fake_output(self): Q2grid = self.mk_g([q2_out], len(pids), len(xgrid)) d = dict( xgrid=xgrid, + pids=pids, rotations=dict( targetgrid=xgrid, inputgrid=xgrid, From ad6366f8d9f063845ee246f28c448729d0b34fbf Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 18 Jul 2022 13:05:40 +0200 Subject: [PATCH 050/148] Check grids sizes before comparison --- src/eko/runner.py | 6 ++++-- tests/conftest.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/eko/runner.py b/src/eko/runner.py index 69128b578..ac3483eef 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -12,7 +12,6 @@ from .couplings import Couplings from .evolution_operator.grid import OperatorGrid from .output import EKO, manipulate -from .output.struct import Rotations from .thresholds import ThresholdsAtlas logger = logging.getLogger(__name__) @@ -93,7 +92,10 @@ def get_output(self) -> EKO: def similar_to_none(name: str) -> Optional[np.ndarray]: grid = self.post_process[name] - if grid is None or np.allclose(grid, getattr(Rotations, name), atol=1e-12): + default = self.out.xgrid.grid if "grid" in name else self.out.pids + if grid is None or ( + len(grid) == default.size and np.allclose(grid, default, atol=1e-12) + ): return None return np.array(grid) diff --git a/tests/conftest.py b/tests/conftest.py index 37fb098b2..70c08000c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,7 +11,7 @@ @pytest.fixture def cd(): - # thanks https://stackoverflow.com/questions/431684/how-do-i-change-the-working-directory-in-python/24176022#24176022 + # thanks https://stackoverflow.com/a/24176022/8653979 @contextmanager def wrapped(newdir): prevdir = os.getcwd() From 976faefc245ce1fbb519a59d4f75676dfcccd071 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 7 Apr 2022 17:59:02 +0200 Subject: [PATCH 051/148] Combine ekos with chosen precision --- src/eko/output/struct.py | 10 ++++++++++ src/ekobox/utils.py | 16 +++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index b6db7ff0b..f1578bf95 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -114,6 +114,16 @@ def items(self): def Q2grid(self): return np.array(list(self._operators)) + def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: + q2s = self.Q2grid + close = q2s[np.isclose(q2, q2s, rtol=rtol, atol=atol)] + + if close.size == 1: + return close[0] + if close.size == 0: + return None + raise ValueError(f"Multiple values of Q2 have been found close to {q2}") + @classmethod def from_dict(cls, runcard: dict): """Make structure from runcard-like dictionary. diff --git a/src/ekobox/utils.py b/src/ekobox/utils.py index 6538a59d1..9fb66d3a9 100644 --- a/src/ekobox/utils.py +++ b/src/ekobox/utils.py @@ -8,7 +8,9 @@ # TODO: add a control on the theory (but before we need to implement another # kind of output which includes the theory and operator runcards) -def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: +def ekos_product( + eko_ini: EKO, eko_fin: EKO, rtol: float = 1e-6, atol: float = 1e-10, in_place=True +) -> EKO: """Returns the product of two ekos Parameters @@ -17,6 +19,10 @@ def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: initial eko operator eko_fin : eko.output.Output final eko operator + rtol : float + relative tolerance on Q2, used to check compatibility + atol : float + absolute tolerance on Q2, used to check compatibility in_place : bool do operation in place, modifying input arrays @@ -26,14 +32,14 @@ def ekos_product(eko_ini: EKO, eko_fin: EKO, in_place=True) -> EKO: eko operator """ - # TODO: check if it's close, instead of checking identity - if eko_fin.Q02 not in eko_ini.Q2grid: + q2match = eko_ini.approx(eko_fin.Q02, rtol=rtol, atol=atol) + if q2match is None: raise ValueError( "Initial Q2 of final eko operator does not match any final Q2 in" " the initial eko operator" ) - ope1 = eko_ini[eko_fin.Q02].operator.copy() - ope1_error = eko_ini[eko_fin.Q02].error.copy() + ope1 = eko_ini[q2match].operator.copy() + ope1_error = eko_ini[q2match].error.copy() ope2_dict = {} ope2_error_dict = {} From 8157ea86f427a624cda3b62b428e035d26be5a4e Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 7 Apr 2022 17:59:40 +0200 Subject: [PATCH 052/148] Remove annotation about improving deprecated interface --- src/eko/output/legacy.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index ec3cc9418..28eae1895 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -82,7 +82,6 @@ def dump_yaml( Null, if written successfully to stream """ - # TODO explicitly silence yaml out = get_raw(obj, binarize, skip_q2_grid=skip_q2_grid) return yaml.dump(out, stream) From 59f37b24b1d693947841c209a0fb4f0bc00f7bc2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 7 Apr 2022 19:23:23 +0200 Subject: [PATCH 053/148] Add loading semantics to operator objects --- src/eko/output/struct.py | 69 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index f1578bf95..7338d40ff 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- +import logging +import os +import pathlib +import tempfile +import typing from dataclasses import dataclass, fields from typing import Dict, Literal, Optional @@ -8,6 +13,8 @@ from .. import interpolation from .. import version as vmod +logger = logging.getLogger(__name__) + class DictLike: def __init__(self, **kwargs): @@ -90,16 +97,36 @@ class EKO: data_version: str = vmod.__data_version__ def __iter__(self): - return iter(self._operators) + """Iterate operators, with minimal load. + + Pay attention, this iterator: + + - is similar to ``dict.items()``, since it returns tuples of ``(q2, + operator)`` + - is not a read-only operation from the point of view of the in-memory + object (since the final result after iteration is no operator loaded) + - but it is a read-only operation from the point of view of the + permanent object on-disk + + Yields + ------ + tuple + couples of ``(q2, operator)``, loaded immediately before, unloaded + immediately after + + """ + for q2 in self.Q2grid: + yield q2, self[q2] + del self[q2] - def __contains__(self, q2): + def __contains__(self, q2: float) -> bool: return q2 in self._operators - def __getitem__(self, q2): + def __getitem__(self, q2: float): # TODO: autoload return self._operators[q2] - def __setitem__(self, q2, op): + def __setitem__(self, q2: float, op: Operator): # TODO: autodump if isinstance(op, dict): op = Operator.from_dict(op) @@ -107,6 +134,33 @@ def __setitem__(self, q2, op): raise ValueError("Only operators can be stored.") self._operators[q2] = op + def __delitem__(self, q2: float): + """Drop operator from memory. + + This method only drops the operator from memory, and it's not expected + to do anything else. + + Autosave is done on set, and explicit saves are performed by the + computation functions. + + If a further explicit save is required, repeat explicit assignment:: + + eko[q2] = eko[q2] + + This is only useful if the operator has been mutated in place, that in + general should be avoided, since the operator should only be the result + of a full computation or a library manipulation. + + + Parameters + ---------- + q2 : float + the value of :math:`Q^2` for which the corresponding operator + should be dropped + + """ + self._operators[q2] = None + def items(self): return self._operators.items() @@ -184,3 +238,10 @@ def raw(self): rotations=self.rotations.raw, debug=self.debug.raw, ) + + def close(self): + for q2 in self.Q2grid: + del self[q2] + + def __del__(self): + self.close() From b5ff85a0ef4573664106f9636b785e0875478bc3 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 7 Apr 2022 19:23:37 +0200 Subject: [PATCH 054/148] Bind operator object to a given path --- src/eko/output/struct.py | 47 +++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 7338d40ff..154bb9fc5 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -81,14 +81,41 @@ def default(cls, xgrid: interpolation.XGrid, pids: np.ndarray): @dataclass class EKO: - """ - Wrapper for the output to help with application - to PDFs and dumping to file. + """Operator interface. + + This class offers an interface to an abstract operator, between memory and + disk. + + An actual operator might be arbitrarily huge, and in particular size + limitations in memory are far more strict than on disk. + Since manually managing, for each application, the burden of off-loading + part of the operator might be hard and occasionally not possible (without a + clear picture of the internals), the library itself offers this facility. + + In particular, the data format on disk has a complete specification, and + can hold a full operator independently of the loading procedure. + In order to accomplish the former goal, the remaining task of partial + loading is done by this class (for the Python library, other + implementations are possible and encouraged). + + For this reason, a core component of an :cls:`EKO` object is a path, + referring to the location on disk of the corresponding operator. + Any :cls:`EKO` has an associated path: + + - for the computed object, it corresponds to the path where the actual + result of the computation is already saved + - for a new object, it is the path at which any result of final or + intermediate computation is stored, as soon as it is produced + + The computation can be stopped at any time, without the loss of any of the + intermediate results. + """ + path: pathlib.Path + _operators: Dict[float, Optional[Operator]] xgrid: interpolation.XGrid Q02: float - _operators: Dict[float, Optional[Operator]] configs: Configs rotations: Rotations debug: Debug @@ -96,6 +123,10 @@ class EKO: version: str = vmod.__version__ data_version: str = vmod.__data_version__ + def __post_init__(self): + if self.path.suffix != ".tar": + raise ValueError("Not a valid path") + def __iter__(self): """Iterate operators, with minimal load. @@ -205,9 +236,14 @@ def from_dict(cls, runcard: dict): the generated structure """ + path = pathlib.Path(runcard.get("path", tempfile.mkstemp(suffix=".tar")[1])) xgrid = interpolation.XGrid(runcard["xgrid"]) pids = runcard.get("pids", cls.pids) + + logger.info(f"New operator created at path '{path}'") + return cls( + path=path, xgrid=xgrid, pids=pids, Q02=runcard["Q0"] ** 2, @@ -218,8 +254,9 @@ def from_dict(cls, runcard: dict): ) @classmethod - def load(cls, path): + def load(cls, path: typing.Union[str, os.PathLike]): return cls( + path=pathlib.Path(path), xgrid=None, Q02=None, _operators={q2: None for q2 in ()}, From 0718d15488b769c69326b1a9ee879925fb5b8bc3 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 7 Apr 2022 19:57:53 +0200 Subject: [PATCH 055/148] Attempt valid tarfile creation... fail --- src/eko/output/struct.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 154bb9fc5..c695c4e3e 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -2,6 +2,7 @@ import logging import os import pathlib +import tarfile import tempfile import typing from dataclasses import dataclass, fields @@ -125,7 +126,9 @@ class EKO: def __post_init__(self): if self.path.suffix != ".tar": - raise ValueError("Not a valid path") + raise ValueError("Not a valid path for an EKO") + if not tarfile.is_tarfile(self.path): + raise ValueError("EKO: the corresponding file is not a valid tar archive") def __iter__(self): """Iterate operators, with minimal load. @@ -237,6 +240,10 @@ def from_dict(cls, runcard: dict): """ path = pathlib.Path(runcard.get("path", tempfile.mkstemp(suffix=".tar")[1])) + path.unlink() + with tarfile.open(path, mode="x") as tar: + tar.addfile(tarfile.TarInfo("metadata.yaml")) + xgrid = interpolation.XGrid(runcard["xgrid"]) pids = runcard.get("pids", cls.pids) From d3efb9eeb003788e699fa31d34dbad9006355739 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 14:09:13 +0200 Subject: [PATCH 056/148] Simplify output tests, removing unittest framework --- tests/eko/test_output.py | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index cad0993ef..980aa5bcd 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -4,7 +4,6 @@ import pathlib import shutil import tempfile -from unittest import mock import numpy as np import pytest @@ -30,7 +29,7 @@ def chk_keys(a, b): class TestLegacy: - def test_io(self, fake_output): + def test_io(self, fake_output, tmp_path): # create object o1 = output.EKO.from_dict(fake_output) for q2, op in fake_output["Q2grid"].items(): @@ -44,26 +43,16 @@ def test_io(self, fake_output): np.testing.assert_almost_equal(o1.xgrid.raw, fake_output["xgrid"]) np.testing.assert_almost_equal(o2.xgrid.raw, fake_output["xgrid"]) # fake output files - m_out = mock.mock_open(read_data="") - with mock.patch("builtins.open", m_out) as mock_file: - fn = "test.yaml" - legacy.dump_yaml_to_file(o1, fn) - mock_file.assert_called_with(fn, "w", encoding="utf-8") + fpyaml = tmp_path / "test.yaml" + legacy.dump_yaml_to_file(o1, fpyaml) # fake input file - stream.seek(0) - m_in = mock.mock_open(read_data=stream.getvalue()) - with mock.patch("builtins.open", m_in) as mock_file: - fn = "test.yaml" - o3 = legacy.load_yaml_from_file(fn) - mock_file.assert_called_with(fn, encoding="utf-8") - np.testing.assert_almost_equal(o3.xgrid.raw, fake_output["xgrid"]) + o3 = legacy.load_yaml_from_file(fpyaml) + np.testing.assert_almost_equal(o3.xgrid.raw, fake_output["xgrid"]) # repeat for tar - fn = "test.tar" - with tempfile.TemporaryDirectory() as folder: - fp = pathlib.Path(folder) / fn - legacy.dump_tar(o1, fp) - o4 = legacy.load_tar(fp) - np.testing.assert_almost_equal(o4.xgrid.raw, fake_output["xgrid"]) + fptar = tmp_path / "test.tar" + legacy.dump_tar(o1, fptar) + o4 = legacy.load_tar(fptar) + np.testing.assert_almost_equal(o4.xgrid.raw, fake_output["xgrid"]) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): legacy.dump_tar(o1, fn) From b7ff70712d8de037780733c44009e2283f750f0d Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 14:09:38 +0200 Subject: [PATCH 057/148] Bootstrap new directory layout --- src/eko/output/struct.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index c695c4e3e..072ad2f05 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -2,6 +2,7 @@ import logging import os import pathlib +import shutil import tarfile import tempfile import typing @@ -9,6 +10,7 @@ from typing import Dict, Literal, Optional import numpy as np +import yaml from .. import basis_rotation as br from .. import interpolation @@ -241,8 +243,19 @@ def from_dict(cls, runcard: dict): """ path = pathlib.Path(runcard.get("path", tempfile.mkstemp(suffix=".tar")[1])) path.unlink() - with tarfile.open(path, mode="x") as tar: - tar.addfile(tarfile.TarInfo("metadata.yaml")) + with tempfile.TemporaryDirectory() as td: + td = pathlib.Path(td) + # TODO: replace with actual runcards + (td / "metadata.yaml").write_text(yaml.dump(runcard), encoding="utf-8") + (td / "recipes").mkdir() + (td / "parts").mkdir() + (td / "operators").mkdir() + + with tarfile.open(path, mode="w") as tar: + for element in td.glob("*"): + tar.add(element, arcname=element.name) + + shutil.rmtree(td) xgrid = interpolation.XGrid(runcard["xgrid"]) pids = runcard.get("pids", cls.pids) From 3a15ea322a5e3769ba1cbd0d38b02d0c6352f8c0 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 15:33:39 +0200 Subject: [PATCH 058/148] Split operator file bootstraping in separate function --- src/eko/output/struct.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 072ad2f05..cd4a6222c 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -214,6 +214,26 @@ def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: return None raise ValueError(f"Multiple values of Q2 have been found close to {q2}") + @staticmethod + def bootstrap(tdir: typing.Union[str, os.PathLike], theory: dict, operator: dict): + tdir = pathlib.Path(tdir) + (tdir / "theory.yaml").write_text(yaml.dump(theory), encoding="utf-8") + (tdir / "operator.yaml").write_text(yaml.dump(operator), encoding="utf-8") + (tdir / "recipes").mkdir() + (tdir / "parts").mkdir() + (tdir / "operators").mkdir() + + def update_runcard(self, runcard: dict, which: str = "theory"): + names = ("theory", "operator") + if which not in names: + raise ValueError(f"Runcard '{which}' not available, choose in {names}.") + + with tarfile.open(self.path, mode="w") as tar: + with tempfile.NamedTemporaryFile() as tmp: + tmppath = pathlib.Path(tmp.name) + tmppath.write_text(yaml.dump(runcard), encoding="utf-8") + tar.add(tmppath, arcname=f"{which}.yaml") + @classmethod def from_dict(cls, runcard: dict): """Make structure from runcard-like dictionary. @@ -246,10 +266,7 @@ def from_dict(cls, runcard: dict): with tempfile.TemporaryDirectory() as td: td = pathlib.Path(td) # TODO: replace with actual runcards - (td / "metadata.yaml").write_text(yaml.dump(runcard), encoding="utf-8") - (td / "recipes").mkdir() - (td / "parts").mkdir() - (td / "operators").mkdir() + cls.bootstrap(td, theory={}, operator=runcard) with tarfile.open(path, mode="w") as tar: for element in td.glob("*"): From 33b70d07e4aebdc4ebc3769d189284562063214a Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 15:51:56 +0200 Subject: [PATCH 059/148] Add methods to retrieve runcards --- src/eko/output/struct.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index cd4a6222c..a3576f6a3 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -223,16 +223,28 @@ def bootstrap(tdir: typing.Union[str, os.PathLike], theory: dict, operator: dict (tdir / "parts").mkdir() (tdir / "operators").mkdir() - def update_runcard(self, runcard: dict, which: str = "theory"): - names = ("theory", "operator") - if which not in names: - raise ValueError(f"Runcard '{which}' not available, choose in {names}.") - - with tarfile.open(self.path, mode="w") as tar: - with tempfile.NamedTemporaryFile() as tmp: - tmppath = pathlib.Path(tmp.name) - tmppath.write_text(yaml.dump(runcard), encoding="utf-8") - tar.add(tmppath, arcname=f"{which}.yaml") + def extract(self, filename: str) -> str: + with tarfile.open(self.path, "r") as tar: + fd = tar.extractfile(filename) + if fd is None: + raise ValueError( + f"The member '{filename}' is not a readable file inside EKO tar" + ) + content = fd.read().decode() + + return content + + @property + def theory(self) -> dict: + return yaml.safe_load(self.extract("theory.yaml")) + + @property + def theory_card(self) -> dict: + return self.theory + + @property + def operator_card(self) -> dict: + return yaml.safe_load(self.extract("theory.yaml")) @classmethod def from_dict(cls, runcard: dict): From a186b7fb5fe4111ea0bc333bb68678ede52fd0ba Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 16:37:47 +0200 Subject: [PATCH 060/148] Rename from_dict constructor --- src/eko/output/legacy.py | 4 +- src/eko/output/struct.py | 114 +++++++++++++++++++++++++----------- src/eko/runner.py | 4 +- tests/eko/test_output.py | 14 ++--- tests/ekomark/test_apply.py | 4 +- 5 files changed, 93 insertions(+), 47 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 28eae1895..536142f94 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -197,7 +197,7 @@ def load_yaml(stream: TextIO, skip_q2_grid=False) -> struct.EKO: v = np.frombuffer(lz4.frame.decompress(v)) v = v.reshape(len_tpids, len_tgrid, len_ipids, len_igrid) op[k] = v - return struct.EKO.from_dict(obj) + return struct.EKO.new(theory={}, operator=obj) def load_yaml_from_file( @@ -271,7 +271,7 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: operator_grid[q2] = dict(zip(grids.keys(), slices)) metadata["Q2grid"] = operator_grid - eko = struct.EKO.from_dict(metadata) + eko = struct.EKO.new(theory={}, operator=metadata) for q2, op in metadata["Q2grid"].items(): eko[q2] = op diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index a3576f6a3..618ca8906 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -18,6 +18,8 @@ logger = logging.getLogger(__name__) +PathLike = typing.Union[str, os.PathLike] + class DictLike: def __init__(self, **kwargs): @@ -215,7 +217,7 @@ def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: raise ValueError(f"Multiple values of Q2 have been found close to {q2}") @staticmethod - def bootstrap(tdir: typing.Union[str, os.PathLike], theory: dict, operator: dict): + def bootstrap(tdir: PathLike, theory: dict, operator: dict): tdir = pathlib.Path(tdir) (tdir / "theory.yaml").write_text(yaml.dump(theory), encoding="utf-8") (tdir / "operator.yaml").write_text(yaml.dump(operator), encoding="utf-8") @@ -223,8 +225,11 @@ def bootstrap(tdir: typing.Union[str, os.PathLike], theory: dict, operator: dict (tdir / "parts").mkdir() (tdir / "operators").mkdir() - def extract(self, filename: str) -> str: - with tarfile.open(self.path, "r") as tar: + @staticmethod + def extract(path: PathLike, filename: str) -> str: + path = pathlib.Path(path) + + with tarfile.open(path, "r") as tar: fd = tar.extractfile(filename) if fd is None: raise ValueError( @@ -232,11 +237,12 @@ def extract(self, filename: str) -> str: ) content = fd.read().decode() + __import__("pdb").set_trace() return content @property def theory(self) -> dict: - return yaml.safe_load(self.extract("theory.yaml")) + return yaml.safe_load(self.extract(self.path, "theory.yaml")) @property def theory_card(self) -> dict: @@ -244,10 +250,51 @@ def theory_card(self) -> dict: @property def operator_card(self) -> dict: - return yaml.safe_load(self.extract("theory.yaml")) + return yaml.safe_load(self.extract(self.path, "operator.yaml")) @classmethod - def from_dict(cls, runcard: dict): + def detached(cls, theory: dict, operator: dict, path: pathlib.Path): + """Build the in-memory object alone. + + Note + ---- + This constructor is meant for internal use, backing the usual ones (like + :meth:`new` or :meth:`load`), but it should not be used directly, since + it has no guarantee that the underlying path is valid, breaking the + object semantic. + + Parameters + ---------- + theory : dict + the theory card + operator : dict + the operator card + path : str or os.PathLike + the underlying path (it has to be a valid object, but it is not + guaranteed, see the note) + + Returns + ------- + EKO + the generated structure + + """ + xgrid = interpolation.XGrid(operator["xgrid"]) + pids = np.array(operator.get("pids", cls.pids)) + + return cls( + path=path, + xgrid=xgrid, + pids=pids, + Q02=operator["Q0"] ** 2, + _operators={q2: None for q2 in operator["Q2grid"]}, + configs=Configs.from_dict(operator["configs"]), + rotations=Rotations.default(xgrid, pids), + debug=Debug.from_dict(operator.get("debug", {})), + ) + + @classmethod + def new(cls, theory: dict, operator: dict, path: Optional[PathLike] = None): """Make structure from runcard-like dictionary. This constructor is made to be used with loaded runcards, in order to @@ -264,8 +311,13 @@ def from_dict(cls, runcard: dict): Parameters ---------- - runcard : dict - a dictionary containing the runcard's content + theory : dict + the theory card + operator : dict + the operator card + path : str or os.PathLike + the underlying path (if not provided, it is created in a temporary + path) Returns ------- @@ -273,12 +325,13 @@ def from_dict(cls, runcard: dict): the generated structure """ - path = pathlib.Path(runcard.get("path", tempfile.mkstemp(suffix=".tar")[1])) + path = pathlib.Path( + path if path is not None else tempfile.mkstemp(suffix=".tar")[1] + ) path.unlink() with tempfile.TemporaryDirectory() as td: td = pathlib.Path(td) - # TODO: replace with actual runcards - cls.bootstrap(td, theory={}, operator=runcard) + cls.bootstrap(td, theory=theory, operator=operator) with tarfile.open(path, mode="w") as tar: for element in td.glob("*"): @@ -286,38 +339,29 @@ def from_dict(cls, runcard: dict): shutil.rmtree(td) - xgrid = interpolation.XGrid(runcard["xgrid"]) - pids = runcard.get("pids", cls.pids) - + eko = cls.detached(theory, operator, path=path) logger.info(f"New operator created at path '{path}'") - - return cls( - path=path, - xgrid=xgrid, - pids=pids, - Q02=runcard["Q0"] ** 2, - _operators={q2: None for q2 in runcard["Q2grid"]}, - configs=Configs.from_dict(runcard["configs"]), - rotations=Rotations.default(xgrid, pids), - debug=Debug.from_dict(runcard.get("debug", {})), - ) + return eko @classmethod - def load(cls, path: typing.Union[str, os.PathLike]): - return cls( - path=pathlib.Path(path), - xgrid=None, - Q02=None, - _operators={q2: None for q2 in ()}, - configs=None, - rotations=None, - debug=None, - ) + def load(cls, path: PathLike): + path = pathlib.Path(path) + if not tarfile.is_tarfile(path): + raise ValueError("EKO: the corresponding file is not a valid tar archive") + + theory = yaml.safe_load(cls.extract(path, "theory.yaml")) + operator = yaml.safe_load(cls.extract(path, "operator.yaml")) + + eko = cls.detached(theory, operator, path=path) + logger.info(f"Operator loaded from path '{path}'") + return eko @property def raw(self): return dict( + path=str(self.path), xgrid=self.xgrid.tolist(), + pids=self.pids.tolist(), Q0=float(np.sqrt(self.Q02)), Q2grid=self.Q2grid, configs=self.configs.raw, diff --git a/src/eko/runner.py b/src/eko/runner.py index ac3483eef..7ad960b3e 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -68,7 +68,9 @@ def __init__(self, theory_card: dict, operators_card: dict): for key in ("inputgrid", "targetgrid", "inputpids", "targetpids") } - self.out = EKO.from_dict(dict(Q0=np.sqrt(tc.q2_ref), **operators_card)) + self.out = EKO.new( + theory=theory_card, operator=dict(Q0=np.sqrt(tc.q2_ref), **operators_card) + ) def get_output(self) -> EKO: """Run evolution and generate output operator. diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 980aa5bcd..6e0d9853c 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -31,7 +31,7 @@ def chk_keys(a, b): class TestLegacy: def test_io(self, fake_output, tmp_path): # create object - o1 = output.EKO.from_dict(fake_output) + o1 = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) # test streams @@ -60,7 +60,7 @@ def test_io(self, fake_output, tmp_path): def test_rename_issue81(self, fake_output): # https://github.com/N3PDF/eko/issues/81 # create object - o1 = output.EKO.from_dict(fake_output) + o1 = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) @@ -78,7 +78,7 @@ def test_rename_issue81(self, fake_output): def test_io_bin(self, fake_output): # create object - o1 = output.EKO.from_dict(fake_output) + o1 = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) # test streams @@ -95,7 +95,7 @@ class TestManipulate: def test_xgrid_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) - o1 = output.EKO.from_dict(fake_output) + o1 = output.EKO.new(theory={}, operator=fake_output) o1.xgrid = xg o1.rotations.targetgrid = xg o1.rotations.inputgrid = xg @@ -143,7 +143,7 @@ def test_xgrid_reshape(self, fake_output): def test_reshape_io(self, fake_output): # create object - o1 = output.EKO.from_dict(fake_output) + o1 = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) o2 = copy.deepcopy(o1) @@ -160,7 +160,7 @@ def test_reshape_io(self, fake_output): def test_flavor_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) - o1 = output.EKO.from_dict(fake_output) + o1 = output.EKO.new(theory={}, operator=fake_output) o1.xgrid = xg o1.rotations.targetgrid = xg o1.rotations.inputgrid = xg @@ -238,7 +238,7 @@ def test_to_evol(self, fake_factory): backward_inversion="exact", ), ) - o00 = output.EKO.from_dict(d) + o00 = output.EKO.new(theory={}, operator=d) o00[q2_out] = Q2grid[q2_out] o01 = copy.deepcopy(o00) manipulate.to_evol(o01) diff --git a/tests/ekomark/test_apply.py b/tests/ekomark/test_apply.py index 47b342bdf..1f048322a 100644 --- a/tests/ekomark/test_apply.py +++ b/tests/ekomark/test_apply.py @@ -9,7 +9,7 @@ class TestApply: def test_apply(self, fake_output, fake_pdf): q2_out = list(fake_output["Q2grid"].keys())[0] # create object - o = output.EKO.from_dict(fake_output) + o = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o[q2] = output.Operator.from_dict(op) # fake pdfs @@ -35,7 +35,7 @@ def test_apply(self, fake_output, fake_pdf): def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): q2_out = list(fake_output["Q2grid"].keys())[0] # create object - o = output.EKO.from_dict(fake_output) + o = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o[q2] = output.Operator.from_dict(op) # fake pdfs From 8093d99da6758ae61913eff179bca26f628adeea Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 17:05:22 +0200 Subject: [PATCH 061/148] Init eko CLI --- pyproject.toml | 1 + src/ekobox/cli/__init__.py | 3 +++ src/ekobox/cli/_base.py | 7 +++++++ src/ekobox/cli/run.py | 18 ++++++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 src/ekobox/cli/__init__.py create mode 100644 src/ekobox/cli/_base.py create mode 100644 src/ekobox/cli/run.py diff --git a/pyproject.toml b/pyproject.toml index a2130c1f3..875eacb4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,6 +82,7 @@ files = ["src/eko/version.py"] [tool.poetry.scripts] ekonav = "ekomark.navigator:launch_navigator" genpdf = "ekobox.genpdf.cli:cli" +eko = "ekobox.cli:command" [tool.poe.tasks] test = "pytest tests" diff --git a/src/ekobox/cli/__init__.py b/src/ekobox/cli/__init__.py new file mode 100644 index 000000000..95fbff988 --- /dev/null +++ b/src/ekobox/cli/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +from . import run +from ._base import command diff --git a/src/ekobox/cli/_base.py b/src/ekobox/cli/_base.py new file mode 100644 index 000000000..b53d3aff2 --- /dev/null +++ b/src/ekobox/cli/_base.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +import click + + +@click.group() +def command(): + pass diff --git a/src/ekobox/cli/run.py b/src/ekobox/cli/run.py new file mode 100644 index 000000000..0afd90bf3 --- /dev/null +++ b/src/ekobox/cli/run.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +import click + +from ._base import command + + +@command.command("run") +@click.argument("q2") +def subcommand(q2): + """Launch EKO computation. + + Parameters + ---------- + q2: sequence[float] + sequnce of q2 to compute + + """ + print(f"Running EKO for {q2}") From 159b541c85d24b15a1af7d209a6926b694c74655 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 19:39:22 +0200 Subject: [PATCH 062/148] Add self managed item retrieval, through context --- src/eko/output/struct.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 618ca8906..a0ca766da 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import contextlib import logging import os import pathlib @@ -199,6 +200,13 @@ def __delitem__(self, q2: float): """ self._operators[q2] = None + @contextlib.contextmanager + def operator(self, q2: float): + try: + yield self[q2] + finally: + del self[q2] + def items(self): return self._operators.items() From ad6dd31adcdd6705e195afa666abb2371e727e52 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 8 Apr 2022 19:40:18 +0200 Subject: [PATCH 063/148] Test items self mnagement, not yet fully implemented --- tests/eko/test_output_struct.py | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/eko/test_output_struct.py diff --git a/tests/eko/test_output_struct.py b/tests/eko/test_output_struct.py new file mode 100644 index 000000000..4b9cffb77 --- /dev/null +++ b/tests/eko/test_output_struct.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from eko import output +from eko.output import struct + + +class TestLegacy: + def test_items(self, fake_output): + """Test autodump, autoload, and manual unload.""" + eko = output.EKO.new(theory={}, operator=fake_output) + for q2, op in fake_output["Q2grid"].items(): + eko[q2] = output.Operator.from_dict(op) + + q2 = next(iter(fake_output["Q2grid"])) + + eko._operators[q2] = None + assert isinstance(eko[q2], struct.Operator) + assert isinstance(eko._operators[q2], struct.Operator) + + del eko[q2] + + assert eko._operators[q2] is None + + def test_iter(self, fake_output): + """Test managed iteration.""" + eko = output.EKO.new(theory={}, operator=fake_output) + for q2, op in fake_output["Q2grid"].items(): + eko[q2] = output.Operator.from_dict(op) + + q2prev = None + for q2, op in eko: + if q2prev is not None: + assert eko._operators[q2prev] is None + assert isinstance(op, struct.Operator) + q2prev = q2 + + def test_context_operator(self, fake_output): + """Test automated handling through context.""" + eko = output.EKO.new(theory={}, operator=fake_output) + for q2, op in fake_output["Q2grid"].items(): + eko[q2] = output.Operator.from_dict(op) + + q2 = next(iter(fake_output["Q2grid"])) + + with eko.operator(q2) as op: + assert isinstance(op, struct.Operator) + + assert eko._operators[q2] is None From 8b71862b2ec40dfbe0dbe3209d1d249e769a1e77 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sat, 9 Apr 2022 09:26:49 +0200 Subject: [PATCH 064/148] Add IO methods for Operator --- src/eko/output/struct.py | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index a0ca766da..2b823209b 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import contextlib +import io import logging import os import pathlib @@ -8,9 +9,11 @@ import tempfile import typing from dataclasses import dataclass, fields -from typing import Dict, Literal, Optional +from typing import BinaryIO, Dict, Literal, Optional, Tuple +import lz4.frame import numpy as np +import numpy.lib.npyio as npyio import yaml from .. import basis_rotation as br @@ -54,6 +57,42 @@ class Operator(DictLike): operator: np.ndarray error: Optional[np.ndarray] = None + # IO works with streams in memory, in order to avoid intermediate write on + # disk (keep read from and write to tar file only) + + def save(self, compress: bool = True) -> Tuple[BinaryIO, bool]: + stream = io.BytesIO() + if self.error is None: + np.save(stream, self.operator) + else: + np.savez(stream, operator=self.operator, error=self.error) + stream.seek(0) + + # compress if requested + if compress: + stream = io.BytesIO(lz4.frame.compress(stream.read())) + + # return the stream ready to be read, and the type of array dumped (i.e. + # 'npy' or 'npz') + return stream, self.error is None + + @classmethod + def load(cls, stream: BinaryIO): + content = np.load(stream) + + if isinstance(content, np.ndarray): + op = content + err = None + elif isinstance(content, npyio.NpzFile): + op = content["operator"] + err = content["error"] + else: + raise ValueError( + "Not possible to load operator, content format not recognized" + ) + + return cls(alphas=0.0, operator=op, error=err) + @dataclass class Debug(DictLike): @@ -171,6 +210,7 @@ def __setitem__(self, q2: float, op: Operator): op = Operator.from_dict(op) if not isinstance(op, Operator): raise ValueError("Only operators can be stored.") + self._operators[q2] = op def __delitem__(self, q2: float): From 08cfeb9b826678cd02465c129391f32c64887be4 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sat, 9 Apr 2022 11:34:55 +0200 Subject: [PATCH 065/148] Extend EKO documentation --- src/eko/output/struct.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 2b823209b..a109a4a12 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -155,16 +155,46 @@ class EKO: The computation can be stopped at any time, without the loss of any of the intermediate results. + Attributes + ---------- + path : pathlib.Path + path on disk, to which this object is linked (and for which it is + essentially an interface) + Q02 : float + inital scale + xgrid : interpolation.XGrid + momentum fraction internal grid + pids : np.ndarray + array of integers, corresponding to internal PIDs + configs : Configs + specific configuration to be used during the calculation of these + operators + rotations : Rotations + manipulation information, describing the current status of the EKO (e.g. + `inputgrid` and `targetgrid`) + debug : Debug + debug configurations + version : str + library version used to create the corresponding file + data_version : str + specs version, to which the file adheres + """ - path: pathlib.Path + # operators cache, contains the Q2 grid information _operators: Dict[float, Optional[Operator]] - xgrid: interpolation.XGrid + # public attributes + # ----------------- + # mandatory, identifying features + path: pathlib.Path Q02: float + xgrid: interpolation.XGrid + pids: np.ndarray + # collections configs: Configs rotations: Rotations debug: Debug - pids: np.ndarray = np.array(br.flavor_basis_pids) + # tagging information version: str = vmod.__version__ data_version: str = vmod.__data_version__ From dedd220e26d9220213e3fe1e8c2139fa24177526 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sat, 9 Apr 2022 11:35:13 +0200 Subject: [PATCH 066/148] Rearrange EKO iterators --- src/eko/output/struct.py | 67 ++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index a109a4a12..eba4a2fbb 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -204,32 +204,6 @@ def __post_init__(self): if not tarfile.is_tarfile(self.path): raise ValueError("EKO: the corresponding file is not a valid tar archive") - def __iter__(self): - """Iterate operators, with minimal load. - - Pay attention, this iterator: - - - is similar to ``dict.items()``, since it returns tuples of ``(q2, - operator)`` - - is not a read-only operation from the point of view of the in-memory - object (since the final result after iteration is no operator loaded) - - but it is a read-only operation from the point of view of the - permanent object on-disk - - Yields - ------ - tuple - couples of ``(q2, operator)``, loaded immediately before, unloaded - immediately after - - """ - for q2 in self.Q2grid: - yield q2, self[q2] - del self[q2] - - def __contains__(self, q2: float) -> bool: - return q2 in self._operators - def __getitem__(self, q2: float): # TODO: autoload return self._operators[q2] @@ -277,13 +251,46 @@ def operator(self, q2: float): finally: del self[q2] - def items(self): - return self._operators.items() - @property def Q2grid(self): return np.array(list(self._operators)) + def __iter__(self): + """Iterate over keys (i.e. Q2 values) + + Yields + ------ + float + q2 values + + """ + for q2 in self._operators: + yield q2 + + def items(self): + """Iterate operators, with minimal load. + + Pay attention, this iterator: + + - is not a read-only operation from the point of view of the in-memory + object (since the final result after iteration is no operator loaded) + - but it is a read-only operation from the point of view of the + permanent object on-disk + + Yields + ------ + tuple + couples of ``(q2, operator)``, loaded immediately before, unloaded + immediately after + + """ + for q2 in self.Q2grid: + yield q2, self[q2] + del self[q2] + + def __contains__(self, q2: float) -> bool: + return q2 in self._operators + def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: q2s = self.Q2grid close = q2s[np.isclose(q2, q2s, rtol=rtol, atol=atol)] @@ -358,7 +365,7 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): """ xgrid = interpolation.XGrid(operator["xgrid"]) - pids = np.array(operator.get("pids", cls.pids)) + pids = np.array(operator.get("pids", np.array(br.flavor_basis_pids))) return cls( path=path, From 80e95006aa192da3a4ce7afb11aeecd45dce2ea3 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 10 Apr 2022 12:14:55 +0200 Subject: [PATCH 067/148] Add autoload and autodump --- src/eko/output/struct.py | 60 +++++++++++++++++++++++++++------ tests/eko/test_output_struct.py | 2 +- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index eba4a2fbb..e5e61cb90 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -7,6 +7,7 @@ import shutil import tarfile import tempfile +import time import typing from dataclasses import dataclass, fields from typing import BinaryIO, Dict, Literal, Optional, Tuple @@ -60,7 +61,7 @@ class Operator(DictLike): # IO works with streams in memory, in order to avoid intermediate write on # disk (keep read from and write to tar file only) - def save(self, compress: bool = True) -> Tuple[BinaryIO, bool]: + def save(self, compress: bool = True) -> Tuple[io.BytesIO, bool]: stream = io.BytesIO() if self.error is None: np.save(stream, self.operator) @@ -77,7 +78,9 @@ def save(self, compress: bool = True) -> Tuple[BinaryIO, bool]: return stream, self.error is None @classmethod - def load(cls, stream: BinaryIO): + def load(cls, stream: BinaryIO, compressed: bool = True): + if compressed: + stream = io.BytesIO(lz4.frame.decompress(stream.read())) content = np.load(stream) if isinstance(content, np.ndarray): @@ -206,14 +209,51 @@ def __post_init__(self): def __getitem__(self, q2: float): # TODO: autoload - return self._operators[q2] - - def __setitem__(self, q2: float, op: Operator): - # TODO: autodump - if isinstance(op, dict): - op = Operator.from_dict(op) - if not isinstance(op, Operator): - raise ValueError("Only operators can be stored.") + op = self._operators[q2] + if op is not None: + return op + + start = f"operators/{q2:8.2f}" + + with tarfile.open(self.path) as tar: + names = list(filter(lambda n: n.startswith(start), tar.getnames())) + + if len(names) == 0: + raise ValueError(f"Q2 value '{q2}' not available in '{self.path}'") + if len(names) > 1: + raise ValueError( + f"Q2 value '{q2}' occurs multiple times in '{self.path}'" + ) + + name = names[0] + compressed = name.endswith(".lz4") + stream = tar.extractfile(name) + + if stream is None: + raise ValueError + + op = Operator.load(stream, compressed=compressed) + + self._operators[q2] = op + return op + + def __setitem__(self, q2: float, op: Optional[Operator], compress: bool = True): + if op is not None: + stream, without_err = op.save(compress) + + suffix = "npy" if without_err else "npz" + if compress: + suffix += ".lz4" + + info = tarfile.TarInfo(name=f"operators/{q2:8.2f}.{suffix}") + info.size = len(stream.getbuffer()) + info.mtime = time.time() + info.mode = 436 + info.uname = os.getlogin() + info.gname = os.getlogin() + + with tarfile.open(self.path, "a") as tar: + tar.addfile(info, fileobj=stream) self._operators[q2] = op diff --git a/tests/eko/test_output_struct.py b/tests/eko/test_output_struct.py index 4b9cffb77..01bbe4456 100644 --- a/tests/eko/test_output_struct.py +++ b/tests/eko/test_output_struct.py @@ -27,7 +27,7 @@ def test_iter(self, fake_output): eko[q2] = output.Operator.from_dict(op) q2prev = None - for q2, op in eko: + for q2, op in eko.items(): if q2prev is not None: assert eko._operators[q2prev] is None assert isinstance(op, struct.Operator) From 2673d1d4cf7e21f55491d2865883a28b0ef27f92 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sun, 10 Apr 2022 12:19:37 +0200 Subject: [PATCH 068/148] Remove user and group info from tar elements --- src/eko/output/struct.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index e5e61cb90..09487ebbe 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -249,8 +249,8 @@ def __setitem__(self, q2: float, op: Optional[Operator], compress: bool = True): info.size = len(stream.getbuffer()) info.mtime = time.time() info.mode = 436 - info.uname = os.getlogin() - info.gname = os.getlogin() + # info.uname = os.getlogin() + # info.gname = os.getlogin() with tarfile.open(self.path, "a") as tar: tar.addfile(info, fileobj=stream) From 54edfbca675007fc6ece056e6b5a8f0b4f553ca6 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 19 Jul 2022 11:15:50 +0200 Subject: [PATCH 069/148] Fix compatibility layer to use subsections --- benchmarks/eko/benchmark_evol_to_unity.py | 2 +- src/eko/compatibility.py | 15 ++++++++++++--- tests/eko/test_ev_op_grid.py | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/benchmarks/eko/benchmark_evol_to_unity.py b/benchmarks/eko/benchmark_evol_to_unity.py index e0d38dd70..eb2e9d07d 100644 --- a/benchmarks/eko/benchmark_evol_to_unity.py +++ b/benchmarks/eko/benchmark_evol_to_unity.py @@ -48,7 +48,7 @@ class BenchmarkBackwardForward: "configs": { "interpolation_polynomial_degree": 1, "interpolation_is_log": True, - "ev_op_max_order": 1, + "ev_op_max_order": (2, 0), "ev_op_iterations": 1, "backward_inversion": "exact", "n_integration_cores": 1, diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index a41ce4d91..34d44b4f1 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -1,4 +1,9 @@ # -*- coding: utf-8 -*- +"""Compatibility functions. + +Upgrade old input (NNPDF jargon compatible) to the native one. + +""" import copy @@ -27,9 +32,13 @@ def update(theory, operators): if "QED" in new_theory: new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) if operators is not None: - if isinstance(new_operators["ev_op_max_order"], int): - new_operators["ev_op_max_order"] = ( - new_operators["ev_op_max_order"], + if "configs" not in operators: + raise ValueError("No subsections, old format.") + + max_order = new_operators["configs"]["ev_op_max_order"] + if isinstance(max_order, int): + new_operators["configs"]["ev_op_max_order"] = ( + max_order, new_theory["order"][1], ) return new_theory, new_operators diff --git a/tests/eko/test_ev_op_grid.py b/tests/eko/test_ev_op_grid.py index 0fee639e3..017176d85 100644 --- a/tests/eko/test_ev_op_grid.py +++ b/tests/eko/test_ev_op_grid.py @@ -140,5 +140,5 @@ def test_mod_expanded(self): ) sv_opg = sv_opgrid.compute(3) np.testing.assert_allclose( - opg[3]["operator"], sv_opg[3]["operator"], atol=0.7 * epsilon + opg[3]["operators"], sv_opg[3]["operators"], atol=0.7 * epsilon ) From 2fb64a635853d1cf6c882b8d85a9543f8c6bfd17 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 19 Jul 2022 11:18:59 +0200 Subject: [PATCH 070/148] Update compatibility tests as well --- tests/eko/test_compatibility.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/eko/test_compatibility.py b/tests/eko/test_compatibility.py index 5e09d6673..4fc08c6a9 100644 --- a/tests/eko/test_compatibility.py +++ b/tests/eko/test_compatibility.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import pytest from eko import compatibility @@ -14,9 +13,13 @@ def test_compatibility(): new_theory = compatibility.update_theory(theory1) + assert new_theory["order"][0] == theory1["PTO"] + 1 -operator_dict = {"ev_op_max_order": 2} + +operator_dict = {"configs": {"ev_op_max_order": 2}} def test_compatibility_operators(): - new_theory, new_operator = compatibility.update(theory1, operator_dict) + _, new_operator = compatibility.update(theory1, operator_dict) + + assert not isinstance(new_operator["configs"]["ev_op_max_order"], int) From 425485e0a7420eb46e439825fa46bbb156bb6290 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 19 Jul 2022 11:23:52 +0200 Subject: [PATCH 071/148] Disable workflows run on WIP branch --- .github/workflows/isobench.yml | 5 ++++- .github/workflows/unittests.yml | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/isobench.yml b/.github/workflows/isobench.yml index bd0cbd4e2..74cdaa96d 100644 --- a/.github/workflows/isobench.yml +++ b/.github/workflows/isobench.yml @@ -1,6 +1,9 @@ name: isolated benchmarks -on: push +on: + push: + branches-ignore: + - feature/composite-output jobs: isobench: diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index a7dd8eaa7..2e8da37f4 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -1,6 +1,9 @@ name: tests -on: push +on: + push: + branches-ignore: + - feature/composite-output jobs: py38: From 5b74d3662bc2fc519ed04e7b619ecace2675d77f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 19 Jul 2022 11:54:22 +0200 Subject: [PATCH 072/148] Update operator card example --- tests/eko/test_ev_operator.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 5fe5b3b68..60ddd6355 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -157,15 +157,16 @@ def test_quad_ker(monkeypatch): } operators_card = { "Q2grid": [1, 10], - "interpolation_xgrid": [0.1, 1.0], - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": False, - "debug_skip_non_singlet": False, - "ev_op_max_order": 1, - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + "xgrid": [0.1, 1.0], + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": 1, + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": {"skip_singlet": False, "skip_non_singlet": False}, } From aea1aa3cdbd00186c6cd44255c40c69f7ba6e212 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 19 Jul 2022 12:00:45 +0200 Subject: [PATCH 073/148] Update error identifiers --- doc/source/code/IO.rst | 6 +- doc/source/overview/tutorials/output.ipynb | 4 +- src/eko/evolution_operator/grid.py | 39 ++++++++----- src/eko/output/manipulate.py | 33 +++++------ src/ekomark/plots.py | 64 +++++++++++----------- tests/eko/test_ev_op_grid.py | 4 +- 6 files changed, 80 insertions(+), 70 deletions(-) diff --git a/doc/source/code/IO.rst b/doc/source/code/IO.rst index c63eb37dd..4879fa75b 100644 --- a/doc/source/code/IO.rst +++ b/doc/source/code/IO.rst @@ -67,7 +67,7 @@ The grid elements contains two keys each - ``operators`` a :py:obj:`dict` with all evolution kernel operators where the key indicates which distribution is generated by which other one and the value represents the eko in matrix representation - this can either be the plain list representation or the binary representation (as provided by :py:meth:`numpy.ndarray.tobytes`) -- ``operator_errors`` a :py:obj:`dict` with the integration errors associated to the respective operators following the same conventions as +- ``errors`` a :py:obj:`dict` with the integration errors associated to the respective operators following the same conventions as the ``operator`` dictionary - each element (|EKO|) is a rank-4 tensor with the indices ordered in the following way: ``EKO[pid_out][x_out][pid_in][x_in]`` where ``pid_out`` and ``x_out`` refer to the outgoing |PDF| and ``pid_in`` and ``x_in`` to the incoming |PDF|. The ordering of ``pid_out/pid_in`` is determined by the ``pids`` @@ -88,7 +88,7 @@ Example Output dump - - - - 0 - 0 # more values ... - operator_errors: + errors: - - - - 0 - 0 # more values ... @@ -97,7 +97,7 @@ Example Output dump - - - - 0 - 0 # more values ... - operator_errors: + errors: - - - - 0 - 0 # more values ... diff --git a/doc/source/overview/tutorials/output.ipynb b/doc/source/overview/tutorials/output.ipynb index b3b9112ce..79bddaec3 100644 --- a/doc/source/overview/tutorials/output.ipynb +++ b/doc/source/overview/tutorials/output.ipynb @@ -275,7 +275,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "dict_keys(['operators', 'operator_errors'])\n", + "dict_keys(['operators', 'errors'])\n", "\n", "OPERATOR\n", "(14, 5, 14, 5) float64\n", @@ -288,7 +288,7 @@ "source": [ "print(opgrid[100.].keys())\n", "op = opgrid[100.][\"operators\"]\n", - "operr = opgrid[100.][\"operator_errors\"]\n", + "operr = opgrid[100.][\"errors\"]\n", "\n", "print(f\"\\nOPERATOR\\n{op.shape} {op.dtype}\")\n", "print(f\"\\nERROR\\n{operr.shape} {operr.dtype}\")" diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index e8206073f..4fae2f3ce 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -21,14 +21,33 @@ class OperatorGrid(sv.ModeMixin): - """ + """Collection of evolution operators for several scales. + The operator grid is the driver class of the evolution. It receives as input a threshold holder and a generator of a_s. From that point onwards it can compute any operator at any q2. - Parameters + Attributes ---------- + config: dict + q2_grid: np.ndarray + managers: dict + + """ + + def __init__( + self, + config, + q2_grid, + thresholds_config, + strong_coupling, + interpol_dispatcher, + ): + """Initialize `OperatorGrid`. + + Parameters + ---------- config: dict configuration dictionary q2_grid: array @@ -44,16 +63,8 @@ class OperatorGrid(sv.ModeMixin): kernel_dispatcher: eko.kernel_generation.KernelDispatcher Instance of the :class:`~eko.kernel_generation.KernelDispatcher` with the information about the kernels - """ - def __init__( - self, - config, - q2_grid, - thresholds_config, - strong_coupling, - interpol_dispatcher, - ): + """ # check order = config["order"] method = config["method"] @@ -203,7 +214,7 @@ def get_threshold_operators(self, path): return thr_ops def compute(self, q2grid=None): - """Computes all ekos for the `q2grid`. + """Compute all ekos for the `q2grid`. Parameters ---------- @@ -234,7 +245,7 @@ def compute(self, q2grid=None): return grid_return def generate(self, q2): - r"""Computes a single EKO. + r"""Compute a single EKO. Parameters ---------- @@ -298,5 +309,5 @@ def generate(self, q2): values, errors = final_op.to_flavor_basis_tensor() return { "operators": values, - "operator_errors": errors, + "errors": errors, } diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index cba8409fb..8a75a6cc4 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Manipulate output generate by EKO.""" import logging import warnings from typing import Optional @@ -18,17 +19,17 @@ def xgrid_reshape( inputgrid: Optional[np.ndarray] = None, inplace: bool = True, ): - """ - Changes the operators to have in the output targetgrid and/or in the input inputgrid. + """Change the operators to have in the output targetgrid and/or in the input inputgrid. The operation is in-place. Parameters ---------- - targetgrid : None or list - xgrid for the target - inputgrid : None or list - xgrid for the input + targetgrid : None or list + xgrid for the target + inputgrid : None or list + xgrid for the input + """ # calling with no arguments is an error if targetgrid is None and inputgrid is None: @@ -98,17 +99,17 @@ def flavor_reshape( inputbasis: Optional[np.ndarray] = None, inplace: bool = True, ): - """ - Changes the operators to have in the output targetbasis and/or in the input inputbasis. + """Change the operators to have in the output targetbasis and/or in the input inputbasis. The operation is in-place. Parameters ---------- - targetbasis : numpy.ndarray - target rotation specified in the flavor basis - inputbasis : None or list - input rotation specified in the flavor basis + targetbasis : numpy.ndarray + target rotation specified in the flavor basis + inputbasis : None or list + input rotation specified in the flavor basis + """ # calling with no arguments is an error if targetbasis is None and inputbasis is None: @@ -148,8 +149,8 @@ def flavor_reshape( else: ops = np.einsum("ca,ajbk,bd->cjdk", targetbasis, ops, inv_inputbasis) errs = np.einsum("ca,ajbk,bd->cjdk", targetbasis, errs, inv_inputbasis) - elem.operators = ops - elem.operator_errors = errs + elem.operator = ops + elem.error = errs # drop PIDs - keeping them int nevertheless # there is no meaningful way to set them in general, after rotation if inputbasis is not None: @@ -159,8 +160,7 @@ def flavor_reshape( def to_evol(eko: EKO, source: bool = True, target: bool = False, inplace: bool = True): - """ - Rotate the operator into evolution basis. + """Rotate the operator into evolution basis. This also assigns also the pids. The operation is in-place. @@ -170,6 +170,7 @@ def to_evol(eko: EKO, source: bool = True, target: bool = False, inplace: bool = rotate on the input tensor target : bool rotate on the output tensor + """ # rotate inputbasis = br.rotate_flavor_to_evolution if source else None diff --git a/src/ekomark/plots.py b/src/ekomark/plots.py index 2fcff96f8..92f821af9 100644 --- a/src/ekomark/plots.py +++ b/src/ekomark/plots.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -Plotting tools to show the evolved PDF and the computed operators -""" +"""Plotting tools to show the evolved PDF and the computed operators.""" import io import pprint @@ -15,22 +13,22 @@ def input_figure(theory, ops, pdf_name=None): - """ - Pretty-prints the setup to a figure + """Pretty-prints the setup to a figure. Parameters ---------- - theory : dict - theory card - ops : dict - operator card - pdf_name : str - PDF name + theory : dict + theory card + ops : dict + operator card + pdf_name : str + PDF name Returns ------- - firstPage : matplotlib.pyplot.Figure - figure + firstPage : matplotlib.pyplot.Figure + figure + """ firstPage = plt.figure(figsize=(25, 20)) # theory @@ -56,8 +54,7 @@ def input_figure(theory, ops, pdf_name=None): def plot_dist(x, y, yerr, yref, title=None, oMx_min=1e-2, oMx_max=0.5): - """ - Compare two distributions. + """Compare two distributions. Generates a plot with 3 areas: @@ -68,28 +65,29 @@ def plot_dist(x, y, yerr, yref, title=None, oMx_min=1e-2, oMx_max=0.5): Parameters ---------- - x : numpy.ndarray - list of abscisses - y : numpy.ndarray - computed list of ordinates - yerr : numpy.ndarray - list of ordinate errors - yref : numpy.ndarray - reference list of ordinates + x : numpy.ndarray + list of abscisses + y : numpy.ndarray + computed list of ordinates + yerr : numpy.ndarray + list of ordinate errors + yref : numpy.ndarray + reference list of ordinates Other Parameters ---------------- - title : str, optional - additional overall title - oMx_min : float - maximum value for the large x region, i.e. 1-x > 1 - `oMx_min` - oMx_max : float - minimum value for the large x region, i.e. 1 - `oMx_max` > 1-x + title : str, optional + additional overall title + oMx_min : float + maximum value for the large x region, i.e. 1-x > 1 - `oMx_min` + oMx_max : float + minimum value for the large x region, i.e. 1 - `oMx_max` > 1-x Returns ------- - fig : matplotlib.pyplot.figure - created figure + fig : matplotlib.pyplot.figure + created figure + """ np.seterr(divide="ignore", invalid="ignore") fig = plt.figure(figsize=(15, 5)) @@ -164,7 +162,7 @@ def plot_operator(var_name, op, op_err, log_operator=True, abs_operator=True): """ # get # op = ret["operators"][var_name] - # op_err = ret["operator_errors"][var_name] + # op_err = ret["errors"][var_name] # empty? thre = 1e-8 @@ -244,7 +242,7 @@ def save_operators_to_pdf(path, theory, ops, me, skip_pdfs, change_lab=False): # it's necessary to reshuffle the eko output for q2 in me["Q2grid"]: results = me["Q2grid"][q2]["operators"] - errors = me["Q2grid"][q2]["operator_errors"] + errors = me["Q2grid"][q2]["errors"] # loop on pids for label_out, res, res_err in zip(ops_names, results, errors): diff --git a/tests/eko/test_ev_op_grid.py b/tests/eko/test_ev_op_grid.py index 017176d85..b132ee2cb 100644 --- a/tests/eko/test_ev_op_grid.py +++ b/tests/eko/test_ev_op_grid.py @@ -112,10 +112,10 @@ def test_compute_q2grid(self): # we can also pass a single number opg = opgrid.compute() assert len(opg) == 2 - assert all(k in op for k in ["operator", "error"] for op in opg.values()) + assert all(k in op for k in ["operators", "errors"] for op in opg.values()) opg = opgrid.compute(3) assert len(opg) == 1 - assert all(k in op for k in ["operator", "error"] for op in opg.values()) + assert all(k in op for k in ["operators", "errors"] for op in opg.values()) def test_grid_computation_VFNS(self): """Checks that the grid can be computed""" From 588d1e64ed2967ad567b4378da8395d697d5e3a8 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Tue, 19 Jul 2022 12:20:35 +0200 Subject: [PATCH 074/148] Fix operator runcard layout in OME tests --- tests/eko/test_ome.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/eko/test_ome.py b/tests/eko/test_ome.py index 178058522..80e0a804b 100644 --- a/tests/eko/test_ome.py +++ b/tests/eko/test_ome.py @@ -314,15 +314,16 @@ class TestOperatorMatrixElement: } operators_card = { "Q2grid": [20], - "interpolation_xgrid": [0.1, 1.0], - "interpolation_polynomial_degree": 1, - "interpolation_is_log": True, - "debug_skip_singlet": True, - "debug_skip_non_singlet": False, - "ev_op_max_order": 1, - "ev_op_iterations": 1, - "backward_inversion": "exact", - "n_integration_cores": 1, + "xgrid": [0.1, 1.0], + "configs": { + "interpolation_polynomial_degree": 1, + "interpolation_is_log": True, + "ev_op_max_order": 1, + "ev_op_iterations": 1, + "backward_inversion": "exact", + "n_integration_cores": 1, + }, + "debug": {"skip_singlet": True, "skip_non_singlet": False}, } def test_labels(self): @@ -421,7 +422,7 @@ def test_compute_n3lo(self): def test_compute_lo(self): self.theory_card.update({"PTO": (1, 0)}) self.operators_card.update( - dict(debug={"debug_skip_singlet": False, "skip_non_singlet": False}) + dict(debug={"skip_singlet": False, "skip_non_singlet": False}) ) g = OperatorGrid.from_dict( self.theory_card, From 9c40e959c3c9f504a3004f565823596e041dd660 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 20 Jul 2022 11:29:59 +0200 Subject: [PATCH 075/148] Drop alphas, sanify q2 to dump --- src/eko/output/legacy.py | 11 ++++------- src/eko/output/struct.py | 6 ++---- src/eko/runner.py | 2 -- tests/conftest.py | 1 - tests/eko/test_output.py | 3 +-- 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 536142f94..b95c39aa3 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -42,18 +42,17 @@ def get_raw(eko: struct.EKO, binarize: bool = True, skip_q2_grid: bool = False): # make operators raw if not skip_q2_grid: for q2, op in eko.items(): + q2 = float(q2) out["Q2grid"][q2] = {} if op is not None: for k, v in dataclasses.asdict(op).items(): - if k == "alphas": - out["Q2grid"][q2][k] = float(v) - continue if binarize: out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) else: out["Q2grid"][q2][k] = v.tolist() else: out["Q2grid"] = obj["Q2grid"] + return out @@ -141,7 +140,7 @@ def dump_tar(obj: struct.EKO, tarname: Union[str, os.PathLike]): with open(yamlname, "w", encoding="utf-8") as fd: yaml.dump(metadata, fd) - for kind in ["alphas", "operator", "error"]: + for kind in ["operator", "error"]: elements = [] for q2, op in obj.items(): if op is not None: @@ -189,9 +188,7 @@ def load_yaml(stream: TextIO, skip_q2_grid=False) -> struct.EKO: if not skip_q2_grid: for op in obj["Q2grid"].values(): for k, v in op.items(): - if k == "alphas": - v = float(v) - elif isinstance(v, list): + if isinstance(v, list): v = np.array(v) elif isinstance(v, bytes): v = np.frombuffer(lz4.frame.decompress(v)) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 09487ebbe..72c056adc 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -54,7 +54,6 @@ def raw(self): @dataclass class Operator(DictLike): - alphas: float operator: np.ndarray error: Optional[np.ndarray] = None @@ -94,7 +93,7 @@ def load(cls, stream: BinaryIO, compressed: bool = True): "Not possible to load operator, content format not recognized" ) - return cls(alphas=0.0, operator=op, error=err) + return cls(operator=op, error=err) @dataclass @@ -362,7 +361,6 @@ def extract(path: PathLike, filename: str) -> str: ) content = fd.read().decode() - __import__("pdb").set_trace() return content @property @@ -411,7 +409,7 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): path=path, xgrid=xgrid, pids=pids, - Q02=operator["Q0"] ** 2, + Q02=float(operator["Q0"] ** 2), _operators={q2: None for q2 in operator["Q2grid"]}, configs=Configs.from_dict(operator["configs"]), rotations=Rotations.default(xgrid, pids), diff --git a/src/eko/runner.py b/src/eko/runner.py index 7ad960b3e..a6777a495 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -40,8 +40,6 @@ class Runner: """ def __init__(self, theory_card: dict, operators_card: dict): - self.out = EKO() - new_theory, new_operators = compatibility.update(theory_card, operators_card) # Store inputs diff --git a/tests/conftest.py b/tests/conftest.py index 70c08000c..b5fe84e14 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,7 +50,6 @@ def mk_g(self, q2s, lpids, lx): Q2grid[q2] = { "operator": np.random.rand(lpids, lx, lpids, lx), "error": np.random.rand(lpids, lx, lpids, lx), - "alphas": np.random.rand(), } return Q2grid diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 6e0d9853c..737bfb1ff 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -34,6 +34,7 @@ def test_io(self, fake_output, tmp_path): o1 = output.EKO.new(theory={}, operator=fake_output) for q2, op in fake_output["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) + # test streams stream = io.StringIO() legacy.dump_yaml(o1, stream) @@ -104,7 +105,6 @@ def test_xgrid_reshape(self, fake_output): dict( operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], error=np.zeros((2, len(xg), 2, len(xg))), - alphas=np.random.rand(), ) ) } @@ -169,7 +169,6 @@ def test_flavor_reshape(self, fake_output): dict( operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], error=np.zeros((2, len(xg), 2, len(xg))), - alphas=np.random.rand(), ) ) } From 38a37be789a4da8522529bfbeb9b6e46165c3787 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 20 Jul 2022 11:49:03 +0200 Subject: [PATCH 076/148] Convert all dictionaries to operators --- src/eko/evolution_operator/grid.py | 4 ++-- src/eko/output/legacy.py | 2 +- src/eko/output/struct.py | 18 ++++++++++++++---- src/eko/runner.py | 4 ++-- src/ekobox/utils.py | 5 ++--- tests/eko/test_ev_op_grid.py | 6 +++--- 6 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/eko/evolution_operator/grid.py b/src/eko/evolution_operator/grid.py index 4fae2f3ce..2ab23c515 100644 --- a/src/eko/evolution_operator/grid.py +++ b/src/eko/evolution_operator/grid.py @@ -308,6 +308,6 @@ def generate(self, q2): values, errors = final_op.to_flavor_basis_tensor() return { - "operators": values, - "errors": errors, + "operator": values, + "error": errors, } diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index b95c39aa3..81d0d9755 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -265,7 +265,7 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: q2grid = metadata["Q2grid"] operator_grid = {} for q2, slices in zip(q2grid, zip(*grids.values())): - operator_grid[q2] = dict(zip(grids.keys(), slices)) + operator_grid[q2] = struct.Operator(**dict(zip(grids.keys(), slices))) metadata["Q2grid"] = operator_grid eko = struct.EKO.new(theory={}, operator=metadata) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 72c056adc..4ddccbdfe 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -238,6 +238,9 @@ def __getitem__(self, q2: float): def __setitem__(self, q2: float, op: Optional[Operator], compress: bool = True): if op is not None: + if not isinstance(op, Operator): + raise ValueError("Only an Operator can be added to an EKO") + stream, without_err = op.save(compress) suffix = "npy" if without_err else "npz" @@ -448,10 +451,17 @@ def new(cls, theory: dict, operator: dict, path: Optional[PathLike] = None): the generated structure """ - path = pathlib.Path( - path if path is not None else tempfile.mkstemp(suffix=".tar")[1] - ) - path.unlink() + givenpath = path is not None + + path = pathlib.Path(path if givenpath else tempfile.mkstemp(suffix=".tar")[1]) + if path.exists(): + if givenpath: + raise FileExistsError( + f"File exists at given path '{path}', cannot be used for a new operator." + ) + # delete the file created in case of temporary file + path.unlink() + with tempfile.TemporaryDirectory() as td: td = pathlib.Path(td) cls.bootstrap(td, theory=theory, operator=operator) diff --git a/src/eko/runner.py b/src/eko/runner.py index a6777a495..7896337ee 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -11,7 +11,7 @@ from . import compatibility, interpolation, msbar_masses from .couplings import Couplings from .evolution_operator.grid import OperatorGrid -from .output import EKO, manipulate +from .output import EKO, Operator, manipulate from .thresholds import ThresholdsAtlas logger = logging.getLogger(__name__) @@ -87,7 +87,7 @@ def get_output(self) -> EKO: """ # add all operators for final_scale, op in self.op_grid.compute().items(): - self.out[float(final_scale)] = op + self.out[float(final_scale)] = Operator.from_dict(op) def similar_to_none(name: str) -> Optional[np.ndarray]: grid = self.post_process[name] diff --git a/src/ekobox/utils.py b/src/ekobox/utils.py index 9fb66d3a9..13622046c 100644 --- a/src/ekobox/utils.py +++ b/src/ekobox/utils.py @@ -3,7 +3,7 @@ import numpy as np -from eko.output import EKO +from eko.output import EKO, Operator # TODO: add a control on the theory (but before we need to implement another @@ -77,7 +77,6 @@ def ekos_product( "ajbk,bkcl -> ajcl", ope1_error, op2.operator ) - alphas = eko_fin[q2].alphas - final_eko[q2] = dict(operator=op, error=error, alphas=alphas) + final_eko[q2] = Operator(operator=op, error=error) return final_eko diff --git a/tests/eko/test_ev_op_grid.py b/tests/eko/test_ev_op_grid.py index b132ee2cb..0fee639e3 100644 --- a/tests/eko/test_ev_op_grid.py +++ b/tests/eko/test_ev_op_grid.py @@ -112,10 +112,10 @@ def test_compute_q2grid(self): # we can also pass a single number opg = opgrid.compute() assert len(opg) == 2 - assert all(k in op for k in ["operators", "errors"] for op in opg.values()) + assert all(k in op for k in ["operator", "error"] for op in opg.values()) opg = opgrid.compute(3) assert len(opg) == 1 - assert all(k in op for k in ["operators", "errors"] for op in opg.values()) + assert all(k in op for k in ["operator", "error"] for op in opg.values()) def test_grid_computation_VFNS(self): """Checks that the grid can be computed""" @@ -140,5 +140,5 @@ def test_mod_expanded(self): ) sv_opg = sv_opgrid.compute(3) np.testing.assert_allclose( - opg[3]["operators"], sv_opg[3]["operators"], atol=0.7 * epsilon + opg[3]["operator"], sv_opg[3]["operator"], atol=0.7 * epsilon ) From d40b6c7feb322ff0f11b0e332fdea867969bb364 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 20 Jul 2022 13:25:14 +0200 Subject: [PATCH 077/148] Fix double write to archive --- src/eko/output/manipulate.py | 4 +++- src/eko/output/struct.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 8a75a6cc4..b7237a62d 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -75,7 +75,7 @@ def xgrid_reshape( eko.rotations.inputgrid = np.array(inputgrid) # build new grid - for _, elem in eko.items(): + for q2, elem in eko.items(): if elem is None: continue ops = elem.operator @@ -92,6 +92,8 @@ def xgrid_reshape( elem.operator = ops elem.error = errs + eko[q2] = elem + def flavor_reshape( eko: EKO, diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 4ddccbdfe..d2723680b 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -5,6 +5,7 @@ import os import pathlib import shutil +import subprocess import tarfile import tempfile import time @@ -254,6 +255,17 @@ def __setitem__(self, q2: float, op: Optional[Operator], compress: bool = True): # info.uname = os.getlogin() # info.gname = os.getlogin() + # TODO: unfortunately Python has no native support for deleting + # files inside tar, so the proper way is to make that function + # ourselves, in the inefficient way of constructing a new archive + # from the existing one, but for the file to be removed + # at the moment, an implicit dependency on `tar` command has been + # introduced -> dangerous for portability + # since it's not raising any error, it is fine to run in any case: + # if the file is not there, nothing happens + subprocess.run( + f"tar -f {self.path.absolute()} --delete".split() + [info.name] + ) with tarfile.open(self.path, "a") as tar: tar.addfile(info, fileobj=stream) From f227a8ee9d73c2c55764b9e49afa1d177dc34529 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 20 Jul 2022 13:50:26 +0200 Subject: [PATCH 078/148] Fix flavor reshaping, set operator --- src/eko/output/manipulate.py | 5 ++++- tests/eko/test_output.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index b7237a62d..ca8283819 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -137,7 +137,7 @@ def flavor_reshape( inv_inputbasis = np.linalg.inv(inputbasis) # build new grid - for _, elem in eko.items(): + for q2, elem in eko.items(): if elem is None: continue ops = elem.operator @@ -153,6 +153,9 @@ def flavor_reshape( errs = np.einsum("ca,ajbk,bd->cjdk", targetbasis, errs, inv_inputbasis) elem.operator = ops elem.error = errs + + eko[q2] = elem + # drop PIDs - keeping them int nevertheless # there is no meaningful way to set them in general, after rotation if inputbasis is not None: diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 737bfb1ff..bd7695e3c 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -238,7 +238,7 @@ def test_to_evol(self, fake_factory): ), ) o00 = output.EKO.new(theory={}, operator=d) - o00[q2_out] = Q2grid[q2_out] + o00[q2_out] = output.Operator(**Q2grid[q2_out]) o01 = copy.deepcopy(o00) manipulate.to_evol(o01) o10 = copy.deepcopy(o00) From 822d552fef955f4274df7db05fa39c9912c263c0 Mon Sep 17 00:00:00 2001 From: giacomomagni Date: Wed, 20 Jul 2022 15:08:33 +0200 Subject: [PATCH 079/148] fix ome test --- tests/eko/test_ome.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/eko/test_ome.py b/tests/eko/test_ome.py index 80e0a804b..c22b4762f 100644 --- a/tests/eko/test_ome.py +++ b/tests/eko/test_ome.py @@ -420,7 +420,7 @@ def test_compute_n3lo(self): np.testing.assert_allclose(mat, np.triu(mat)) def test_compute_lo(self): - self.theory_card.update({"PTO": (1, 0)}) + self.theory_card.update({"order": (1, 0)}) self.operators_card.update( dict(debug={"skip_singlet": False, "skip_non_singlet": False}) ) From f5da5c09c31edc343deebfabde07745a2367efec Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Wed, 20 Jul 2022 18:54:38 +0200 Subject: [PATCH 080/148] Check file exists in tar --- src/eko/output/manipulate.py | 6 ++++-- src/eko/output/struct.py | 13 ++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index ca8283819..161d5d27b 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -137,7 +137,9 @@ def flavor_reshape( inv_inputbasis = np.linalg.inv(inputbasis) # build new grid - for q2, elem in eko.items(): + # for q2, elem in eko.items(): + for q2 in eko.Q2grid: + elem = eko[q2] if elem is None: continue ops = elem.operator @@ -157,7 +159,7 @@ def flavor_reshape( eko[q2] = elem # drop PIDs - keeping them int nevertheless - # there is no meaningful way to set them in general, after rotation + # there is no meaningful way to set them in general, after rotation if inputbasis is not None: eko.rotations.inputpids = [0] * len(eko.rotations.inputpids) if targetbasis is not None: diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index d2723680b..7d9642106 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -262,13 +262,16 @@ def __setitem__(self, q2: float, op: Optional[Operator], compress: bool = True): # at the moment, an implicit dependency on `tar` command has been # introduced -> dangerous for portability # since it's not raising any error, it is fine to run in any case: - # if the file is not there, nothing happens - subprocess.run( - f"tar -f {self.path.absolute()} --delete".split() + [info.name] - ) + has_file = False + with tarfile.open(self.path, mode="r") as tar: + has_file = f"operators/{q2:8.2f}.{suffix}" in tar.getnames() + + if has_file: + subprocess.run( + f"tar -f {self.path.absolute()} --delete".split() + [info.name] + ) with tarfile.open(self.path, "a") as tar: tar.addfile(info, fileobj=stream) - self._operators[q2] = op def __delitem__(self, q2: float): From 955126178f9e4380ce0849db2a3eaf6fc6acaa70 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 20 Jul 2022 19:27:40 +0200 Subject: [PATCH 081/148] Unleash workflows --- .github/workflows/isobench.yml | 5 +---- .github/workflows/unittests.yml | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/isobench.yml b/.github/workflows/isobench.yml index 74cdaa96d..bd0cbd4e2 100644 --- a/.github/workflows/isobench.yml +++ b/.github/workflows/isobench.yml @@ -1,9 +1,6 @@ name: isolated benchmarks -on: - push: - branches-ignore: - - feature/composite-output +on: push jobs: isobench: diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml index 2e8da37f4..a7dd8eaa7 100644 --- a/.github/workflows/unittests.yml +++ b/.github/workflows/unittests.yml @@ -1,9 +1,6 @@ name: tests -on: - push: - branches-ignore: - - feature/composite-output +on: push jobs: py38: From d3e825a6867395dc882e100aed64643c182a8939 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 25 Jul 2022 15:16:19 +0200 Subject: [PATCH 082/148] Add docstrings for DictLike --- src/eko/output/struct.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 7d9642106..4c44e812e 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Define output representation structures.""" import contextlib import io import logging @@ -28,15 +29,50 @@ class DictLike: + """Dictionary compatibility base class, for dataclasses. + + This class add compatibility to import and export from Python :class:`dict`, + in such a way to support serialization interfaces working with them. + + Some collections and scalar objects are normalized to native Python + structures, in order to simplify the on-disk representation. + + """ + def __init__(self, **kwargs): + """Empty initializer.""" pass @classmethod def from_dict(cls, dictionary): + """Initialize dataclass object from raw dictionary. + + Parameters + ---------- + dictionary: dict + the dictionary to be converted to :class:`DictLike` + + Returns + ------- + DictLike + instance with `dictionary` content loaded as attributes + + """ return cls(**dictionary) @property def raw(self): + """Convert dataclass object to raw dictionary. + + Normalize :class:`np.ndarray` to lists (possibly nested), and scalars to + the corresponding built-in type. + + Returns + ------- + dict + dictionary representation + + """ dictionary = {} for field in fields(self): value = getattr(self, field.name) From dbe7869df2ceda6087757b951836252ff2b3c5b1 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Mon, 25 Jul 2022 23:22:07 +0200 Subject: [PATCH 083/148] Init operator docstrings --- src/eko/output/struct.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 4c44e812e..4362f5964 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -91,6 +91,21 @@ def raw(self): @dataclass class Operator(DictLike): + """Operator representation. + + To be used to hold the result of a computed 4-dim operator (from a given + scale to another given one). + + Attributes + ---------- + operator: np.ndarray + content of the evolution operator + error: np.ndarray or None + errors on individual operator elements (mainly used for integration + error, but it can host any kind of error) + + """ + operator: np.ndarray error: Optional[np.ndarray] = None @@ -98,6 +113,7 @@ class Operator(DictLike): # disk (keep read from and write to tar file only) def save(self, compress: bool = True) -> Tuple[io.BytesIO, bool]: + """Save content of operator to bytes.""" stream = io.BytesIO() if self.error is None: np.save(stream, self.operator) @@ -115,6 +131,7 @@ def save(self, compress: bool = True) -> Tuple[io.BytesIO, bool]: @classmethod def load(cls, stream: BinaryIO, compressed: bool = True): + """Load operator from bytes.""" if compressed: stream = io.BytesIO(lz4.frame.decompress(stream.read())) content = np.load(stream) From 034f7e5dfbb39cc94123ab3d2bf195aa2a93117c Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 26 Jul 2022 17:56:24 +0200 Subject: [PATCH 084/148] Add compatibility test --- tests/eko/test_compatibility.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/eko/test_compatibility.py b/tests/eko/test_compatibility.py index 4fc08c6a9..d5c4e3a41 100644 --- a/tests/eko/test_compatibility.py +++ b/tests/eko/test_compatibility.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import pytest from eko import compatibility @@ -23,3 +24,6 @@ def test_compatibility_operators(): _, new_operator = compatibility.update(theory1, operator_dict) assert not isinstance(new_operator["configs"]["ev_op_max_order"], int) + + with pytest.raises(ValueError): + compatibility.update(theory1, {}) From 76f38aca1619b5c2598ef6f1bb1f57c96b93992a Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 26 Jul 2022 18:11:55 +0200 Subject: [PATCH 085/148] Add CLI test --- tests/ekobox/test_cli.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/ekobox/test_cli.py diff --git a/tests/ekobox/test_cli.py b/tests/ekobox/test_cli.py new file mode 100644 index 000000000..5405e78c3 --- /dev/null +++ b/tests/ekobox/test_cli.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from click.testing import CliRunner + +from ekobox.cli import command + + +def test_run(): + runner = CliRunner() + result = runner.invoke(command, ["run", "a"]) + assert "Running EKO for" in result.output From aec2d6a39bc8b612e3d8d9b999ac55940681094c Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 26 Jul 2022 18:27:23 +0200 Subject: [PATCH 086/148] Fix and test evol_pdf --- src/ekobox/evol_pdf.py | 43 +++++++++++++++++------------------ tests/ekobox/test_evol_pdf.py | 9 +++++++- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index 8504c2462..391937031 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Tools to evolve actual PDFs.""" import pathlib import eko @@ -20,30 +21,28 @@ def evolve_pdfs( name="Evolved_PDF", info_update=None, ): - """ - This function evolves an initial_PDF using a theory card and an operator card - and dump the evolved PDF in lhapdf format + """Evolves one or several initial_PDFs and dump the evolved PDFs in lhapdf format. Parameters ---------- - initial_PDF_list : list(lhapdf object) - list of PDF members to be evolved - theory_card : dict - theory card - operators_card : dict - operators card - path : str - path to cached eko output (if "None" it will be recomputed) - store_path : str - path where the eko is stored (if "None" will not be saved) - targetgrid : list(float) - target x-grid (if different from input x-grid) - install : bool - set whether to install evolved PDF to lhapdf directory - name : str - set name of evolved PDF - info_update : dict - dict of info to add or update to default info file + initial_PDF_list : list(lhapdf object) + list of PDF members to be evolved + theory_card : dict + theory card + operators_card : dict + operators card + path : str + path to cached eko output (if "None" it will be recomputed) + store_path : str + path where the eko is stored (if "None" will not be saved) + targetgrid : list(float) + target x-grid (if different from input x-grid) + install : bool + set whether to install evolved PDF to lhapdf directory + name : str + set name of evolved PDF + info_update : dict + dict of info to add or update to default info file """ eko_output = None if path is not None: @@ -56,7 +55,7 @@ def evolve_pdfs( else: eko_output = eko.run_dglap(theory_card, operators_card) if store_path is not None: - eko_output.dump_tar(store_path) + eko.output.legacy.dump_tar(eko_output, store_path) evolved_PDF_list = [] for initial_PDF in initial_PDF_list: diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index fc833cd90..d2d194802 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -22,10 +22,17 @@ def test_evolve_pdfs_run(fake_lhapdf, cd): n = "test_evolve_pdfs_run" mytmp = fake_lhapdf / "install" mytmp.mkdir() + store_path = mytmp / "test.tar" with cd(mytmp): - ev_p.evolve_pdfs([toy.mkPDF("", 0)], theory, op, install=True, name=n) + ev_p.evolve_pdfs( + [toy.mkPDF("", 0)], theory, op, install=True, name=n, store_path=store_path + ) p = fake_lhapdf / n assert p.exists() + # check dumped eko + assert store_path.exists() + assert store_path.is_file() + out.load_tar(store_path) def test_evolve_pdfs_dump_path(fake_lhapdf, cd): From 0e9537761df53607257a1c605594ce8207d9b9bf Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 26 Jul 2022 18:32:13 +0200 Subject: [PATCH 087/148] Comment runcards --- src/eko/runcards.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/eko/runcards.py b/src/eko/runcards.py index b9be63c4c..eb7f14e3d 100644 --- a/src/eko/runcards.py +++ b/src/eko/runcards.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -from dataclasses import dataclass +"""Runcard definitions.""" +# from dataclasses import dataclass -@dataclass -class TheoryCard: - pto: int +# @dataclass +# class TheoryCard: +# pto: int -@dataclass -class OperatorCard: - xgrid: list +# @dataclass +# class OperatorCard: +# xgrid: list From 976bc98430534090d031241eaac5b2dd26d60d80 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Tue, 26 Jul 2022 18:49:02 +0200 Subject: [PATCH 088/148] Fix runner test --- tests/eko/test_runner.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index c4e54c22f..9c23ecee6 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -69,17 +69,23 @@ def test_targetgrid(): r = eko.runner.Runner(tc, oc) o = r.get_output() check_shapes(o, eko.interpolation.XGrid(np.array(tgrid)), o.xgrid, tc, oc) + # check actual value + np.testing.assert_allclose(o.rotations.targetgrid, tgrid) -def test_targetbasis(): - # change targetbasis +def test_rotate_pids(): + # change pids tc = copy.deepcopy(theory_card) oc = copy.deepcopy(operators_card) - oc["targetbasis"] = np.eye(14) + 0.1 * np.random.rand(14, 14) - oc["inputbasis"] = np.eye(14) + 0.1 * np.random.rand(14, 14) + oc["rotations"] = {} + oc["rotations"]["targetpids"] = np.eye(14) + 0.1 * np.random.rand(14, 14) + oc["rotations"]["inputpids"] = np.eye(14) + 0.1 * np.random.rand(14, 14) r = eko.runner.Runner(tc, oc) o = r.get_output() check_shapes(o, o.xgrid, o.xgrid, tc, oc) + # check actual values + assert o.rotations.targetpids == [0] * 14 + assert o.rotations.inputpids == [0] * 14 def check_shapes(o, txs, ixs, theory_card, operators_card): From d5885a8c3c21c19f260e7d8e8697297e2510a7d4 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 15:18:08 +0200 Subject: [PATCH 089/148] Revert "Comment runcards" This reverts commit 0e9537761df53607257a1c605594ce8207d9b9bf. --- src/eko/runcards.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/eko/runcards.py b/src/eko/runcards.py index eb7f14e3d..b9be63c4c 100644 --- a/src/eko/runcards.py +++ b/src/eko/runcards.py @@ -1,13 +1,12 @@ # -*- coding: utf-8 -*- -"""Runcard definitions.""" -# from dataclasses import dataclass +from dataclasses import dataclass -# @dataclass -# class TheoryCard: -# pto: int +@dataclass +class TheoryCard: + pto: int -# @dataclass -# class OperatorCard: -# xgrid: list +@dataclass +class OperatorCard: + xgrid: list From c426b5f5f9b7b10140e79ab05ce7ffde4b196563 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 15:27:17 +0200 Subject: [PATCH 090/148] Change all {input,target}basis names to {}pids --- src/eko/output/manipulate.py | 62 ++++++++++++++++++------------------ src/eko/runner.py | 26 +++++++++------ tests/eko/test_output.py | 8 ++--- tests/eko/test_runner.py | 2 +- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 161d5d27b..9d4af4105 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -97,44 +97,44 @@ def xgrid_reshape( def flavor_reshape( eko: EKO, - targetbasis: Optional[np.ndarray] = None, - inputbasis: Optional[np.ndarray] = None, + targetpids: Optional[np.ndarray] = None, + inputpids: Optional[np.ndarray] = None, inplace: bool = True, ): - """Change the operators to have in the output targetbasis and/or in the input inputbasis. + """Change the operators to have in the output targetpids and/or in the input inputpids. The operation is in-place. Parameters ---------- - targetbasis : numpy.ndarray + targetpids : numpy.ndarray target rotation specified in the flavor basis - inputbasis : None or list + inputpids : None or list input rotation specified in the flavor basis """ # calling with no arguments is an error - if targetbasis is None and inputbasis is None: - raise ValueError("Nor inputbasis nor targetbasis was given") + if targetpids is None and inputpids is None: + raise ValueError("Nor inputpids nor targetpids was given") # now check to the current status - if targetbasis is not None and np.allclose( - targetbasis, np.eye(len(eko.rotations.targetpids)) + if targetpids is not None and np.allclose( + targetpids, np.eye(len(eko.rotations.targetpids)) ): - targetbasis = None - warnings.warn("The new targetbasis is close to current basis") - if inputbasis is not None and np.allclose( - inputbasis, np.eye(len(eko.rotations.inputpids)) + targetpids = None + warnings.warn("The new targetpids is close to current basis") + if inputpids is not None and np.allclose( + inputpids, np.eye(len(eko.rotations.inputpids)) ): - inputbasis = None - warnings.warn("The new inputbasis is close to current basis") + inputpids = None + warnings.warn("The new inputpids is close to current basis") # after the checks: if there is still nothing to do, skip - if targetbasis is None and inputbasis is None: + if targetpids is None and inputpids is None: logger.debug("Nothing done.") return # flip input around - if inputbasis is not None: - inv_inputbasis = np.linalg.inv(inputbasis) + if inputpids is not None: + inv_inputpids = np.linalg.inv(inputpids) # build new grid # for q2, elem in eko.items(): @@ -144,15 +144,15 @@ def flavor_reshape( continue ops = elem.operator errs = elem.error - if targetbasis is not None and inputbasis is None: - ops = np.einsum("ca,ajbk->cjbk", targetbasis, ops) - errs = np.einsum("ca,ajbk->cjbk", targetbasis, errs) - elif inputbasis is not None and targetbasis is None: - ops = np.einsum("ajbk,bd->ajdk", ops, inv_inputbasis) - errs = np.einsum("ajbk,bd->ajdk", errs, inv_inputbasis) + if targetpids is not None and inputpids is None: + ops = np.einsum("ca,ajbk->cjbk", targetpids, ops) + errs = np.einsum("ca,ajbk->cjbk", targetpids, errs) + elif inputpids is not None and targetpids is None: + ops = np.einsum("ajbk,bd->ajdk", ops, inv_inputpids) + errs = np.einsum("ajbk,bd->ajdk", errs, inv_inputpids) else: - ops = np.einsum("ca,ajbk,bd->cjdk", targetbasis, ops, inv_inputbasis) - errs = np.einsum("ca,ajbk,bd->cjdk", targetbasis, errs, inv_inputbasis) + ops = np.einsum("ca,ajbk,bd->cjdk", targetpids, ops, inv_inputpids) + errs = np.einsum("ca,ajbk,bd->cjdk", targetpids, errs, inv_inputpids) elem.operator = ops elem.error = errs @@ -160,9 +160,9 @@ def flavor_reshape( # drop PIDs - keeping them int nevertheless # there is no meaningful way to set them in general, after rotation - if inputbasis is not None: + if inputpids is not None: eko.rotations.inputpids = [0] * len(eko.rotations.inputpids) - if targetbasis is not None: + if targetpids is not None: eko.rotations.targetpids = [0] * len(eko.rotations.targetpids) @@ -180,9 +180,9 @@ def to_evol(eko: EKO, source: bool = True, target: bool = False, inplace: bool = """ # rotate - inputbasis = br.rotate_flavor_to_evolution if source else None - targetbasis = br.rotate_flavor_to_evolution if target else None - flavor_reshape(eko, inputbasis=inputbasis, targetbasis=targetbasis) + inputpids = br.rotate_flavor_to_evolution if source else None + targetpids = br.rotate_flavor_to_evolution if target else None + flavor_reshape(eko, inputpids=inputpids, targetpids=targetpids) # assign pids if source: eko.rotations.inputpids = br.evol_basis_pids diff --git a/src/eko/runner.py b/src/eko/runner.py index 7896337ee..3abcc069d 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" - This file contains the main application class of eko -""" +"""This file contains the main application class of eko.""" import copy import logging from typing import Optional @@ -18,15 +16,15 @@ class Runner: - """ - Represents a single input configuration. + """Represents a single input configuration. For details about the configuration, see :doc:`here ` - Parameters + Attributes ---------- - setup : dict - input configurations + setup : dict + input configurations + """ banner = r""" @@ -40,6 +38,16 @@ class Runner: """ def __init__(self, theory_card: dict, operators_card: dict): + """Initialize runner. + + Parameters + ---------- + theory_card: dict + theory parameters and options + operators_card: dict + operator specific options + + """ new_theory, new_operators = compatibility.update(theory_card, operators_card) # Store inputs @@ -113,7 +121,7 @@ def similar_to_none(name: str) -> Optional[np.ndarray]: targetpids = similar_to_none("targetpids") if inputpids is not None or targetpids is not None: manipulate.flavor_reshape( - self.out, targetbasis=targetpids, inputbasis=inputpids + self.out, targetpids=targetpids, inputpids=inputpids ) return copy.deepcopy(self.out) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index bd7695e3c..52aefc18e 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -148,7 +148,7 @@ def test_reshape_io(self, fake_output): o1[q2] = output.Operator.from_dict(op) o2 = copy.deepcopy(o1) manipulate.xgrid_reshape(o2, [0.1, 1.0], [0.1, 1.0]) - manipulate.flavor_reshape(o2, inputbasis=np.array([[1, -1], [1, 1]])) + manipulate.flavor_reshape(o2, inputpids=np.array([[1, -1], [1, 1]])) # dump stream = io.StringIO() legacy.dump_yaml(o2, stream) @@ -189,14 +189,14 @@ def test_flavor_reshape(self, fake_output): # only input input_r = np.array([[1, -1], [1, 1]]) oi = copy.deepcopy(o1) - manipulate.flavor_reshape(oi, inputbasis=input_r) + manipulate.flavor_reshape(oi, inputpids=input_r) chk_keys(o1.raw, oi.raw) assert oi[10].operator.shape == (2, len(xg), 2, len(xg)) oii = copy.deepcopy(oi) - manipulate.flavor_reshape(oii, inputbasis=np.linalg.inv(input_r)) + manipulate.flavor_reshape(oii, inputpids=np.linalg.inv(input_r)) np.testing.assert_allclose(oii[10].operator, o1[10].operator) with pytest.warns(Warning): - manipulate.flavor_reshape(oii, inputbasis=np.eye(2)) + manipulate.flavor_reshape(oii, inputpids=np.eye(2)) chk_keys(o1.raw, oii.raw) np.testing.assert_allclose(oii[10].operator, o1[10].operator) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 9c23ecee6..054d0970e 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -109,7 +109,7 @@ def check_shapes(o, txs, ixs, theory_card, operators_card): def test_vfns(): - # change targetbasis + # change targetpids tc = copy.deepcopy(theory_card) oc = copy.deepcopy(operators_card) tc["kcThr"] = 1.0 From a621d847da996f9498c2a32bf0658d4d8aa28432 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 16:35:19 +0200 Subject: [PATCH 091/148] Remove PathLike in favor of standard library os.PathLike --- src/eko/output/struct.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 4362f5964..1bfa6db4f 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -10,7 +10,6 @@ import tarfile import tempfile import time -import typing from dataclasses import dataclass, fields from typing import BinaryIO, Dict, Literal, Optional, Tuple @@ -25,8 +24,6 @@ logger = logging.getLogger(__name__) -PathLike = typing.Union[str, os.PathLike] - class DictLike: """Dictionary compatibility base class, for dataclasses. @@ -412,7 +409,7 @@ def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: raise ValueError(f"Multiple values of Q2 have been found close to {q2}") @staticmethod - def bootstrap(tdir: PathLike, theory: dict, operator: dict): + def bootstrap(tdir: os.PathLike, theory: dict, operator: dict): tdir = pathlib.Path(tdir) (tdir / "theory.yaml").write_text(yaml.dump(theory), encoding="utf-8") (tdir / "operator.yaml").write_text(yaml.dump(operator), encoding="utf-8") @@ -421,7 +418,7 @@ def bootstrap(tdir: PathLike, theory: dict, operator: dict): (tdir / "operators").mkdir() @staticmethod - def extract(path: PathLike, filename: str) -> str: + def extract(path: os.PathLike, filename: str) -> str: path = pathlib.Path(path) with tarfile.open(path, "r") as tar: @@ -463,7 +460,7 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): the theory card operator : dict the operator card - path : str or os.PathLike + path : os.PathLike the underlying path (it has to be a valid object, but it is not guaranteed, see the note) @@ -488,7 +485,7 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): ) @classmethod - def new(cls, theory: dict, operator: dict, path: Optional[PathLike] = None): + def new(cls, theory: dict, operator: dict, path: Optional[os.PathLike] = None): """Make structure from runcard-like dictionary. This constructor is made to be used with loaded runcards, in order to @@ -509,7 +506,7 @@ def new(cls, theory: dict, operator: dict, path: Optional[PathLike] = None): the theory card operator : dict the operator card - path : str or os.PathLike + path : os.PathLike the underlying path (if not provided, it is created in a temporary path) @@ -545,7 +542,7 @@ def new(cls, theory: dict, operator: dict, path: Optional[PathLike] = None): return eko @classmethod - def load(cls, path: PathLike): + def load(cls, path: os.PathLike): path = pathlib.Path(path) if not tarfile.is_tarfile(path): raise ValueError("EKO: the corresponding file is not a valid tar archive") From d84e24dd8bb499ca5766059da5f43e0d53869a56 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 17:19:32 +0200 Subject: [PATCH 092/148] Provide native deep copy for EKO --- src/eko/output/struct.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 1bfa6db4f..32536dec4 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Define output representation structures.""" import contextlib +import copy import io import logging import os @@ -408,6 +409,45 @@ def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: return None raise ValueError(f"Multiple values of Q2 have been found close to {q2}") + def unload(self): + """Fully unload the operators in memory.""" + + for q2 in self: + del self[q2] + + def deepcopy(self, path: os.PathLike): + """Create a deep copy of current instance. + + The managed on-disk object is copied as well, to the new ``path`` + location. + If you don't want to copy the disk, consider using directly:: + + copy.deepcopy(myeko) + + It will perform the exact same operation, without propagating it to the + disk counterpart. + + Parameters + ---------- + path: os.PathLike + path were to copy the disk counterpart of the operator object + + Returns + ------- + EKO + the copy created + + """ + # return the object fully on disk, in order to avoid expensive copies in + # memory (they would be copied on-disk in any case) + self.unload() + + new = copy.deepcopy(self) + new.path = pathlib.Path(path) + shutil.copy2(self.path, new.path) + + return new + @staticmethod def bootstrap(tdir: os.PathLike, theory: dict, operator: dict): tdir = pathlib.Path(tdir) From d5c581682ed67989dae8dd6e09db02b6be753273 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 17:44:10 +0200 Subject: [PATCH 093/148] Remove the option to delete item setting it -> use delitem --- src/eko/output/struct.py | 70 ++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 32536dec4..ea2964ff8 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -288,41 +288,41 @@ def __getitem__(self, q2: float): self._operators[q2] = op return op - def __setitem__(self, q2: float, op: Optional[Operator], compress: bool = True): - if op is not None: - if not isinstance(op, Operator): - raise ValueError("Only an Operator can be added to an EKO") - - stream, without_err = op.save(compress) - - suffix = "npy" if without_err else "npz" - if compress: - suffix += ".lz4" - - info = tarfile.TarInfo(name=f"operators/{q2:8.2f}.{suffix}") - info.size = len(stream.getbuffer()) - info.mtime = time.time() - info.mode = 436 - # info.uname = os.getlogin() - # info.gname = os.getlogin() - - # TODO: unfortunately Python has no native support for deleting - # files inside tar, so the proper way is to make that function - # ourselves, in the inefficient way of constructing a new archive - # from the existing one, but for the file to be removed - # at the moment, an implicit dependency on `tar` command has been - # introduced -> dangerous for portability - # since it's not raising any error, it is fine to run in any case: - has_file = False - with tarfile.open(self.path, mode="r") as tar: - has_file = f"operators/{q2:8.2f}.{suffix}" in tar.getnames() - - if has_file: - subprocess.run( - f"tar -f {self.path.absolute()} --delete".split() + [info.name] - ) - with tarfile.open(self.path, "a") as tar: - tar.addfile(info, fileobj=stream) + def __setitem__(self, q2: float, op: Operator, compress: bool = True): + if not isinstance(op, Operator): + raise ValueError("Only an Operator can be added to an EKO") + + stream, without_err = op.save(compress) + + suffix = "npy" if without_err else "npz" + if compress: + suffix += ".lz4" + + info = tarfile.TarInfo(name=f"operators/{q2:8.2f}.{suffix}") + info.size = len(stream.getbuffer()) + info.mtime = time.time() + info.mode = 436 + # info.uname = os.getlogin() + # info.gname = os.getlogin() + + # TODO: unfortunately Python has no native support for deleting + # files inside tar, so the proper way is to make that function + # ourselves, in the inefficient way of constructing a new archive + # from the existing one, but for the file to be removed + # at the moment, an implicit dependency on `tar` command has been + # introduced -> dangerous for portability + # since it's not raising any error, it is fine to run in any case: + has_file = False + with tarfile.open(self.path, mode="r") as tar: + has_file = f"operators/{q2:8.2f}.{suffix}" in tar.getnames() + + if has_file: + subprocess.run( + f"tar -f {self.path.absolute()} --delete".split() + [info.name] + ) + with tarfile.open(self.path, "a") as tar: + tar.addfile(info, fileobj=stream) + self._operators[q2] = op def __delitem__(self, q2: float): From 1b8bb42bfa5a9f98670ca007dd78eb67ee9c4dda Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 17:51:22 +0200 Subject: [PATCH 094/148] Replace file and directory names with module level constants --- src/eko/output/struct.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index ea2964ff8..b1997482e 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -25,6 +25,12 @@ logger = logging.getLogger(__name__) +THEORYFILE = "theory.yaml" +OPERATORFILE = "operator.yaml" +RECIPESDIR = "recipes" +PARTSDIR = "parts" +OPERATORSDIR = "operators" + class DictLike: """Dictionary compatibility base class, for dataclasses. @@ -451,11 +457,11 @@ def deepcopy(self, path: os.PathLike): @staticmethod def bootstrap(tdir: os.PathLike, theory: dict, operator: dict): tdir = pathlib.Path(tdir) - (tdir / "theory.yaml").write_text(yaml.dump(theory), encoding="utf-8") - (tdir / "operator.yaml").write_text(yaml.dump(operator), encoding="utf-8") - (tdir / "recipes").mkdir() - (tdir / "parts").mkdir() - (tdir / "operators").mkdir() + (tdir / THEORYFILE).write_text(yaml.dump(theory), encoding="utf-8") + (tdir / OPERATORFILE).write_text(yaml.dump(operator), encoding="utf-8") + (tdir / RECIPESDIR).mkdir() + (tdir / PARTSDIR).mkdir() + (tdir / OPERATORSDIR).mkdir() @staticmethod def extract(path: os.PathLike, filename: str) -> str: @@ -473,7 +479,7 @@ def extract(path: os.PathLike, filename: str) -> str: @property def theory(self) -> dict: - return yaml.safe_load(self.extract(self.path, "theory.yaml")) + return yaml.safe_load(self.extract(self.path, THEORYFILE)) @property def theory_card(self) -> dict: @@ -481,7 +487,7 @@ def theory_card(self) -> dict: @property def operator_card(self) -> dict: - return yaml.safe_load(self.extract(self.path, "operator.yaml")) + return yaml.safe_load(self.extract(self.path, OPERATORFILE)) @classmethod def detached(cls, theory: dict, operator: dict, path: pathlib.Path): @@ -587,8 +593,8 @@ def load(cls, path: os.PathLike): if not tarfile.is_tarfile(path): raise ValueError("EKO: the corresponding file is not a valid tar archive") - theory = yaml.safe_load(cls.extract(path, "theory.yaml")) - operator = yaml.safe_load(cls.extract(path, "operator.yaml")) + theory = yaml.safe_load(cls.extract(path, THEORYFILE)) + operator = yaml.safe_load(cls.extract(path, OPERATORFILE)) eko = cls.detached(theory, operator, path=path) logger.info(f"Operator loaded from path '{path}'") From a8a5818eb007cd1cc3b8815216a7784f001f7e0a Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 17:57:29 +0200 Subject: [PATCH 095/148] Refactor save, to take stream as input --- src/eko/output/struct.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index b1997482e..a2f395ac4 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -12,7 +12,7 @@ import tempfile import time from dataclasses import dataclass, fields -from typing import BinaryIO, Dict, Literal, Optional, Tuple +from typing import BinaryIO, Dict, Literal, Optional import lz4.frame import numpy as np @@ -100,6 +100,11 @@ class Operator(DictLike): To be used to hold the result of a computed 4-dim operator (from a given scale to another given one). + Note + ---- + IO works with streams in memory, in order to avoid intermediate write on + disk (keep read from and write to tar file only). + Attributes ---------- operator: np.ndarray @@ -113,12 +118,8 @@ class Operator(DictLike): operator: np.ndarray error: Optional[np.ndarray] = None - # IO works with streams in memory, in order to avoid intermediate write on - # disk (keep read from and write to tar file only) - - def save(self, compress: bool = True) -> Tuple[io.BytesIO, bool]: + def save(self, stream: io.BytesIO, compress: bool = True) -> bool: """Save content of operator to bytes.""" - stream = io.BytesIO() if self.error is None: np.save(stream, self.operator) else: @@ -129,9 +130,8 @@ def save(self, compress: bool = True) -> Tuple[io.BytesIO, bool]: if compress: stream = io.BytesIO(lz4.frame.compress(stream.read())) - # return the stream ready to be read, and the type of array dumped (i.e. - # 'npy' or 'npz') - return stream, self.error is None + # return the type of array dumped (i.e. 'npy' or 'npz') + return self.error is None @classmethod def load(cls, stream: BinaryIO, compressed: bool = True): From 1be6fa4e8faedfa49065fbacfa5adcb86b3554d0 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Wed, 27 Jul 2022 23:46:35 +0200 Subject: [PATCH 096/148] Replace duplicated string by function call --- src/eko/output/struct.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index a2f395ac4..d3ba4fd42 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -264,16 +264,20 @@ def __post_init__(self): if not tarfile.is_tarfile(self.path): raise ValueError("EKO: the corresponding file is not a valid tar archive") + @staticmethod + def opname(q2: float) -> str: + return f"operators/{q2:8.2f}" + def __getitem__(self, q2: float): # TODO: autoload op = self._operators[q2] if op is not None: return op - start = f"operators/{q2:8.2f}" - with tarfile.open(self.path) as tar: - names = list(filter(lambda n: n.startswith(start), tar.getnames())) + names = list( + filter(lambda n: n.startswith(self.opname(q2)), tar.getnames()) + ) if len(names) == 0: raise ValueError(f"Q2 value '{q2}' not available in '{self.path}'") @@ -304,7 +308,7 @@ def __setitem__(self, q2: float, op: Operator, compress: bool = True): if compress: suffix += ".lz4" - info = tarfile.TarInfo(name=f"operators/{q2:8.2f}.{suffix}") + info = tarfile.TarInfo(name=f"{self.opname(q2)}.{suffix}") info.size = len(stream.getbuffer()) info.mtime = time.time() info.mode = 436 From 1227f764a344957322c112889b6224acbae0ff29 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 08:36:00 +0200 Subject: [PATCH 097/148] Fix operator save regression Since now stream is passed as input, we can't overwrite the variable to write compressed content, but instead we have to replace stream content --- src/eko/output/struct.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index d3ba4fd42..af53f6189 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -128,7 +128,13 @@ def save(self, stream: io.BytesIO, compress: bool = True) -> bool: # compress if requested if compress: - stream = io.BytesIO(lz4.frame.compress(stream.read())) + compressed = lz4.frame.compress(stream.read()) + # remove previous content + stream.seek(0) + stream.truncate() + # write compressed data + stream.write(compressed) + stream.seek(0) # return the type of array dumped (i.e. 'npy' or 'npz') return self.error is None @@ -302,7 +308,9 @@ def __setitem__(self, q2: float, op: Operator, compress: bool = True): if not isinstance(op, Operator): raise ValueError("Only an Operator can be added to an EKO") - stream, without_err = op.save(compress) + stream = io.BytesIO() + without_err = op.save(stream, compress) + stream.seek(0) suffix = "npy" if without_err else "npz" if compress: @@ -310,7 +318,7 @@ def __setitem__(self, q2: float, op: Operator, compress: bool = True): info = tarfile.TarInfo(name=f"{self.opname(q2)}.{suffix}") info.size = len(stream.getbuffer()) - info.mtime = time.time() + info.mtime = int(time.time()) info.mode = 436 # info.uname = os.getlogin() # info.gname = os.getlogin() From 008800066fa1e38fbced8cb6119fae9f44a2f738 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 08:48:37 +0200 Subject: [PATCH 098/148] Remove dead code from legacy --- src/eko/output/legacy.py | 118 +++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 68 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 81d0d9755..541f43b6c 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Support legacy storage formats.""" import dataclasses import io import os @@ -15,7 +16,7 @@ from . import struct -def get_raw(eko: struct.EKO, binarize: bool = True, skip_q2_grid: bool = False): +def get_raw(eko: struct.EKO, binarize: bool = True): """Serialize result as dict/YAML. This maps the original numpy matrices to lists. @@ -40,18 +41,15 @@ def get_raw(eko: struct.EKO, binarize: bool = True, skip_q2_grid: bool = False): out[f] = obj[f] # make operators raw - if not skip_q2_grid: - for q2, op in eko.items(): - q2 = float(q2) - out["Q2grid"][q2] = {} - if op is not None: - for k, v in dataclasses.asdict(op).items(): - if binarize: - out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) - else: - out["Q2grid"][q2][k] = v.tolist() - else: - out["Q2grid"] = obj["Q2grid"] + for q2, op in eko.items(): + q2 = float(q2) + out["Q2grid"][q2] = {} + if op is not None: + for k, v in dataclasses.asdict(op).items(): + if binarize: + out["Q2grid"][q2][k] = lz4.frame.compress(v.tobytes()) + else: + out["Q2grid"][q2][k] = v.tolist() return out @@ -60,7 +58,6 @@ def dump_yaml( obj: struct.EKO, stream: Optional[TextIO] = None, binarize: bool = True, - skip_q2_grid: bool = False, ): """Serialize result as YAML. @@ -70,9 +67,6 @@ def dump_yaml( if given, dump is written on it binarize : bool dump in binary format (instead of list format) - skip_q2_grid : bool - avoid dumping Q2grid (i.e. the actual operators) into the yaml - file (default: ``False``) Returns ------- @@ -81,7 +75,7 @@ def dump_yaml( Null, if written successfully to stream """ - out = get_raw(obj, binarize, skip_q2_grid=skip_q2_grid) + out = get_raw(obj, binarize) return yaml.dump(out, stream) @@ -89,9 +83,8 @@ def dump_yaml_to_file( obj: struct.EKO, filename: Union[str, os.PathLike], binarize: bool = True, - skip_q2_grid: bool = False, ): - """Writes YAML representation to a file. + """Write YAML representation to a file. Parameters ---------- @@ -99,9 +92,6 @@ def dump_yaml_to_file( target file name binarize : bool dump in binary format (instead of list format) - skip_q2_grid : bool - avoid dumping Q2grid (i.e. the actual operators) into the yaml - file (default: ``False``) Returns ------- @@ -110,12 +100,14 @@ def dump_yaml_to_file( """ with open(filename, "w", encoding="utf-8") as f: - ret = dump_yaml(obj, f, binarize, skip_q2_grid=skip_q2_grid) + ret = dump_yaml(obj, f, binarize) return ret def dump_tar(obj: struct.EKO, tarname: Union[str, os.PathLike]): - """Writes representation into a tar archive containing: + """Write representation into a tar archive. + + The written archive will contain: - metadata (in YAML) - operator (in numpy ``.npy`` format) @@ -143,10 +135,7 @@ def dump_tar(obj: struct.EKO, tarname: Union[str, os.PathLike]): for kind in ["operator", "error"]: elements = [] for q2, op in obj.items(): - if op is not None: - el = getattr(op, kind) - else: - el = np.zeros((len(obj.xgrid), 14, len(obj.xgrid), 14)) + el = getattr(op, kind) elements.append(el) operator = np.stack(elements) stream = io.BytesIO() @@ -159,22 +148,19 @@ def dump_tar(obj: struct.EKO, tarname: Union[str, os.PathLike]): tar.add(tmpdir, arcname=tarpath.stem) -def load_yaml(stream: TextIO, skip_q2_grid=False) -> struct.EKO: - """ - Load YAML representation from stream +def load_yaml(stream: TextIO) -> struct.EKO: + """Load YAML representation from stream. Parameters ---------- - stream : TextIO - source stream - skip_q2_grid : bool - avoid loading Q2grid (i.e. the actual operators) from the yaml - file (default: ``False``) + stream : TextIO + source stream Returns ------- - obj : output - loaded object + obj : output + loaded object + """ obj = yaml.safe_load(stream) len_tpids = len(obj["rotations"]["targetpids"]) @@ -185,57 +171,53 @@ def load_yaml(stream: TextIO, skip_q2_grid=False) -> struct.EKO: for k, v in obj["rotations"].items(): obj["rotations"][k] = np.array(v) # make operators numpy - if not skip_q2_grid: - for op in obj["Q2grid"].values(): - for k, v in op.items(): - if isinstance(v, list): - v = np.array(v) - elif isinstance(v, bytes): - v = np.frombuffer(lz4.frame.decompress(v)) - v = v.reshape(len_tpids, len_tgrid, len_ipids, len_igrid) - op[k] = v + for op in obj["Q2grid"].values(): + for k, v in op.items(): + if isinstance(v, list): + v = np.array(v) + elif isinstance(v, bytes): + v = np.frombuffer(lz4.frame.decompress(v)) + v = v.reshape(len_tpids, len_tgrid, len_ipids, len_igrid) + op[k] = v + return struct.EKO.new(theory={}, operator=obj) -def load_yaml_from_file( - filename: Union[str, os.PathLike], skip_q2_grid: bool = False -) -> struct.EKO: - """ - Load YAML representation from file +def load_yaml_from_file(filename: Union[str, os.PathLike]) -> struct.EKO: + """Load YAML representation from file. Parameters ---------- - filename : str - source file name - skip_q2_grid : bool - avoid loading Q2grid (i.e. the actual operators) from the yaml - file (default: ``False``) + filename : str + source file name Returns ------- - obj : output - loaded object + obj : output + loaded object + """ obj = None with open(filename, encoding="utf-8") as o: - obj = load_yaml(o, skip_q2_grid) + obj = load_yaml(o) return obj def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: - """ - Load tar representation from file (compliant with :meth:`dump_tar` - output). + """Load tar representation from file. + + Compliant with :meth:`dump_tar` output. Parameters ---------- - tarname : str - source tar name + tarname : str + source tar name Returns ------- - obj : output - loaded object + obj : output + loaded object + """ tarpath = pathlib.Path(tarname) From 21f7b430662a2a029193b3f6e74d995eaca31afe Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 09:02:01 +0200 Subject: [PATCH 099/148] Remove dead code from manipulate --- src/eko/output/manipulate.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 9d4af4105..899f21d51 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -19,16 +19,16 @@ def xgrid_reshape( inputgrid: Optional[np.ndarray] = None, inplace: bool = True, ): - """Change the operators to have in the output targetgrid and/or in the input inputgrid. + """Reinterpolate operators on output and/or input grids. The operation is in-place. Parameters ---------- targetgrid : None or list - xgrid for the target + xgrid for the target (output PDF) inputgrid : None or list - xgrid for the input + xgrid for the input (input PDF) """ # calling with no arguments is an error @@ -76,8 +76,6 @@ def xgrid_reshape( # build new grid for q2, elem in eko.items(): - if elem is None: - continue ops = elem.operator errs = elem.error if targetgrid is not None and inputgrid is None: @@ -137,11 +135,10 @@ def flavor_reshape( inv_inputpids = np.linalg.inv(inputpids) # build new grid + # TODO: restore items # for q2, elem in eko.items(): for q2 in eko.Q2grid: elem = eko[q2] - if elem is None: - continue ops = elem.operator errs = elem.error if targetpids is not None and inputpids is None: From e4078f77c2c57a0dc084489efd900a8d19d89da0 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 09:15:08 +0200 Subject: [PATCH 100/148] Remove dead code from struct --- src/eko/output/struct.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index af53f6189..cfefe8f00 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -45,7 +45,6 @@ class DictLike: def __init__(self, **kwargs): """Empty initializer.""" - pass @classmethod def from_dict(cls, dictionary): @@ -491,14 +490,18 @@ def extract(path: os.PathLike, filename: str) -> str: @property def theory(self) -> dict: + # TODO: make an actual attribute, move load to `theory_card`, and the + # type of the attribute will be `eko.runcards.TheoryCard` return yaml.safe_load(self.extract(self.path, THEORYFILE)) @property def theory_card(self) -> dict: + # TODO: return `eko.runcards.TheoryCard` return self.theory @property def operator_card(self) -> dict: + # TODO: return `eko.runcards.OperatorCard` return yaml.safe_load(self.extract(self.path, OPERATORFILE)) @classmethod From e6d5a36102ad549dca3c2a76788f5311ae946a6b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 11:41:36 +0200 Subject: [PATCH 101/148] Document dataclass attributes in place --- doc/source/conf.py | 3 ++ src/eko/output/struct.py | 102 +++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index eee87debb..2f1becb11 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -103,6 +103,9 @@ # The name of the Pygments (syntax highlighting) style to use. pygments_style = None +# Criterion to sort class members +autodoc_member_order = "bysource" + # A string to be included at the beginning of all files shared = here / "shared" rst_prolog = "\n".join( diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index cfefe8f00..f9ecfe4ea 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -104,21 +104,33 @@ class Operator(DictLike): IO works with streams in memory, in order to avoid intermediate write on disk (keep read from and write to tar file only). - Attributes - ---------- - operator: np.ndarray - content of the evolution operator - error: np.ndarray or None - errors on individual operator elements (mainly used for integration - error, but it can host any kind of error) - """ operator: np.ndarray + """Content of the evolution operator.""" error: Optional[np.ndarray] = None + """Errors on individual operator elements (mainly used for integration + error, but it can host any kind of error). + """ + + def save(self, stream: BinaryIO, compress: bool = True) -> bool: + """Save content of operator to bytes. + + Parameters + ---------- + stream: BinaryIO + a stream where to save the operator content (in order to be able to + perform the operation both on disk and in memory) + compress: bool + flag to control optional compression (default: `True`) + + Returns + ------- + bool + whether the operator saved contained or not the error (this control + even the format, ``npz`` with errors, ``npy`` otherwise) - def save(self, stream: io.BytesIO, compress: bool = True) -> bool: - """Save content of operator to bytes.""" + """ if self.error is None: np.save(stream, self.operator) else: @@ -140,7 +152,22 @@ def save(self, stream: io.BytesIO, compress: bool = True) -> bool: @classmethod def load(cls, stream: BinaryIO, compressed: bool = True): - """Load operator from bytes.""" + """Load operator from bytes. + + Parameters + ---------- + stream: BinaryIO + a stream to load the operator from (to support the operation both on + disk and in memory) + compressed: bool + declare whether the stream is or is not compressed (default: `True`) + + Returns + ------- + Operator + the loaded instance + + """ if compressed: stream = io.BytesIO(lz4.frame.decompress(stream.read())) content = np.load(stream) @@ -161,6 +188,8 @@ def load(cls, stream: BinaryIO, compressed: bool = True): @dataclass class Debug(DictLike): + """Debug configurations.""" + skip_singlet: bool = False skip_non_singlet: bool = False @@ -208,42 +237,18 @@ class EKO: loading is done by this class (for the Python library, other implementations are possible and encouraged). - For this reason, a core component of an :cls:`EKO` object is a path, + For this reason, a core component of an :class:`EKO` object is a path, referring to the location on disk of the corresponding operator. - Any :cls:`EKO` has an associated path: + Any :class:`EKO` has an associated path: - - for the computed object, it corresponds to the path where the actual - result of the computation is already saved - - for a new object, it is the path at which any result of final or - intermediate computation is stored, as soon as it is produced + - for the computed object, it corresponds to the path where the actual + result of the computation is already saved + - for a new object, it is the path at which any result of final or + intermediate computation is stored, as soon as it is produced The computation can be stopped at any time, without the loss of any of the intermediate results. - Attributes - ---------- - path : pathlib.Path - path on disk, to which this object is linked (and for which it is - essentially an interface) - Q02 : float - inital scale - xgrid : interpolation.XGrid - momentum fraction internal grid - pids : np.ndarray - array of integers, corresponding to internal PIDs - configs : Configs - specific configuration to be used during the calculation of these - operators - rotations : Rotations - manipulation information, describing the current status of the EKO (e.g. - `inputgrid` and `targetgrid`) - debug : Debug - debug configurations - version : str - library version used to create the corresponding file - data_version : str - specs version, to which the file adheres - """ # operators cache, contains the Q2 grid information @@ -252,16 +257,31 @@ class EKO: # ----------------- # mandatory, identifying features path: pathlib.Path + """Path on disk, to which this object is linked (and for which it is + essentially an interface). + """ Q02: float + """Inital scale.""" xgrid: interpolation.XGrid + """Momentum fraction internal grid.""" pids: np.ndarray + """Array of integers, corresponding to internal PIDs.""" # collections configs: Configs + """Specific configuration to be used during the calculation of these + operators. + """ rotations: Rotations + """Manipulation information, describing the current status of the EKO (e.g. + `inputgrid` and `targetgrid`). + """ debug: Debug + """Debug configurations.""" # tagging information version: str = vmod.__version__ + """Library version used to create the corresponding file.""" data_version: str = vmod.__data_version__ + """Specs version, to which the file adheres.""" def __post_init__(self): if self.path.suffix != ".tar": From a57be9d459b02cbcc03f5aceee3abd8209d1070e Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 11:42:39 +0200 Subject: [PATCH 102/148] Fix tests --- benchmarks/lha_paper_bench.py | 4 +- src/eko/output/legacy.py | 15 ++++---- src/eko/output/manipulate.py | 5 +-- tests/conftest.py | 7 +++- tests/eko/test_output.py | 66 ++++++++++++++++----------------- tests/eko/test_output_struct.py | 16 ++++---- tests/ekomark/test_apply.py | 39 ++++++++----------- 7 files changed, 72 insertions(+), 80 deletions(-) diff --git a/benchmarks/lha_paper_bench.py b/benchmarks/lha_paper_bench.py index 9d3bc36d1..451c638d1 100644 --- a/benchmarks/lha_paper_bench.py +++ b/benchmarks/lha_paper_bench.py @@ -224,8 +224,8 @@ def benchmark_sv(self, pto): obj = BenchmarkVFNS() # obj = BenchmarkFFNS() - # obj.benchmark_plain(0) - obj.benchmark_sv(2) + obj.benchmark_plain(0) + # obj.benchmark_sv(2) # # VFNS benchmarks with LHA settings # programs = ["LHA", "pegasus", "apfel"] diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 541f43b6c..97e8962ca 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -221,20 +221,20 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: """ tarpath = pathlib.Path(tarname) + operator_grid = {} with tempfile.TemporaryDirectory() as tmpdir: tmpdir = pathlib.Path(tmpdir) with tarfile.open(tarpath, "r") as tar: tar.extractall(tmpdir) - # metadata = cls(**{str(k): v for k, v in obj.items() if k != "Q2grid"}) - # metadata["Q2grid"] = list(obj["Q2grid"].keys()) - + # load metadata innerdir = list(tmpdir.glob("*"))[0] yamlname = innerdir / "metadata.yaml" with open(yamlname, encoding="utf-8") as fd: metadata = yaml.safe_load(fd) + # get actual grids grids = {} for fp in innerdir.glob("*.npy.lz4"): with lz4.frame.open(fp, "rb") as fd: @@ -245,13 +245,12 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: fp.unlink() q2grid = metadata["Q2grid"] - operator_grid = {} for q2, slices in zip(q2grid, zip(*grids.values())): - operator_grid[q2] = struct.Operator(**dict(zip(grids.keys(), slices))) - metadata["Q2grid"] = operator_grid + operator_grid[q2] = dict(zip(grids.keys(), slices)) + # now eveything is in place eko = struct.EKO.new(theory={}, operator=metadata) - for q2, op in metadata["Q2grid"].items(): - eko[q2] = op + for q2, op in operator_grid.items(): + eko[q2] = struct.Operator.from_dict(op) return eko diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 899f21d51..b437ba1c6 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -135,10 +135,7 @@ def flavor_reshape( inv_inputpids = np.linalg.inv(inputpids) # build new grid - # TODO: restore items - # for q2, elem in eko.items(): - for q2 in eko.Q2grid: - elem = eko[q2] + for q2, elem in eko.items(): ops = elem.operator errs = elem.error if targetpids is not None and inputpids is None: diff --git a/tests/conftest.py b/tests/conftest.py index b5fe84e14..d7b0734ba 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,6 +8,8 @@ import numpy as np import pytest +from eko import output + @pytest.fixture def cd(): @@ -82,7 +84,10 @@ def fake_output(self): ), Q2grid=Q2grid, ) - return d + obj = output.EKO.new(theory={}, operator=d) + for q2, op in Q2grid.items(): + obj[q2] = output.struct.Operator.from_dict(op) + return obj, d @pytest.fixture diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 52aefc18e..1959dfa99 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -31,8 +31,8 @@ def chk_keys(a, b): class TestLegacy: def test_io(self, fake_output, tmp_path): # create object - o1 = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + o1, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) # test streams @@ -41,19 +41,19 @@ def test_io(self, fake_output, tmp_path): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid.raw, fake_output["xgrid"]) - np.testing.assert_almost_equal(o2.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o1.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal(o2.xgrid.raw, fake_card["xgrid"]) # fake output files fpyaml = tmp_path / "test.yaml" legacy.dump_yaml_to_file(o1, fpyaml) # fake input file o3 = legacy.load_yaml_from_file(fpyaml) - np.testing.assert_almost_equal(o3.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o3.xgrid.raw, fake_card["xgrid"]) # repeat for tar fptar = tmp_path / "test.tar" legacy.dump_tar(o1, fptar) o4 = legacy.load_tar(fptar) - np.testing.assert_almost_equal(o4.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o4.xgrid.raw, fake_card["xgrid"]) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): legacy.dump_tar(o1, fn) @@ -61,8 +61,8 @@ def test_io(self, fake_output, tmp_path): def test_rename_issue81(self, fake_output): # https://github.com/N3PDF/eko/issues/81 # create object - o1 = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + o1, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) with tempfile.TemporaryDirectory() as folder: @@ -75,12 +75,12 @@ def test_rename_issue81(self, fake_output): shutil.move(fp1, fp2) # reload o4 = legacy.load_tar(fp2) - np.testing.assert_almost_equal(o4.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o4.xgrid.raw, fake_card["xgrid"]) def test_io_bin(self, fake_output): # create object - o1 = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + o1, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) # test streams stream = io.StringIO() @@ -88,15 +88,15 @@ def test_io_bin(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid.raw, fake_output["xgrid"]) - np.testing.assert_almost_equal(o2.xgrid.raw, fake_output["xgrid"]) + np.testing.assert_almost_equal(o1.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal(o2.xgrid.raw, fake_card["xgrid"]) class TestManipulate: def test_xgrid_reshape(self, fake_output): # create object xg = np.geomspace(1e-5, 1.0, 21) - o1 = output.EKO.new(theory={}, operator=fake_output) + o1, _fake_card = fake_output o1.xgrid = xg o1.rotations.targetgrid = xg o1.rotations.inputgrid = xg @@ -143,8 +143,8 @@ def test_xgrid_reshape(self, fake_output): def test_reshape_io(self, fake_output): # create object - o1 = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + o1, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) o2 = copy.deepcopy(o1) manipulate.xgrid_reshape(o2, [0.1, 1.0], [0.1, 1.0]) @@ -157,28 +157,26 @@ def test_reshape_io(self, fake_output): o3 = legacy.load_yaml(stream) chk_keys(o1.raw, o3.raw) - def test_flavor_reshape(self, fake_output): + def test_flavor_reshape(self, fake_output, tmp_path): # create object xg = np.geomspace(1e-5, 1.0, 21) - o1 = output.EKO.new(theory={}, operator=fake_output) + o1, _fake_card = fake_output o1.xgrid = xg o1.rotations.targetgrid = xg o1.rotations.inputgrid = xg - o1._operators = { - 10: output.Operator.from_dict( - dict( - operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], - error=np.zeros((2, len(xg), 2, len(xg))), - ) + o1[10.0] = output.Operator.from_dict( + dict( + operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], + error=np.zeros((2, len(xg), 2, len(xg))), ) - } + ) # only target target_r = np.array([[1, -1], [1, 1]]) - ot = copy.deepcopy(o1) + ot = o1.deepcopy(tmp_path / "ot.tar") manipulate.flavor_reshape(ot, target_r) chk_keys(o1.raw, ot.raw) assert ot[10].operator.shape == (2, len(xg), 2, len(xg)) - ott = copy.deepcopy(ot) + ott = ot.deepcopy(tmp_path / "ott.tar") manipulate.flavor_reshape(ott, np.linalg.inv(target_r)) np.testing.assert_allclose(ott[10].operator, o1[10].operator) with pytest.warns(Warning): @@ -188,7 +186,7 @@ def test_flavor_reshape(self, fake_output): # only input input_r = np.array([[1, -1], [1, 1]]) - oi = copy.deepcopy(o1) + oi = o1.deepcopy(tmp_path / "oi.tar") manipulate.flavor_reshape(oi, inputpids=input_r) chk_keys(o1.raw, oi.raw) assert oi[10].operator.shape == (2, len(xg), 2, len(xg)) @@ -201,7 +199,7 @@ def test_flavor_reshape(self, fake_output): np.testing.assert_allclose(oii[10].operator, o1[10].operator) # both - oit = copy.deepcopy(o1) + oit = o1.deepcopy(tmp_path / "oit.tar") manipulate.flavor_reshape( oit, np.array([[1, -1], [1, 1]]), np.array([[1, -1], [1, 1]]) ) @@ -210,9 +208,9 @@ def test_flavor_reshape(self, fake_output): np.testing.assert_allclose(oit[10].operator, op[0], atol=1e-10) # error with pytest.raises(ValueError): - manipulate.flavor_reshape(copy.deepcopy(o1)) + manipulate.flavor_reshape(o1.deepcopy(tmp_path / "fail.tar")) - def test_to_evol(self, fake_factory): + def test_to_evol(self, fake_factory, tmp_path): xgrid = np.array([0.5, 1.0]) interpolation_polynomial_degree = 1 interpolation_is_log = False @@ -239,11 +237,11 @@ def test_to_evol(self, fake_factory): ) o00 = output.EKO.new(theory={}, operator=d) o00[q2_out] = output.Operator(**Q2grid[q2_out]) - o01 = copy.deepcopy(o00) + o01 = o00.deepcopy(tmp_path / "o01.tar") manipulate.to_evol(o01) - o10 = copy.deepcopy(o00) + o10 = o00.deepcopy(tmp_path / "o10.tar") manipulate.to_evol(o10, False, True) - o11 = copy.deepcopy(o00) + o11 = o00.deepcopy(tmp_path / "o11.tar") manipulate.to_evol(o11, True, True) chk_keys(o00.raw, o11.raw) diff --git a/tests/eko/test_output_struct.py b/tests/eko/test_output_struct.py index 01bbe4456..c850766a0 100644 --- a/tests/eko/test_output_struct.py +++ b/tests/eko/test_output_struct.py @@ -6,11 +6,11 @@ class TestLegacy: def test_items(self, fake_output): """Test autodump, autoload, and manual unload.""" - eko = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + eko, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): eko[q2] = output.Operator.from_dict(op) - q2 = next(iter(fake_output["Q2grid"])) + q2 = next(iter(fake_card["Q2grid"])) eko._operators[q2] = None assert isinstance(eko[q2], struct.Operator) @@ -22,8 +22,8 @@ def test_items(self, fake_output): def test_iter(self, fake_output): """Test managed iteration.""" - eko = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + eko, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): eko[q2] = output.Operator.from_dict(op) q2prev = None @@ -35,11 +35,11 @@ def test_iter(self, fake_output): def test_context_operator(self, fake_output): """Test automated handling through context.""" - eko = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): + eko, fake_card = fake_output + for q2, op in fake_card["Q2grid"].items(): eko[q2] = output.Operator.from_dict(op) - q2 = next(iter(fake_output["Q2grid"])) + q2 = next(iter(fake_card["Q2grid"])) with eko.operator(q2) as op: assert isinstance(op, struct.Operator) diff --git a/tests/ekomark/test_apply.py b/tests/ekomark/test_apply.py index 1f048322a..48b044636 100644 --- a/tests/ekomark/test_apply.py +++ b/tests/ekomark/test_apply.py @@ -1,28 +1,24 @@ # -*- coding: utf-8 -*- import numpy as np -from eko import output from ekomark import apply class TestApply: def test_apply(self, fake_output, fake_pdf): - q2_out = list(fake_output["Q2grid"].keys())[0] - # create object - o = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): - o[q2] = output.Operator.from_dict(op) + o, fake_card = fake_output + q2_out = list(fake_card["Q2grid"].keys())[0] # fake pdfs pdf_grid = apply.apply_pdf(o, fake_pdf) - assert len(pdf_grid) == len(fake_output["Q2grid"]) + assert len(pdf_grid) == len(fake_card["Q2grid"]) pdfs = pdf_grid[q2_out]["pdfs"] - assert list(pdfs.keys()) == fake_output["rotations"]["targetpids"] - ref_pid1 = fake_output["Q2grid"][q2_out]["operator"][0, :, 1, :] @ np.ones( - len(fake_output["xgrid"]) + assert list(pdfs.keys()) == fake_card["rotations"]["targetpids"] + ref_pid1 = fake_card["Q2grid"][q2_out]["operator"][0, :, 1, :] @ np.ones( + len(fake_card["xgrid"]) ) np.testing.assert_allclose(pdfs[0], ref_pid1) - ref_pid2 = fake_output["Q2grid"][q2_out]["operator"][1, :, 1, :] @ np.ones( - len(fake_output["xgrid"]) + ref_pid2 = fake_card["Q2grid"][q2_out]["operator"][1, :, 1, :] @ np.ones( + len(fake_card["xgrid"]) ) np.testing.assert_allclose(pdfs[1], ref_pid2) # rotate to target_grid @@ -30,30 +26,27 @@ def test_apply(self, fake_output, fake_pdf): pdf_grid = apply.apply_pdf(o, fake_pdf, target_grid) assert len(pdf_grid) == 1 pdfs = pdf_grid[q2_out]["pdfs"] - assert list(pdfs.keys()) == fake_output["rotations"]["targetpids"] + assert list(pdfs.keys()) == fake_card["rotations"]["targetpids"] def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): - q2_out = list(fake_output["Q2grid"].keys())[0] - # create object - o = output.EKO.new(theory={}, operator=fake_output) - for q2, op in fake_output["Q2grid"].items(): - o[q2] = output.Operator.from_dict(op) + o, fake_card = fake_output + q2_out = list(fake_card["Q2grid"].keys())[0] # fake pdfs monkeypatch.setattr( "eko.basis_rotation.rotate_flavor_to_evolution", np.ones((2, 2)) ) monkeypatch.setattr( "eko.basis_rotation.flavor_basis_pids", - fake_output["rotations"]["targetpids"], + fake_card["rotations"]["targetpids"], ) fake_evol_basis = ("a", "b") monkeypatch.setattr("eko.basis_rotation.evol_basis", fake_evol_basis) pdf_grid = apply.apply_pdf(o, fake_pdf, rotate_to_evolution_basis=True) - assert len(pdf_grid) == len(fake_output["Q2grid"]) + assert len(pdf_grid) == len(fake_card["Q2grid"]) pdfs = pdf_grid[q2_out]["pdfs"] assert list(pdfs.keys()) == list(fake_evol_basis) ref_a = ( - fake_output["Q2grid"][q2_out]["operator"][0, :, 1, :] - + fake_output["Q2grid"][q2_out]["operator"][1, :, 1, :] - ) @ np.ones(len(fake_output["xgrid"])) + fake_card["Q2grid"][q2_out]["operator"][0, :, 1, :] + + fake_card["Q2grid"][q2_out]["operator"][1, :, 1, :] + ) @ np.ones(len(fake_card["xgrid"])) np.testing.assert_allclose(pdfs["a"], ref_a) From 16c6fdda3c968805b42843fa96e1f54102bc61a5 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 11:52:37 +0200 Subject: [PATCH 103/148] Document configs and rotations, remove useless default constructor The default is useless because the class already have defaults: everything to None --- src/eko/output/struct.py | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index f9ecfe4ea..bd36612e7 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -191,32 +191,50 @@ class Debug(DictLike): """Debug configurations.""" skip_singlet: bool = False + """Whether to skip QCD singlet computation.""" skip_non_singlet: bool = False + """Whether to skip QCD non-singlet computation.""" @dataclass class Configs(DictLike): + """Solution specific configurations.""" + ev_op_max_order: int + """Maximum order to use in U matrices expansion. + Used only in ``perturbative`` solutions. + """ ev_op_iterations: int + """Number of intervals in which to break the global path.""" interpolation_polynomial_degree: int + """Degree of elements of the intepolation polynomial basis.""" interpolation_is_log: bool + r"""Whether to use polynomials in :math:`\log(x)`. + If `false`, polynomials are in :math:`x`. + """ backward_inversion: Literal["exact", "expanded"] + """Which method to use for backward matching conditions.""" n_integration_cores: int = 1 + """Number of cores used to parallelize integration.""" @dataclass class Rotations(DictLike): + """Rotations related configurations. + + Here "Rotation" is intended in a broad sense: it includes both rotations in + flavor space (labeled with suffix `pids`) and in :math:`x`-space (labeled + with suffix `grid`). + Rotations in :math:`x`-space correspond to reinterpolate the result on a + different basis of polynomials. + + """ + targetgrid: Optional[np.ndarray] = None inputgrid: Optional[np.ndarray] = None targetpids: Optional[np.ndarray] = None inputpids: Optional[np.ndarray] = None - @classmethod - def default(cls, xgrid: interpolation.XGrid, pids: np.ndarray): - return cls( - targetgrid=xgrid.raw, inputgrid=xgrid.raw, targetpids=pids, inputpids=pids - ) - @dataclass class EKO: @@ -561,7 +579,7 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): Q02=float(operator["Q0"] ** 2), _operators={q2: None for q2 in operator["Q2grid"]}, configs=Configs.from_dict(operator["configs"]), - rotations=Rotations.default(xgrid, pids), + rotations=Rotations(), debug=Debug.from_dict(operator.get("debug", {})), ) From eba344cfe698c69a5d310a786c2f2e7d77e79c06 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 11:59:47 +0200 Subject: [PATCH 104/148] Restore defaults, since None unsupported in manipulate --- src/eko/output/struct.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index bd36612e7..59983c977 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -235,6 +235,37 @@ class Rotations(DictLike): targetpids: Optional[np.ndarray] = None inputpids: Optional[np.ndarray] = None + @classmethod + def default(cls, xgrid: interpolation.XGrid, pids: np.ndarray): + """Create instance with default rotations. + + Default is no rotation at all, that you can also specify leaving the + rotations empty. + This method instead set them to the internal values, such that the + rotation results in an identity, and no operation is performed. + + Note + ---- + Setting them to `None` is not currently an option, since methods to + perform manipulations do not recognize this value. + + Parameters + ---------- + xgrid: interpolation.XGrid + the :math:`x`-grid used internally for computation + pids: np.ndarray + the PIDs used internally for computation + + Returns + ------- + Rotations + the instance with default values + + """ + return cls( + targetgrid=xgrid.raw, inputgrid=xgrid.raw, targetpids=pids, inputpids=pids + ) + @dataclass class EKO: @@ -579,7 +610,7 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): Q02=float(operator["Q0"] ** 2), _operators={q2: None for q2 in operator["Q2grid"]}, configs=Configs.from_dict(operator["configs"]), - rotations=Rotations(), + rotations=Rotations.default(xgrid, pids), debug=Debug.from_dict(operator.get("debug", {})), ) From ff02f256e75510fc88ee7135618f1d1dfc3296dd Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 16:29:06 +0200 Subject: [PATCH 105/148] Serialize properly grids --- src/eko/interpolation.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index afe56a458..86554463e 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -452,6 +452,13 @@ def size(self) -> int: def tolist(self) -> list: return self.raw.tolist() + def dump(self) -> dict: + return dict(grid=self.tolist(), log=self.log) + + @classmethod + def load(cls, doc: dict): + return cls(doc["grid"], log=doc["log"]) + class InterpolatorDispatcher: """ From d529c4b351034cde2eae048a4a9ba205be54ae95 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 16:31:02 +0200 Subject: [PATCH 106/148] Extend compatibility layer --- src/eko/compatibility.py | 46 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index 34d44b4f1..aa3dd61a0 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -5,33 +5,38 @@ """ import copy +from typing import Optional -def update(theory, operators): - """ - Upgrade the legacy theory and observable runcards with the new settings. +def update(theory: dict, operators: Optional[dict]): + """Upgrade the legacy theory and observable runcards with the new settings. Parameters ---------- - theory : dict - theory runcard - observables : dict - observable runcard + theory : dict + theory runcard + observables : dict or None + observable runcard (default: `None`) Returns ------- - new_theory : dict - upgraded theory runcard - new_obs : dict - upgraded observable runcard + new_theory : dict + upgraded theory runcard + new_obs : dict + upgraded observable runcard + """ new_theory = copy.deepcopy(theory) new_operators = copy.deepcopy(operators) + if "alphaqed" in new_theory: new_theory["alphaem"] = new_theory.pop("alphaqed") if "QED" in new_theory: new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) + if operators is not None: + if new_operators is None: + raise ValueError("Unreachable.") if "configs" not in operators: raise ValueError("No subsections, old format.") @@ -41,21 +46,26 @@ def update(theory, operators): max_order, new_theory["order"][1], ) + + new_operators["rotations"]["xgrid"] = operators["xgrid"] + for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): + new_operators["rotations"][f"_{basis}"] = operators["rotations"][basis] + return new_theory, new_operators -def update_theory(theory): - """ - Upgrade the legacy theory runcards with the new settings. +def update_theory(theory: dict): + """Upgrade the legacy theory runcards with the new settings. Parameters ---------- - theory : dict - theory runcard + theory : dict + theory runcard Returns ------- - new_theory : dict - upgraded theory runcard + new_theory : dict + upgraded theory runcard + """ return update(theory, None)[0] From d74207ef90272f21c3d9d84a1da91434b52c0d7f Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 16:47:46 +0200 Subject: [PATCH 107/148] Fix compatibility tests --- src/eko/compatibility.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index aa3dd61a0..2cc4f5c32 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -35,21 +35,22 @@ def update(theory: dict, operators: Optional[dict]): new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) if operators is not None: - if new_operators is None: - raise ValueError("Unreachable.") - if "configs" not in operators: - raise ValueError("No subsections, old format.") + assert new_operators is not None - max_order = new_operators["configs"]["ev_op_max_order"] + new_operators["configs"] = {} + new_operators["rotations"] = {} + new_operators["debug"] = {} + + max_order = operators["ev_op_max_order"] if isinstance(max_order, int): new_operators["configs"]["ev_op_max_order"] = ( max_order, new_theory["order"][1], ) - new_operators["rotations"]["xgrid"] = operators["xgrid"] + new_operators["rotations"]["xgrid"] = operators["interpolation_xgrid"] for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): - new_operators["rotations"][f"_{basis}"] = operators["rotations"][basis] + new_operators["rotations"][f"_{basis}"] = operators[basis] return new_theory, new_operators From cdaed45d9d543c0846c045186000167fba64ab1b Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 17:00:41 +0200 Subject: [PATCH 108/148] Skip compatibility for new cards --- src/eko/compatibility.py | 2 +- tests/eko/test_compatibility.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index 2cc4f5c32..a1c10aa36 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -34,7 +34,7 @@ def update(theory: dict, operators: Optional[dict]): if "QED" in new_theory: new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) - if operators is not None: + if operators is not None and "configs" not in operators: assert new_operators is not None new_operators["configs"] = {} diff --git a/tests/eko/test_compatibility.py b/tests/eko/test_compatibility.py index d5c4e3a41..2d4c0b25b 100644 --- a/tests/eko/test_compatibility.py +++ b/tests/eko/test_compatibility.py @@ -23,7 +23,7 @@ def test_compatibility(): def test_compatibility_operators(): _, new_operator = compatibility.update(theory1, operator_dict) - assert not isinstance(new_operator["configs"]["ev_op_max_order"], int) + assert isinstance(new_operator["configs"]["ev_op_max_order"], int) - with pytest.raises(ValueError): + with pytest.raises(KeyError): compatibility.update(theory1, {}) From 5f457a4d34715340113685eb2123280506434296 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 17:02:20 +0200 Subject: [PATCH 109/148] Load legacy YAML output --- src/eko/output/legacy.py | 44 ++++++++++++---- src/eko/output/manipulate.py | 16 +++--- src/eko/output/struct.py | 91 +++++++++++++++++---------------- src/eko/runner.py | 2 +- tests/conftest.py | 49 ++++++++++++++---- tests/eko/test_compatibility.py | 22 +++++--- tests/eko/test_output.py | 38 +++++++------- 7 files changed, 164 insertions(+), 98 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 97e8962ca..72b31698a 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- """Support legacy storage formats.""" +import copy import dataclasses import io import os @@ -37,7 +38,7 @@ def get_raw(eko: struct.EKO, binarize: bool = True): # prepare output dict out = {"Q2grid": {}, "eko_version": version.__version__} # dump raw elements - for f in ["Q0", "xgrid", "configs", "rotations"]: + for f in ["Q0", "configs", "rotations"]: out[f] = obj[f] # make operators raw @@ -54,6 +55,32 @@ def get_raw(eko: struct.EKO, binarize: bool = True): return out +def upgrade(raw: dict) -> dict: + """Upgrade raw representation to new layout. + + Parameters + ---------- + raw: dict + legacy raw representation of Output + + Returns + ------- + dict + upgraded dict representation + + """ + upgraded = copy.deepcopy(raw) + + if "rotations" not in raw: + upgraded["rotations"] = {} + upgraded["rotations"]["xgrid"] = raw["interpolation_xgrid"] + upgraded["rotations"]["pids"] = raw["pids"] + for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): + upgraded["rotations"][f"_{basis}"] = raw[basis] + + return upgraded + + def dump_yaml( obj: struct.EKO, stream: Optional[TextIO] = None, @@ -162,14 +189,11 @@ def load_yaml(stream: TextIO) -> struct.EKO: loaded object """ - obj = yaml.safe_load(stream) - len_tpids = len(obj["rotations"]["targetpids"]) - len_ipids = len(obj["rotations"]["inputpids"]) - len_tgrid = len(obj["rotations"]["targetgrid"]) - len_igrid = len(obj["rotations"]["inputgrid"]) - # cast lists to numpy - for k, v in obj["rotations"].items(): - obj["rotations"][k] = np.array(v) + obj = upgrade(yaml.safe_load(stream)) + len_tpids = len(obj["rotations"]["_targetpids"]) + len_ipids = len(obj["rotations"]["_inputpids"]) + len_tgrid = len(obj["rotations"]["_targetgrid"]) + len_igrid = len(obj["rotations"]["_inputgrid"]) # make operators numpy for op in obj["Q2grid"].values(): for k, v in op.items(): @@ -232,7 +256,7 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: innerdir = list(tmpdir.glob("*"))[0] yamlname = innerdir / "metadata.yaml" with open(yamlname, encoding="utf-8") as fd: - metadata = yaml.safe_load(fd) + metadata = upgrade(yaml.safe_load(fd)) # get actual grids grids = {} diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index b437ba1c6..6dbe90254 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -17,7 +17,6 @@ def xgrid_reshape( eko: EKO, targetgrid: Optional[np.ndarray] = None, inputgrid: Optional[np.ndarray] = None, - inplace: bool = True, ): """Reinterpolate operators on output and/or input grids. @@ -63,7 +62,7 @@ def xgrid_reshape( False, ) target_rot = b.get_interpolation(targetgrid) - eko.rotations.targetgrid = np.array(targetgrid) + eko.rotations._targetgrid = interpolation.XGrid(targetgrid) if inputgrid is not None: b = interpolation.InterpolatorDispatcher( inputgrid, @@ -72,7 +71,7 @@ def xgrid_reshape( False, ) input_rot = b.get_interpolation(eko.rotations.inputgrid) - eko.rotations.inputgrid = np.array(inputgrid) + eko.rotations._inputgrid = interpolation.XGrid(inputgrid) # build new grid for q2, elem in eko.items(): @@ -97,7 +96,6 @@ def flavor_reshape( eko: EKO, targetpids: Optional[np.ndarray] = None, inputpids: Optional[np.ndarray] = None, - inplace: bool = True, ): """Change the operators to have in the output targetpids and/or in the input inputpids. @@ -155,12 +153,12 @@ def flavor_reshape( # drop PIDs - keeping them int nevertheless # there is no meaningful way to set them in general, after rotation if inputpids is not None: - eko.rotations.inputpids = [0] * len(eko.rotations.inputpids) + eko.rotations._inputpids = np.array([0] * len(eko.rotations.inputpids)) if targetpids is not None: - eko.rotations.targetpids = [0] * len(eko.rotations.targetpids) + eko.rotations._targetpids = np.array([0] * len(eko.rotations.targetpids)) -def to_evol(eko: EKO, source: bool = True, target: bool = False, inplace: bool = True): +def to_evol(eko: EKO, source: bool = True, target: bool = False): """Rotate the operator into evolution basis. This also assigns also the pids. The operation is in-place. @@ -179,6 +177,6 @@ def to_evol(eko: EKO, source: bool = True, target: bool = False, inplace: bool = flavor_reshape(eko, inputpids=inputpids, targetpids=targetpids) # assign pids if source: - eko.rotations.inputpids = br.evol_basis_pids + eko.rotations._inputpids = br.evol_basis_pids if target: - eko.rotations.targetpids = br.evol_basis_pids + eko.rotations._targetpids = br.evol_basis_pids diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 59983c977..8564b897b 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -19,7 +19,6 @@ import numpy.lib.npyio as npyio import yaml -from .. import basis_rotation as br from .. import interpolation from .. import version as vmod @@ -86,6 +85,8 @@ def raw(self): # replace numpy scalars with python ones elif isinstance(value, float): value = float(value) + elif isinstance(value, interpolation.XGrid): + value = value.dump() dictionary[field.name] = value @@ -230,41 +231,46 @@ class Rotations(DictLike): """ - targetgrid: Optional[np.ndarray] = None - inputgrid: Optional[np.ndarray] = None - targetpids: Optional[np.ndarray] = None - inputpids: Optional[np.ndarray] = None - - @classmethod - def default(cls, xgrid: interpolation.XGrid, pids: np.ndarray): - """Create instance with default rotations. + xgrid: interpolation.XGrid + """Momentum fraction internal grid.""" + pids: np.ndarray + """Array of integers, corresponding to internal PIDs.""" + _targetgrid: Optional[interpolation.XGrid] = None + _inputgrid: Optional[interpolation.XGrid] = None + _targetpids: Optional[np.ndarray] = None + _inputpids: Optional[np.ndarray] = None - Default is no rotation at all, that you can also specify leaving the - rotations empty. - This method instead set them to the internal values, such that the - rotation results in an identity, and no operation is performed. + def __post_init__(self): + for attr in ("xgrid", "_inputgrid", "_targetgrid"): + value = getattr(self, attr) + if isinstance(value, np.ndarray): + setattr(self, attr, interpolation.XGrid(value)) + elif not isinstance(value, interpolation.XGrid): + setattr(self, attr, interpolation.XGrid.load(value)) - Note - ---- - Setting them to `None` is not currently an option, since methods to - perform manipulations do not recognize this value. + @property + def inputpids(self) -> np.ndarray: + if self._inputpids is None: + return self.pids + return self._inputpids - Parameters - ---------- - xgrid: interpolation.XGrid - the :math:`x`-grid used internally for computation - pids: np.ndarray - the PIDs used internally for computation + @property + def targetpids(self) -> np.ndarray: + if self._targetpids is None: + return self.pids + return self._targetpids - Returns - ------- - Rotations - the instance with default values + @property + def inputgrid(self) -> interpolation.XGrid: + if self._inputgrid is None: + return self.xgrid + return self._inputgrid - """ - return cls( - targetgrid=xgrid.raw, inputgrid=xgrid.raw, targetpids=pids, inputpids=pids - ) + @property + def targetgrid(self) -> interpolation.XGrid: + if self._targetgrid is None: + return self.xgrid + return self._targetgrid @dataclass @@ -311,10 +317,6 @@ class EKO: """ Q02: float """Inital scale.""" - xgrid: interpolation.XGrid - """Momentum fraction internal grid.""" - pids: np.ndarray - """Array of integers, corresponding to internal PIDs.""" # collections configs: Configs """Specific configuration to be used during the calculation of these @@ -332,6 +334,16 @@ class EKO: data_version: str = vmod.__data_version__ """Specs version, to which the file adheres.""" + @property + def xgrid(self) -> interpolation.XGrid: + """Momentum fraction internal grid.""" + return self.rotations.xgrid + + @xgrid.setter + def xgrid(self, value: interpolation.XGrid): + """Set `xgrid` value.""" + self.rotations.xgrid = value + def __post_init__(self): if self.path.suffix != ".tar": raise ValueError("Not a valid path for an EKO") @@ -600,17 +612,12 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): the generated structure """ - xgrid = interpolation.XGrid(operator["xgrid"]) - pids = np.array(operator.get("pids", np.array(br.flavor_basis_pids))) - return cls( path=path, - xgrid=xgrid, - pids=pids, Q02=float(operator["Q0"] ** 2), _operators={q2: None for q2 in operator["Q2grid"]}, configs=Configs.from_dict(operator["configs"]), - rotations=Rotations.default(xgrid, pids), + rotations=Rotations.from_dict(operator["rotations"]), debug=Debug.from_dict(operator.get("debug", {})), ) @@ -688,8 +695,6 @@ def load(cls, path: os.PathLike): def raw(self): return dict( path=str(self.path), - xgrid=self.xgrid.tolist(), - pids=self.pids.tolist(), Q0=float(np.sqrt(self.Q02)), Q2grid=self.Q2grid, configs=self.configs.raw, diff --git a/src/eko/runner.py b/src/eko/runner.py index 3abcc069d..6ce6ccfd8 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -100,7 +100,7 @@ def get_output(self) -> EKO: def similar_to_none(name: str) -> Optional[np.ndarray]: grid = self.post_process[name] - default = self.out.xgrid.grid if "grid" in name else self.out.pids + default = self.out.xgrid.grid if "grid" in name else self.out.rotations.pids if grid is None or ( len(grid) == default.size and np.allclose(grid, default, atol=1e-12) ): diff --git a/tests/conftest.py b/tests/conftest.py index d7b0734ba..6b2e12a3e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,7 @@ import pytest from eko import output +from eko.output import legacy @pytest.fixture @@ -55,8 +56,7 @@ def mk_g(self, q2s, lpids, lx): } return Q2grid - def fake_output(self): - # build data + def mk_dump(self) -> dict: xgrid = np.array([0.5, 1.0]) interpolation_polynomial_degree = 1 interpolation_is_log = False @@ -64,14 +64,14 @@ def fake_output(self): q2_ref = 1 q2_out = 2 Q2grid = self.mk_g([q2_out], len(pids), len(xgrid)) - d = dict( - xgrid=xgrid, - pids=pids, + return dict( rotations=dict( - targetgrid=xgrid, - inputgrid=xgrid, - inputpids=pids, - targetpids=pids, + xgrid=xgrid, + pids=pids, + _targetgrid=xgrid, + _inputgrid=xgrid, + _inputpids=pids, + _targetpids=pids, ), Q0=np.sqrt(q2_ref), couplings=dict(), @@ -84,8 +84,32 @@ def fake_output(self): ), Q2grid=Q2grid, ) + + def fake_output(self): + d = self.mk_dump() + # build data obj = output.EKO.new(theory={}, operator=d) - for q2, op in Q2grid.items(): + for q2, op in d["Q2grid"].items(): + obj[q2] = output.struct.Operator.from_dict(op) + return obj, d + + def fake_legacy(self): + d = self.mk_dump() + bases = d["rotations"].copy() + + d["inputgrid"] = bases["_inputgrid"] + d["targetgrid"] = bases["_targetgrid"] + d["inputpids"] = bases["_inputpids"] + d["targetpids"] = bases["_targetpids"] + + d["interpolation_xgrid"] = bases["xgrid"] + d["pids"] = bases["pids"] + + del d["rotations"] + + # build data + obj = output.EKO.new(theory={}, operator=legacy.upgrade(d)) + for q2, op in d["Q2grid"].items(): obj[q2] = output.struct.Operator.from_dict(op) return obj, d @@ -100,6 +124,11 @@ def fake_output(): return FakeOutput().fake_output() +@pytest.fixture +def fake_legacy(): + return FakeOutput().fake_legacy() + + @pytest.fixture def fake_lhapdf(tmp_path): def lhapdf_paths(): diff --git a/tests/eko/test_compatibility.py b/tests/eko/test_compatibility.py index 2d4c0b25b..6925751a1 100644 --- a/tests/eko/test_compatibility.py +++ b/tests/eko/test_compatibility.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -import pytest - from eko import compatibility theory1 = { @@ -17,13 +15,23 @@ def test_compatibility(): assert new_theory["order"][0] == theory1["PTO"] + 1 -operator_dict = {"configs": {"ev_op_max_order": 2}} +def operator_dict(xgrid, pids): + return dict( + ev_op_max_order=2, + interpolation_xgrid=xgrid, + inputgrid=xgrid, + targetgrid=xgrid, + pids=pids, + inputpids=pids, + targetpids=pids, + ) def test_compatibility_operators(): - _, new_operator = compatibility.update(theory1, operator_dict) + xgrid = [1e-3, 1e-2, 1e-1, 1.0] + pids = [21, -1, 1] - assert isinstance(new_operator["configs"]["ev_op_max_order"], int) + _, new_operator = compatibility.update(theory1, operator_dict(xgrid, pids)) - with pytest.raises(KeyError): - compatibility.update(theory1, {}) + assert new_operator is not None + assert not isinstance(new_operator["configs"]["ev_op_max_order"], int) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 1959dfa99..0e8b99740 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -29,9 +29,9 @@ def chk_keys(a, b): class TestLegacy: - def test_io(self, fake_output, tmp_path): + def test_io(self, fake_legacy, tmp_path): # create object - o1, fake_card = fake_output + o1, fake_card = fake_legacy for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) @@ -41,27 +41,27 @@ def test_io(self, fake_output, tmp_path): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid.raw, fake_card["xgrid"]) - np.testing.assert_almost_equal(o2.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal(o1.xgrid.raw, fake_card["interpolation_xgrid"]) + np.testing.assert_almost_equal(o2.xgrid.raw, fake_card["interpolation_xgrid"]) # fake output files fpyaml = tmp_path / "test.yaml" legacy.dump_yaml_to_file(o1, fpyaml) # fake input file o3 = legacy.load_yaml_from_file(fpyaml) - np.testing.assert_almost_equal(o3.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal(o3.xgrid.raw, fake_card["interpolation_xgrid"]) # repeat for tar fptar = tmp_path / "test.tar" legacy.dump_tar(o1, fptar) o4 = legacy.load_tar(fptar) - np.testing.assert_almost_equal(o4.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal(o4.xgrid.raw, fake_card["interpolation_xgrid"]) fn = "test" with pytest.raises(ValueError, match="wrong suffix"): legacy.dump_tar(o1, fn) - def test_rename_issue81(self, fake_output): + def test_rename_issue81(self, fake_legacy): # https://github.com/N3PDF/eko/issues/81 # create object - o1, fake_card = fake_output + o1, fake_card = fake_legacy for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) @@ -75,11 +75,13 @@ def test_rename_issue81(self, fake_output): shutil.move(fp1, fp2) # reload o4 = legacy.load_tar(fp2) - np.testing.assert_almost_equal(o4.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal( + o4.xgrid.raw, fake_card["interpolation_xgrid"] + ) - def test_io_bin(self, fake_output): + def test_io_bin(self, fake_legacy): # create object - o1, fake_card = fake_output + o1, fake_card = fake_legacy for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) # test streams @@ -88,8 +90,8 @@ def test_io_bin(self, fake_output): # rewind and read again stream.seek(0) o2 = legacy.load_yaml(stream) - np.testing.assert_almost_equal(o1.xgrid.raw, fake_card["xgrid"]) - np.testing.assert_almost_equal(o2.xgrid.raw, fake_card["xgrid"]) + np.testing.assert_almost_equal(o1.xgrid.raw, fake_card["interpolation_xgrid"]) + np.testing.assert_almost_equal(o2.xgrid.raw, fake_card["interpolation_xgrid"]) class TestManipulate: @@ -98,8 +100,8 @@ def test_xgrid_reshape(self, fake_output): xg = np.geomspace(1e-5, 1.0, 21) o1, _fake_card = fake_output o1.xgrid = xg - o1.rotations.targetgrid = xg - o1.rotations.inputgrid = xg + o1.rotations._targetgrid = xg + o1.rotations._inputgrid = xg o1._operators = { 10: output.Operator.from_dict( dict( @@ -111,7 +113,7 @@ def test_xgrid_reshape(self, fake_output): xgp = np.geomspace(1e-5, 1.0, 11) # only target ot = copy.deepcopy(o1) - manipulate.xgrid_reshape(ot, xgp, inplace=True) + manipulate.xgrid_reshape(ot, xgp) chk_keys(o1.raw, ot.raw) assert ot[10].operator.shape == (2, len(xgp), 2, len(xg)) ott = copy.deepcopy(o1) @@ -162,8 +164,8 @@ def test_flavor_reshape(self, fake_output, tmp_path): xg = np.geomspace(1e-5, 1.0, 21) o1, _fake_card = fake_output o1.xgrid = xg - o1.rotations.targetgrid = xg - o1.rotations.inputgrid = xg + o1.rotations._targetgrid = xg + o1.rotations._inputgrid = xg o1[10.0] = output.Operator.from_dict( dict( operator=eko_identity([1, 2, len(xg), 2, len(xg)])[0], From 0cd40bed210624ab2761851a6f8bae4fbd1cd992 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 17:32:15 +0200 Subject: [PATCH 110/148] Fix ekomark/apply --- src/eko/output/struct.py | 22 ++++++++++++++++++++++ src/ekomark/apply.py | 7 +++++-- tests/ekomark/test_apply.py | 20 ++++++++++---------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 8564b897b..5a40d4ab8 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -585,6 +585,28 @@ def operator_card(self) -> dict: # TODO: return `eko.runcards.OperatorCard` return yaml.safe_load(self.extract(self.path, OPERATORFILE)) + def interpolator( + self, mode_N: bool, use_target: bool + ) -> interpolation.InterpolatorDispatcher: + """Return associated interpolation. + + Paramters + --------- + mode_N : bool + interpolate in N-space? + use_target : bool + use target grid? If False, use input grid + + Returns + ------- + interpolation.InterpolatorDispatcher + interpolator + """ + grid = self.rotations.targetgrid if use_target else self.rotations.inputgrid + return interpolation.InterpolatorDispatcher( + grid.raw, self.configs.interpolation_polynomial_degree, grid.log, mode_N + ) + @classmethod def detached(cls, theory: dict, operator: dict, path: pathlib.Path): """Build the in-memory object alone. diff --git a/src/ekomark/apply.py b/src/ekomark/apply.py index d8dfa3f45..e738acdaf 100644 --- a/src/ekomark/apply.py +++ b/src/ekomark/apply.py @@ -60,7 +60,10 @@ def apply_pdf_flavor(eko, lhapdf_like, targetgrid=None, flavor_rotation=None): if not lhapdf_like.hasFlavor(pid): continue pdfs[j] = np.array( - [lhapdf_like.xfxQ2(pid, x, eko.Q02) / x for x in eko.rotations.inputgrid] + [ + lhapdf_like.xfxQ2(pid, x, eko.Q02) / x + for x in eko.rotations.inputgrid.raw + ] ) # build output @@ -87,7 +90,7 @@ def apply_pdf_flavor(eko, lhapdf_like, targetgrid=None, flavor_rotation=None): # rotate/interpolate to target grid if targetgrid is not None: - b = interpolation.InterpolatorDispatcher.from_dict(eko.raw, False) + b = eko.interpolator(False, True) rot = b.get_interpolation(targetgrid) for evpdf in out_grid.values(): for pdf_label in evpdf["pdfs"]: diff --git a/tests/ekomark/test_apply.py b/tests/ekomark/test_apply.py index 48b044636..46c6997c8 100644 --- a/tests/ekomark/test_apply.py +++ b/tests/ekomark/test_apply.py @@ -5,20 +5,20 @@ class TestApply: - def test_apply(self, fake_output, fake_pdf): - o, fake_card = fake_output + def test_apply(self, fake_legacy, fake_pdf): + o, fake_card = fake_legacy q2_out = list(fake_card["Q2grid"].keys())[0] # fake pdfs pdf_grid = apply.apply_pdf(o, fake_pdf) assert len(pdf_grid) == len(fake_card["Q2grid"]) pdfs = pdf_grid[q2_out]["pdfs"] - assert list(pdfs.keys()) == fake_card["rotations"]["targetpids"] + assert list(pdfs.keys()) == o.rotations.targetpids ref_pid1 = fake_card["Q2grid"][q2_out]["operator"][0, :, 1, :] @ np.ones( - len(fake_card["xgrid"]) + len(o.rotations.xgrid) ) np.testing.assert_allclose(pdfs[0], ref_pid1) ref_pid2 = fake_card["Q2grid"][q2_out]["operator"][1, :, 1, :] @ np.ones( - len(fake_card["xgrid"]) + len(o.rotations.xgrid) ) np.testing.assert_allclose(pdfs[1], ref_pid2) # rotate to target_grid @@ -26,10 +26,10 @@ def test_apply(self, fake_output, fake_pdf): pdf_grid = apply.apply_pdf(o, fake_pdf, target_grid) assert len(pdf_grid) == 1 pdfs = pdf_grid[q2_out]["pdfs"] - assert list(pdfs.keys()) == fake_card["rotations"]["targetpids"] + assert list(pdfs.keys()) == o.rotations.targetpids - def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): - o, fake_card = fake_output + def test_apply_flavor(self, fake_legacy, fake_pdf, monkeypatch): + o, fake_card = fake_legacy q2_out = list(fake_card["Q2grid"].keys())[0] # fake pdfs monkeypatch.setattr( @@ -37,7 +37,7 @@ def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): ) monkeypatch.setattr( "eko.basis_rotation.flavor_basis_pids", - fake_card["rotations"]["targetpids"], + o.rotations.targetpids, ) fake_evol_basis = ("a", "b") monkeypatch.setattr("eko.basis_rotation.evol_basis", fake_evol_basis) @@ -48,5 +48,5 @@ def test_apply_flavor(self, fake_output, fake_pdf, monkeypatch): ref_a = ( fake_card["Q2grid"][q2_out]["operator"][0, :, 1, :] + fake_card["Q2grid"][q2_out]["operator"][1, :, 1, :] - ) @ np.ones(len(fake_card["xgrid"])) + ) @ np.ones(len(o.rotations.xgrid)) np.testing.assert_allclose(pdfs["a"], ref_a) From fae26225d351893bff0d6d1f1265aa77e8d35927 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 17:47:09 +0200 Subject: [PATCH 111/148] Drop InterpolatorDispatcher constructor from dict --- src/eko/interpolation.py | 83 ++++++++++++--------------------- src/eko/runner.py | 10 +++- tests/eko/test_interpolation.py | 63 ++++++------------------- tests/eko/test_runner.py | 11 ++++- 4 files changed, 62 insertions(+), 105 deletions(-) diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index 86554463e..a530b41c3 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -8,9 +8,11 @@ """ import logging import math +from typing import Sequence, Union import numba as nb import numpy as np +import numpy.typing as npt import scipy.special as sp logger = logging.getLogger(__name__) @@ -415,7 +417,7 @@ def __call__(self, *args, **kwargs): class XGrid: - def __init__(self, xgrid: np.ndarray, log: bool = True): + def __init__(self, xgrid: Union[Sequence, npt.NDArray], log: bool = True): ugrid = np.array(np.unique(xgrid), np.float_) if len(xgrid) != len(ugrid): raise ValueError(f"xgrid is not unique: {xgrid}") @@ -459,6 +461,20 @@ def dump(self) -> dict: def load(cls, doc: dict): return cls(doc["grid"], log=doc["log"]) + @classmethod + def fromcard(cls, value: list, log: bool): + if len(value) == 0: + raise ValueError("Empty xgrid!") + + if value[0] == "make_grid": + xgrid = make_grid(*value[1:]) + elif value[0] == "make_lambert_grid": + xgrid = make_lambert_grid(*value[1:]) + else: + xgrid = np.array(value) + + return cls(xgrid, log=log) + class InterpolatorDispatcher: """ @@ -482,8 +498,12 @@ class InterpolatorDispatcher: if true compiles the function on N, otherwise compiles x """ - def __init__(self, xgrid, polynomial_degree, log=True, mode_N=True): - xgrid = XGrid(xgrid, log=log) + def __init__( + self, xgrid: Union[XGrid, Sequence, npt.NDArray], polynomial_degree, mode_N=True + ): + if not isinstance(xgrid, XGrid): + xgrid = XGrid(xgrid) + # sanity checks if polynomial_degree < 1: raise ValueError( @@ -498,12 +518,12 @@ def __init__(self, xgrid, polynomial_degree, log=True, mode_N=True): # Save the different variables self.xgrid = xgrid self.polynomial_degree = polynomial_degree - self.log = log + self.log = xgrid.log logger.info( "Interpolation: number of points = %d, polynomial degree = %d, logarithmic = %s", len(xgrid), polynomial_degree, - log, + xgrid.log, ) # Create blocks @@ -528,54 +548,11 @@ def __init__(self, xgrid, polynomial_degree, log=True, mode_N=True): basis_functions = [] for i in range(len(xgrid)): new_basis = BasisFunction( - xgrid.grid, i, list_of_blocks, mode_log=log, mode_N=mode_N + xgrid.grid, i, list_of_blocks, mode_log=xgrid.log, mode_N=mode_N ) basis_functions.append(new_basis) self.basis = basis_functions - @classmethod - def from_dict(cls, operators_card, mode_N=True): - """ - Create object from dictionary. - - .. list-table:: operators runcard parameters - :header-rows: 1 - - * - Name - - Type - - description - * - ``xgrid`` - - :py:obj:`list(float)` - - the interpolation grid - * - ``interpolation_polynomial_degree`` - - :py:obj:`int` - - polynomial degree of the interpolating function - * - ``interpolation_is_log`` - - :py:obj:`bool` - - use logarithmic interpolation? - - Parameters - ---------- - operators_card : dict - input configurations - """ - # load xgrid - xgrid = operators_card["xgrid"] - if len(xgrid) == 0: - raise ValueError("Empty xgrid!") - if xgrid[0] == "make_grid": - xgrid = make_grid(*xgrid[1:]) - elif xgrid[0] == "make_lambert_grid": - xgrid = make_lambert_grid(*xgrid[1:]) - is_log_interpolation = bool(operators_card["configs"]["interpolation_is_log"]) - polynom_rank = operators_card["configs"]["interpolation_polynomial_degree"] - return cls( - xgrid, - polynom_rank, - log=is_log_interpolation, - mode_N=mode_N, - ) - def __eq__(self, other): """Checks equality""" checks = [ @@ -639,11 +616,9 @@ def to_dict(self): full grid configuration """ ret = { - "xgrid": self.xgrid.tolist(), - "configs": { - "interpolation_polynomial_degree": self.polynomial_degree, - "interpolation_is_log": self.log, - }, + "xgrid": self.xgrid.dump(), + "polynomial_degree": self.polynomial_degree, + "is_log": self.log, } return ret diff --git a/src/eko/runner.py b/src/eko/runner.py index 6ce6ccfd8..0ff0301ca 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -54,7 +54,15 @@ def __init__(self, theory_card: dict, operators_card: dict): self._theory = new_theory # setup basis grid - bfd = interpolation.InterpolatorDispatcher.from_dict(new_operators) + bfd = interpolation.InterpolatorDispatcher( + xgrid=interpolation.XGrid( + new_operators["rotations"]["xgrid"], + log=new_operators["configs"]["interpolation_is_log"], + ), + polynomial_degree=new_operators["configs"][ + "interpolation_polynomial_degree" + ], + ) # setup the Threshold path, compute masses if necessary masses = None diff --git a/tests/eko/test_interpolation.py b/tests/eko/test_interpolation.py index 3c377300f..046b36f1c 100644 --- a/tests/eko/test_interpolation.py +++ b/tests/eko/test_interpolation.py @@ -100,62 +100,29 @@ def test_init(self): with pytest.raises(ValueError): interpolation.InterpolatorDispatcher([], 1) - def test_from_dict(self): - d = { - "xgrid": ["make_grid", 3, 3], - "configs": { - "interpolation_is_log": False, - "interpolation_polynomial_degree": 1, - }, - } - a = interpolation.InterpolatorDispatcher.from_dict(d) - np.testing.assert_array_almost_equal( - a.xgrid.grid, np.array([1e-7, 1e-4, 1e-1, 0.55, 1.0]) - ) - assert a.polynomial_degree == 1 - dd = { - "xgrid": ["make_lambert_grid", 20], - "configs": { - "interpolation_is_log": False, - "interpolation_polynomial_degree": 1, - }, - } - aa = interpolation.InterpolatorDispatcher.from_dict(dd) - assert len(aa.xgrid) == 20 - with pytest.raises(ValueError): - d = { - "xgrid": [], - "configs": { - "interpolation_is_log": False, - "interpolation_polynomial_degree": 1, - }, - } - interpolation.InterpolatorDispatcher.from_dict(d) - def test_eq(self): - a = interpolation.InterpolatorDispatcher( - np.linspace(0.1, 1, 10), 4, log=False, mode_N=False - ) - b = interpolation.InterpolatorDispatcher( - np.linspace(0.1, 1, 9), 4, log=False, mode_N=False - ) + # define grids + x9 = interpolation.XGrid(np.linspace(0.1, 1, 9), log=False) + x10 = interpolation.XGrid(np.linspace(0.1, 1, 10), log=False) + # test various options + a = interpolation.InterpolatorDispatcher(x10, 4, mode_N=False) + b = interpolation.InterpolatorDispatcher(x9, 4, mode_N=False) assert a != b - c = interpolation.InterpolatorDispatcher( - np.linspace(0.1, 1, 10), 3, log=False, mode_N=False - ) + c = interpolation.InterpolatorDispatcher(x10, 3, mode_N=False) assert a != c d = interpolation.InterpolatorDispatcher( - np.linspace(0.1, 1, 10), 4, log=True, mode_N=False + np.linspace(0.1, 1, 10), 4, mode_N=False ) assert a != d - e = interpolation.InterpolatorDispatcher( - np.linspace(0.1, 1, 10), 4, log=False, mode_N=False - ) + e = interpolation.InterpolatorDispatcher(x10, 4, mode_N=False) assert a == e # via dict dd = a.to_dict() assert isinstance(dd, dict) - assert a == interpolation.InterpolatorDispatcher.from_dict(dd) + assert a == interpolation.InterpolatorDispatcher( + interpolation.XGrid.load(dd["xgrid"]), + polynomial_degree=dd["polynomial_degree"], + ) def test_iter(self): xgrid = np.linspace(0.1, 1, 10) @@ -165,8 +132,8 @@ def test_iter(self): assert bf == inter_x[k] def test_get_interpolation(self): - xg = [0.5, 1.0] - inter_x = interpolation.InterpolatorDispatcher(xg, 1, False, False) + xg = interpolation.XGrid([0.5, 1.0], log=False) + inter_x = interpolation.InterpolatorDispatcher(xg, 1, False) i = inter_x.get_interpolation(xg) np.testing.assert_array_almost_equal(i, np.eye(len(xg))) # .75 is exactly inbetween diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 054d0970e..fc46dac4b 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -5,7 +5,7 @@ import eko import eko.interpolation -from eko import compatibility +from eko import basis_rotation as br theory_card = { "alphas": 0.35, @@ -38,7 +38,6 @@ } operators_card = { "Q2grid": [10, 100], - "xgrid": [0.01, 0.1, 1.0], "configs": { "interpolation_polynomial_degree": 1, "interpolation_is_log": True, @@ -47,6 +46,14 @@ "backward_inversion": "exact", "n_integration_cores": 1, }, + "rotations": { + "xgrid": [0.01, 0.1, 1.0], + "pids": np.array(br.flavor_basis_pids), + "inputgrid": None, + "targetgrid": None, + "inputpids": None, + "targetpids": None, + }, "debug": {"skip_singlet": True, "skip_non_singlet": True}, } From 3a799f82b7729242728685ce5333d48b0740ee00 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 17:55:29 +0200 Subject: [PATCH 112/148] Fix most interpolation tests --- tests/eko/test_interpolation.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/tests/eko/test_interpolation.py b/tests/eko/test_interpolation.py index 046b36f1c..df57bc341 100644 --- a/tests/eko/test_interpolation.py +++ b/tests/eko/test_interpolation.py @@ -141,14 +141,14 @@ def test_get_interpolation(self): np.testing.assert_array_almost_equal(i, [[0.5, 0.5]]) def test_evaluate_x(self): - xgrid = np.linspace(0.1, 1, 10) poly_degree = 4 for log in [True, False]: + xgrid = interpolation.XGrid(np.linspace(0.1, 1, 10), log=log) inter_x = interpolation.InterpolatorDispatcher( - xgrid, poly_degree, log=log, mode_N=False + xgrid, poly_degree, mode_N=False ) inter_N = interpolation.InterpolatorDispatcher( - xgrid, poly_degree, log=log, mode_N=True + xgrid, poly_degree, mode_N=True ) for x in [0.2, 0.5]: for bx, bN in zip(inter_x, inter_N): @@ -156,16 +156,14 @@ def test_evaluate_x(self): def test_math(self): """Test math properties of interpolator""" - xgrid = np.linspace(0.09, 1, 10) poly_deg = 4 for log in [True, False]: + xgrid = interpolation.XGrid(np.linspace(0.09, 1, 10), log=log) inter_x = interpolation.InterpolatorDispatcher( - xgrid, poly_deg, log=log, mode_N=False + xgrid, poly_deg, mode_N=False ) check_is_interpolator(inter_x) - inter_N = interpolation.InterpolatorDispatcher( - xgrid, poly_deg, log=log, mode_N=True - ) + inter_N = interpolation.InterpolatorDispatcher(xgrid, poly_deg, mode_N=True) check_correspondence_interpolators(inter_x, inter_N) @@ -176,8 +174,8 @@ def test_init(self): interpolation.BasisFunction([0.1, 1.0], 0, []) def test_eval_N(self): - xg = [0.0, 1.0] - inter_N = interpolation.InterpolatorDispatcher(xg, 1, log=False) + xg = interpolation.XGrid([0.0, 1.0], log=False) + inter_N = interpolation.InterpolatorDispatcher(xg, 1) # p_0(x) = 1-x -> \tilde p_0(N) = 1/N - 1/(N+1) p0N = inter_N[0] assert len(p0N.areas) == 1 @@ -210,7 +208,7 @@ def p1Nref(N, lnx): def test_log_eval_N(self): xg = [np.exp(-1), 1.0] - inter_N = interpolation.InterpolatorDispatcher(xg, 1, log=True) + inter_N = interpolation.InterpolatorDispatcher(xg, 1) # p_0(x) = -ln(x) p0N = inter_N[0] assert len(p0N.areas) == 1 @@ -282,9 +280,8 @@ def test_is_below_x(self): "res": [True, True, False, False, False], }, ]: - inter_x = interpolation.InterpolatorDispatcher( - cfg["xg"], cfg["pd"], log=log - ) + xg = interpolation.XGrid(cfg["xg"], log=log) + inter_x = interpolation.InterpolatorDispatcher(xg, cfg["pd"]) actual = [bf.is_below_x(cfg["x"]) for bf in inter_x] assert actual == cfg["res"] From aafa27fa38300476b71dc5e8f2e8e431d0d41caa Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 17:57:54 +0200 Subject: [PATCH 113/148] Fix get_interpolation test --- tests/eko/test_interpolation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/eko/test_interpolation.py b/tests/eko/test_interpolation.py index df57bc341..6096d4ffc 100644 --- a/tests/eko/test_interpolation.py +++ b/tests/eko/test_interpolation.py @@ -134,7 +134,7 @@ def test_iter(self): def test_get_interpolation(self): xg = interpolation.XGrid([0.5, 1.0], log=False) inter_x = interpolation.InterpolatorDispatcher(xg, 1, False) - i = inter_x.get_interpolation(xg) + i = inter_x.get_interpolation(xg.raw) np.testing.assert_array_almost_equal(i, np.eye(len(xg))) # .75 is exactly inbetween i = inter_x.get_interpolation([0.75]) From ac117bfa7e6ce125f02da9452a8f3a482a0fac88 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 17:58:06 +0200 Subject: [PATCH 114/148] On the way to fix test_utils --- src/eko/compatibility.py | 10 ++++++++++ src/eko/runner.py | 2 +- src/ekomark/data/operators.py | 29 +++++++++++++++-------------- tests/ekobox/test_evol_pdf.py | 4 ++-- tests/ekobox/test_utils.py | 8 ++++---- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index a1c10aa36..530d838f3 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -38,8 +38,18 @@ def update(theory: dict, operators: Optional[dict]): assert new_operators is not None new_operators["configs"] = {} + for k in ( + "interpolation_polynomial_degree", + "interpolation_is_log", + "ev_op_iterations", + "backward_inversion", + "n_integration_cores", + ): + new_operators["configs"][k] = operators[k] new_operators["rotations"] = {} new_operators["debug"] = {} + for k in ("debug_skip_non_singlet", "debug_skip_singlet"): + new_operators["debug"][k[len("debug_") :]] = operators[k] max_order = operators["ev_op_max_order"] if isinstance(max_order, int): diff --git a/src/eko/runner.py b/src/eko/runner.py index 0ff0301ca..5c354eaea 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -83,7 +83,7 @@ def __init__(self, theory_card: dict, operators_card: dict): } self.out = EKO.new( - theory=theory_card, operator=dict(Q0=np.sqrt(tc.q2_ref), **operators_card) + theory=theory_card, operator=dict(Q0=np.sqrt(tc.q2_ref), **new_operators) ) def get_output(self) -> EKO: diff --git a/src/ekomark/data/operators.py b/src/ekomark/data/operators.py index 146ed70c8..faab3960c 100644 --- a/src/ekomark/data/operators.py +++ b/src/ekomark/data/operators.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -""" -Operator card configurations. -""" +"""Operator card configurations.""" from banana.data import cartesian_product, sql from eko import interpolation @@ -11,17 +9,20 @@ default_card = dict( sorted( dict( - xgrid=interpolation.make_grid(30, 20).tolist(), - configs=dict( - interpolation_polynomial_degree=4, - interpolation_is_log=True, - ev_op_max_order=10, - ev_op_iterations=10, - backward_inversion="expanded", - n_integration_cores=0, - ), - debug=dict(skip_non_singlet=False, skip_singlet=False), + interpolation_xgrid=interpolation.make_grid(30, 20).tolist(), + interpolation_polynomial_degree=4, + interpolation_is_log=True, + ev_op_max_order=10, + ev_op_iterations=10, + backward_inversion="expanded", + n_integration_cores=0, + debug_skip_non_singlet=False, + debug_skip_singlet=False, Q2grid=[100], + inputgrid=None, + targetgrid=None, + inputpids=None, + targetpids=None, ).items() ) ) @@ -54,7 +55,7 @@ def build(update=None): """ - Generate all operator card updates + Generate all operator card updates. Parameters ---------- diff --git a/tests/ekobox/test_evol_pdf.py b/tests/ekobox/test_evol_pdf.py index d2d194802..cb8ffd703 100644 --- a/tests/ekobox/test_evol_pdf.py +++ b/tests/ekobox/test_evol_pdf.py @@ -11,8 +11,8 @@ op = oc.generate( [100.0], update={ - "xgrid": [0.1, 0.5, 1.0], - "configs": {"interpolation_polynomial_degree": 1}, + "interpolation_xgrid": [0.1, 0.5, 1.0], + "interpolation_polynomial_degree": 1, }, ) theory = tc.generate(0, 1.65) diff --git a/tests/ekobox/test_utils.py b/tests/ekobox/test_utils.py index ccf6198be..e773696bb 100644 --- a/tests/ekobox/test_utils.py +++ b/tests/ekobox/test_utils.py @@ -13,8 +13,8 @@ def test_ekos_product(): op1 = oc.generate( [60.0, 80.0, 100.0], update={ - "xgrid": [0.1, 0.5, 1.0], - "configs": {"interpolation_polynomial_degree": 1}, + "interpolation_xgrid": [0.1, 0.5, 1.0], + "interpolation_polynomial_degree": 1, }, ) theory1 = tc.generate(0, 5.0) @@ -22,8 +22,8 @@ def test_ekos_product(): op2 = oc.generate( [80.0, 100.0, 120.0], update={ - "xgrid": [0.1, 0.5, 1.0], - "configs": {"interpolation_polynomial_degree": 1}, + "interpolation_xgrid": [0.1, 0.5, 1.0], + "interpolation_polynomial_degree": 1, }, ) theory2 = tc.generate(0, 10.0) From 3767943fc117a53fa3636f0bd09acf674a829c1c Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 18:02:52 +0200 Subject: [PATCH 115/148] Propagate new InterpolatorDispatcher construction --- tests/eko/test_ev_op_grid.py | 16 ++++++++++++---- tests/eko/test_ev_operator.py | 26 ++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/tests/eko/test_ev_op_grid.py b/tests/eko/test_ev_op_grid.py index 0fee639e3..d2ae1519f 100644 --- a/tests/eko/test_ev_op_grid.py +++ b/tests/eko/test_ev_op_grid.py @@ -73,8 +73,12 @@ def _get_operator_grid(self, use_FFNS=True, theory_update=None): if theory_update is not None: theory_card.update(theory_update) # create objects - basis_function_dispatcher = interpolation.InterpolatorDispatcher.from_dict( - operators_card + basis_function_dispatcher = interpolation.InterpolatorDispatcher( + interpolation.XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], ) threshold_holder = ThresholdsAtlas.from_dict(theory_card) a_s = Couplings.from_dict(theory_card) @@ -91,8 +95,12 @@ def test_sanity(self): # errors with pytest.raises(ValueError): theory_card, operators_card = self._get_setup(True) - basis_function_dispatcher = interpolation.InterpolatorDispatcher.from_dict( - operators_card + basis_function_dispatcher = interpolation.InterpolatorDispatcher( + interpolation.XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], ) threshold_holder = ThresholdsAtlas.from_dict(theory_card) a_s = Couplings.from_dict(theory_card) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index 60ddd6355..f3e1b6935 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -11,7 +11,7 @@ from eko.couplings import Couplings from eko.evolution_operator import Operator, quad_ker from eko.evolution_operator.grid import OperatorGrid -from eko.interpolation import InterpolatorDispatcher +from eko.interpolation import InterpolatorDispatcher, XGrid from eko.kernels import non_singlet as ns from eko.kernels import singlet as s from eko.thresholds import ThresholdsAtlas @@ -228,7 +228,13 @@ def test_exponentiated(self): ocard, ThresholdsAtlas.from_dict(tcard), Couplings.from_dict(tcard), - InterpolatorDispatcher.from_dict(ocard), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), ) # setup objs o = Operator(g.config, g.managers, 3, 2.0, 10.0) @@ -245,7 +251,13 @@ def test_compute_parallel(self, monkeypatch): ocard, ThresholdsAtlas.from_dict(tcard), Couplings.from_dict(tcard), - InterpolatorDispatcher.from_dict(ocard), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), ) # setup objs o = Operator(g.config, g.managers, 3, 2.0, 10.0) @@ -276,7 +288,13 @@ def test_compute(self, monkeypatch): ocard, ThresholdsAtlas.from_dict(tcard), Couplings.from_dict(tcard), - InterpolatorDispatcher.from_dict(ocard), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), ) # setup objs o = Operator(g.config, g.managers, 3, 2.0, 10.0) From 4e9b67bed62e068e26abfc0accde687d6fad37a8 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 18:06:51 +0200 Subject: [PATCH 116/148] Propagate also into OME tests --- tests/eko/test_ome.py | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tests/eko/test_ome.py b/tests/eko/test_ome.py index c22b4762f..f288b5c57 100644 --- a/tests/eko/test_ome.py +++ b/tests/eko/test_ome.py @@ -9,7 +9,7 @@ from eko.couplings import Couplings from eko.evolution_operator.grid import OperatorGrid from eko.harmonics import compute_cache -from eko.interpolation import InterpolatorDispatcher +from eko.interpolation import InterpolatorDispatcher, XGrid from eko.matching_conditions.operator_matrix_element import ( A_non_singlet, A_singlet, @@ -350,7 +350,13 @@ def test_labels(self): operators_card, ThresholdsAtlas.from_dict(self.theory_card), Couplings.from_dict(self.theory_card), - InterpolatorDispatcher.from_dict(operators_card), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), ) o = OperatorMatrixElement( g.config, @@ -394,7 +400,13 @@ def test_compute_n3lo(self): self.operators_card, ThresholdsAtlas.from_dict(self.theory_card), Couplings.from_dict(self.theory_card), - InterpolatorDispatcher.from_dict(self.operators_card), + InterpolatorDispatcher( + XGrid( + self.operators_card["xgrid"], + log=self.operators_card["configs"]["interpolation_is_log"], + ), + self.operators_card["configs"]["interpolation_polynomial_degree"], + ), ) o = OperatorMatrixElement( g.config, @@ -429,7 +441,13 @@ def test_compute_lo(self): self.operators_card, ThresholdsAtlas.from_dict(self.theory_card), Couplings.from_dict(self.theory_card), - InterpolatorDispatcher.from_dict(self.operators_card), + InterpolatorDispatcher( + XGrid( + self.operators_card["xgrid"], + log=self.operators_card["configs"]["interpolation_is_log"], + ), + self.operators_card["configs"]["interpolation_polynomial_degree"], + ), ) o = OperatorMatrixElement( g.config, @@ -491,7 +509,13 @@ def test_compute_nlo(self): operators_card, ThresholdsAtlas.from_dict(t), Couplings.from_dict(t), - InterpolatorDispatcher.from_dict(operators_card), + InterpolatorDispatcher( + XGrid( + operators_card["xgrid"], + log=operators_card["configs"]["interpolation_is_log"], + ), + operators_card["configs"]["interpolation_polynomial_degree"], + ), ) o = OperatorMatrixElement( g.config, From 3e668ab71decc09e4f0a493710e0025ce3b61660 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 18:23:28 +0200 Subject: [PATCH 117/148] Fix test_utils --- src/eko/output/struct.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 5a40d4ab8..f34876029 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -19,6 +19,7 @@ import numpy.lib.npyio as npyio import yaml +from .. import basis_rotation as br from .. import interpolation from .. import version as vmod @@ -243,6 +244,8 @@ class Rotations(DictLike): def __post_init__(self): for attr in ("xgrid", "_inputgrid", "_targetgrid"): value = getattr(self, attr) + if value is None: + continue if isinstance(value, np.ndarray): setattr(self, attr, interpolation.XGrid(value)) elif not isinstance(value, interpolation.XGrid): @@ -634,12 +637,22 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): the generated structure """ + g = operator["rotations"] + g["pids"] = br.flavor_basis_pids + for k in ("xgrid", "_inputgrid", "_targetgrid"): + if operator["rotations"][k] is None: + continue + g[k] = { + "grid": operator["rotations"][k], + "log": operator["configs"]["interpolation_is_log"], + } + return cls( path=path, Q02=float(operator["Q0"] ** 2), _operators={q2: None for q2 in operator["Q2grid"]}, configs=Configs.from_dict(operator["configs"]), - rotations=Rotations.from_dict(operator["rotations"]), + rotations=Rotations.from_dict(g), debug=Debug.from_dict(operator.get("debug", {})), ) From fd632fc3884d84db021212ce79787f9391939682 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 18:24:53 +0200 Subject: [PATCH 118/148] Fix manipulate compatibility with InterpolatorDispatcher --- src/eko/interpolation.py | 2 +- src/eko/output/manipulate.py | 18 ++++++++---------- tests/eko/test_output.py | 10 ++++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/eko/interpolation.py b/src/eko/interpolation.py index a530b41c3..72c3f78b5 100644 --- a/src/eko/interpolation.py +++ b/src/eko/interpolation.py @@ -570,7 +570,7 @@ def __iter__(self): def __getitem__(self, item): return self.basis[item] - def get_interpolation(self, targetgrid): + def get_interpolation(self, targetgrid: Union[npt.NDArray, Sequence]): """Computes interpolation matrix between `targetgrid` and `xgrid`. .. math:: diff --git a/src/eko/output/manipulate.py b/src/eko/output/manipulate.py index 6dbe90254..5a91f114d 100644 --- a/src/eko/output/manipulate.py +++ b/src/eko/output/manipulate.py @@ -15,8 +15,8 @@ def xgrid_reshape( eko: EKO, - targetgrid: Optional[np.ndarray] = None, - inputgrid: Optional[np.ndarray] = None, + targetgrid: Optional[interpolation.XGrid] = None, + inputgrid: Optional[interpolation.XGrid] = None, ): """Reinterpolate operators on output and/or input grids. @@ -37,14 +37,14 @@ def xgrid_reshape( if ( targetgrid is not None and len(targetgrid) == len(eko.rotations.targetgrid) - and np.allclose(targetgrid, eko.rotations.targetgrid) + and np.allclose(targetgrid.raw, eko.rotations.targetgrid.raw) ): targetgrid = None warnings.warn("The new targetgrid is close to the current targetgrid") if ( inputgrid is not None and len(inputgrid) == len(eko.rotations.inputgrid) - and np.allclose(inputgrid, eko.rotations.inputgrid) + and np.allclose(inputgrid.raw, eko.rotations.inputgrid.raw) ): inputgrid = None warnings.warn("The new inputgrid is close to the current inputgrid") @@ -58,20 +58,18 @@ def xgrid_reshape( b = interpolation.InterpolatorDispatcher( eko.rotations.targetgrid, eko.configs.interpolation_polynomial_degree, - eko.configs.interpolation_is_log, False, ) - target_rot = b.get_interpolation(targetgrid) - eko.rotations._targetgrid = interpolation.XGrid(targetgrid) + target_rot = b.get_interpolation(targetgrid.raw) + eko.rotations._targetgrid = targetgrid if inputgrid is not None: b = interpolation.InterpolatorDispatcher( inputgrid, eko.configs.interpolation_polynomial_degree, - eko.configs.interpolation_is_log, False, ) - input_rot = b.get_interpolation(eko.rotations.inputgrid) - eko.rotations._inputgrid = interpolation.XGrid(inputgrid) + input_rot = b.get_interpolation(eko.rotations.inputgrid.raw) + eko.rotations._inputgrid = inputgrid # build new grid for q2, elem in eko.items(): diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index 0e8b99740..c176eca37 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -9,7 +9,7 @@ import pytest from eko import basis_rotation as br -from eko import output +from eko import interpolation, output from eko.output import legacy, manipulate @@ -97,7 +97,7 @@ def test_io_bin(self, fake_legacy): class TestManipulate: def test_xgrid_reshape(self, fake_output): # create object - xg = np.geomspace(1e-5, 1.0, 21) + xg = interpolation.XGrid(np.geomspace(1e-5, 1.0, 21)) o1, _fake_card = fake_output o1.xgrid = xg o1.rotations._targetgrid = xg @@ -110,7 +110,7 @@ def test_xgrid_reshape(self, fake_output): ) ) } - xgp = np.geomspace(1e-5, 1.0, 11) + xgp = interpolation.XGrid(np.geomspace(1e-5, 1.0, 11)) # only target ot = copy.deepcopy(o1) manipulate.xgrid_reshape(ot, xgp) @@ -149,7 +149,9 @@ def test_reshape_io(self, fake_output): for q2, op in fake_card["Q2grid"].items(): o1[q2] = output.Operator.from_dict(op) o2 = copy.deepcopy(o1) - manipulate.xgrid_reshape(o2, [0.1, 1.0], [0.1, 1.0]) + manipulate.xgrid_reshape( + o2, interpolation.XGrid([0.1, 1.0]), interpolation.XGrid([0.1, 1.0]) + ) manipulate.flavor_reshape(o2, inputpids=np.array([[1, -1], [1, 1]])) # dump stream = io.StringIO() From 984b6050fcca0a3359cce342f8ba10c17ddd34f9 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 18:33:17 +0200 Subject: [PATCH 119/148] Fix eko.interpolator --- src/eko/output/struct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index f34876029..96be975e5 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -607,7 +607,7 @@ def interpolator( """ grid = self.rotations.targetgrid if use_target else self.rotations.inputgrid return interpolation.InterpolatorDispatcher( - grid.raw, self.configs.interpolation_polynomial_degree, grid.log, mode_N + grid, self.configs.interpolation_polynomial_degree, mode_N ) @classmethod From 18c8c314166d5e81787cea1dd63de2b98425de36 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 18:38:19 +0200 Subject: [PATCH 120/148] Fix test_op_cards --- tests/ekobox/test_operators_card.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/ekobox/test_operators_card.py b/tests/ekobox/test_operators_card.py index de196191c..55fc51ffa 100644 --- a/tests/ekobox/test_operators_card.py +++ b/tests/ekobox/test_operators_card.py @@ -7,17 +7,15 @@ def test_generate_ocard(): op = oc.generate([10, 100]) assert op["Q2grid"] == [10, 100] - assert op["configs"]["interpolation_polynomial_degree"] == 4 + assert op["interpolation_polynomial_degree"] == 4 up_err = {"Prova": "Prova"} with pytest.raises(ValueError): op = oc.generate([10], update=up_err) - up = { - "configs": {"interpolation_polynomial_degree": 2, "interpolation_is_log": False} - } + up = {"interpolation_polynomial_degree": 2, "interpolation_is_log": False} op = oc.generate([100], update=up) assert op["Q2grid"] == [100] - assert op["configs"]["interpolation_polynomial_degree"] == 2 - assert op["configs"]["interpolation_is_log"] is False + assert op["interpolation_polynomial_degree"] == 2 + assert op["interpolation_is_log"] is False def test_dump_load_op_card(tmp_path, cd): From a9b4532972bc963c035df632f00dc69e8d108d4a Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 18:38:29 +0200 Subject: [PATCH 121/148] Fix compatibility tests --- src/eko/output/struct.py | 14 +++++++------- tests/eko/test_compatibility.py | 7 +++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 96be975e5..521b795d0 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -637,22 +637,22 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): the generated structure """ - g = operator["rotations"] - g["pids"] = br.flavor_basis_pids + bases = operator["rotations"] + bases["pids"] = np.array(br.flavor_basis_pids) for k in ("xgrid", "_inputgrid", "_targetgrid"): if operator["rotations"][k] is None: continue - g[k] = { - "grid": operator["rotations"][k], - "log": operator["configs"]["interpolation_is_log"], - } + bases[k] = interpolation.XGrid( + operator["rotations"][k], + log=operator["configs"]["interpolation_is_log"], + ) return cls( path=path, Q02=float(operator["Q0"] ** 2), _operators={q2: None for q2 in operator["Q2grid"]}, configs=Configs.from_dict(operator["configs"]), - rotations=Rotations.from_dict(g), + rotations=Rotations.from_dict(bases), debug=Debug.from_dict(operator.get("debug", {})), ) diff --git a/tests/eko/test_compatibility.py b/tests/eko/test_compatibility.py index 6925751a1..8bc46d3f0 100644 --- a/tests/eko/test_compatibility.py +++ b/tests/eko/test_compatibility.py @@ -18,7 +18,14 @@ def test_compatibility(): def operator_dict(xgrid, pids): return dict( ev_op_max_order=2, + ev_op_iterations=1, interpolation_xgrid=xgrid, + interpolation_polynomial_degree=4, + interpolation_is_log=True, + backward_inversion=None, + n_integration_cores=1, + debug_skip_singlet=False, + debug_skip_non_singlet=False, inputgrid=xgrid, targetgrid=xgrid, pids=pids, From 6cb49a2bc26e6a0605c5a11c4644cbf51d5832c0 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Thu, 28 Jul 2022 18:52:00 +0200 Subject: [PATCH 122/148] Fix info_file and its test --- src/ekobox/info_file.py | 29 +++++++++++++++-------------- tests/ekobox/test_info_file.py | 4 ++-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/ekobox/info_file.py b/src/ekobox/info_file.py index 668ffdd5c..8476d0bd5 100644 --- a/src/ekobox/info_file.py +++ b/src/ekobox/info_file.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""LHAPDF info file utilities.""" import copy import math @@ -6,24 +7,23 @@ def build(theory_card, operators_card, num_members, info_update): - """ - Generate a lhapdf info file from theory and operators card + """Generate a lhapdf info file from theory and operators card. Parameters ---------- - theory_card : dict - theory card - operators_card : dict - operators_card - num_members : int - number of pdf set members - info_update : dict - info to update + theory_card : dict + theory card + operators_card : dict + operators_card + num_members : int + number of pdf set members + info_update : dict + info to update Returns ------- - dict - info file in lhapdf format + dict + info file in lhapdf format """ template_info = copy.deepcopy(load.template_info) template_info["SetDesc"] = "Evolved PDF from " + str(theory_card["Q0"]) + " GeV" @@ -32,8 +32,9 @@ def build(theory_card, operators_card, num_members, info_update): template_info.update(info_update) template_info["NumFlavors"] = 14 template_info["Flavors"] = [-6, -5, -4, -3, -2, -1, 1, 2, 3, 4, 5, 6, 21, 22] - template_info["XMin"] = operators_card["xgrid"][0] - template_info["XMax"] = operators_card["xgrid"][-1] + # TODO actually point to input grid + template_info["XMin"] = operators_card["interpolation_xgrid"][0] + template_info["XMax"] = operators_card["interpolation_xgrid"][-1] template_info["NumMembers"] = num_members template_info["OrderQCD"] = theory_card["PTO"] template_info["QMin"] = round(math.sqrt(operators_card["Q2grid"][0]), 4) diff --git a/tests/ekobox/test_info_file.py b/tests/ekobox/test_info_file.py index 643d02745..8cdadabc4 100644 --- a/tests/ekobox/test_info_file.py +++ b/tests/ekobox/test_info_file.py @@ -20,5 +20,5 @@ def test_build(): assert info["NumMembers"] == 4 assert info["MTop"] == theory["mt"] np.testing.assert_allclose(info["QMin"], math.sqrt(op["Q2grid"][0]), rtol=1e-5) - assert info["XMin"] == op["xgrid"][0] - assert info["XMax"] == op["xgrid"][-1] == 1.0 + assert info["XMin"] == op["interpolation_xgrid"][0] + assert info["XMax"] == op["interpolation_xgrid"][-1] == 1.0 From 996fbf47d7e25e67ecc29e5aa70f5a91bac3ab31 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 19:31:42 +0200 Subject: [PATCH 123/148] Fix legacy tests --- src/eko/compatibility.py | 2 +- src/eko/output/legacy.py | 53 +++++++++++++++++++++++++--------------- src/eko/output/struct.py | 4 +++ tests/conftest.py | 25 ++++++++++--------- tests/eko/test_runner.py | 9 +++---- 5 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index 530d838f3..bc7fd0816 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -60,7 +60,7 @@ def update(theory: dict, operators: Optional[dict]): new_operators["rotations"]["xgrid"] = operators["interpolation_xgrid"] for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): - new_operators["rotations"][f"_{basis}"] = operators[basis] + new_operators["rotations"][f"{basis}"] = operators[basis] return new_theory, new_operators diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 72b31698a..e81eff76d 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -37,9 +37,18 @@ def get_raw(eko: struct.EKO, binarize: bool = True): # prepare output dict out = {"Q2grid": {}, "eko_version": version.__version__} + out["Q0"] = obj["Q0"] # dump raw elements - for f in ["Q0", "configs", "rotations"]: - out[f] = obj[f] + for sec in ["configs", "rotations"]: + for key, value in obj[sec].items(): + if key.startswith("_"): + key = key[1:] + if "grid" in key: + value = value["grid"] + out[key] = value + + out["interpolation_xgrid"] = out["xgrid"] + del out["xgrid"] # make operators raw for q2, op in eko.items(): @@ -55,8 +64,8 @@ def get_raw(eko: struct.EKO, binarize: bool = True): return out -def upgrade(raw: dict) -> dict: - """Upgrade raw representation to new layout. +def tocard(raw: dict) -> dict: + """Upgrade raw representation to new card. Parameters ---------- @@ -66,19 +75,23 @@ def upgrade(raw: dict) -> dict: Returns ------- dict - upgraded dict representation + new format operator card """ - upgraded = copy.deepcopy(raw) + card = copy.deepcopy(raw) + + card["rotations"] = {} + card["rotations"]["xgrid"] = raw["interpolation_xgrid"] + card["rotations"]["pids"] = raw["pids"] + for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): + card["rotations"][basis] = raw[basis] - if "rotations" not in raw: - upgraded["rotations"] = {} - upgraded["rotations"]["xgrid"] = raw["interpolation_xgrid"] - upgraded["rotations"]["pids"] = raw["pids"] - for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): - upgraded["rotations"][f"_{basis}"] = raw[basis] + card["configs"] = {} + for field in dataclasses.fields(struct.Configs): + card["configs"][field.name] = raw[field.name] + del card[field.name] - return upgraded + return card def dump_yaml( @@ -152,7 +165,7 @@ def dump_tar(obj: struct.EKO, tarname: Union[str, os.PathLike]): with tempfile.TemporaryDirectory() as tmpdir: tmpdir = pathlib.Path(tmpdir) - metadata = {str(k): v for k, v in obj.raw.items() if k != "Q2grid"} + metadata = {str(k): v for k, v in get_raw(obj).items() if k != "Q2grid"} metadata["Q2grid"] = obj.Q2grid.tolist() yamlname = tmpdir / "metadata.yaml" @@ -189,11 +202,11 @@ def load_yaml(stream: TextIO) -> struct.EKO: loaded object """ - obj = upgrade(yaml.safe_load(stream)) - len_tpids = len(obj["rotations"]["_targetpids"]) - len_ipids = len(obj["rotations"]["_inputpids"]) - len_tgrid = len(obj["rotations"]["_targetgrid"]) - len_igrid = len(obj["rotations"]["_inputgrid"]) + obj = tocard(yaml.safe_load(stream)) + len_tpids = len(obj["rotations"]["targetpids"]) + len_ipids = len(obj["rotations"]["inputpids"]) + len_tgrid = len(obj["rotations"]["targetgrid"]) + len_igrid = len(obj["rotations"]["inputgrid"]) # make operators numpy for op in obj["Q2grid"].values(): for k, v in op.items(): @@ -256,7 +269,7 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: innerdir = list(tmpdir.glob("*"))[0] yamlname = innerdir / "metadata.yaml" with open(yamlname, encoding="utf-8") as fd: - metadata = upgrade(yaml.safe_load(fd)) + metadata = tocard(yaml.safe_load(fd)) # get actual grids grids = {} diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 521b795d0..ba75f693e 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -709,6 +709,10 @@ def new(cls, theory: dict, operator: dict, path: Optional[os.PathLike] = None): shutil.rmtree(td) + for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): + operator["rotations"][f"_{basis}"] = operator["rotations"][basis] + del operator["rotations"][basis] + eko = cls.detached(theory, operator, path=path) logger.info(f"New operator created at path '{path}'") return eko diff --git a/tests/conftest.py b/tests/conftest.py index 6b2e12a3e..2e72bad70 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -68,10 +68,10 @@ def mk_dump(self) -> dict: rotations=dict( xgrid=xgrid, pids=pids, - _targetgrid=xgrid, - _inputgrid=xgrid, - _inputpids=pids, - _targetpids=pids, + targetgrid=xgrid, + inputgrid=xgrid, + inputpids=pids, + targetpids=pids, ), Q0=np.sqrt(q2_ref), couplings=dict(), @@ -97,20 +97,21 @@ def fake_legacy(self): d = self.mk_dump() bases = d["rotations"].copy() - d["inputgrid"] = bases["_inputgrid"] - d["targetgrid"] = bases["_targetgrid"] - d["inputpids"] = bases["_inputpids"] - d["targetpids"] = bases["_targetpids"] + # build data + obj = output.EKO.new(theory={}, operator=d) + for q2, op in d["Q2grid"].items(): + obj[q2] = output.struct.Operator.from_dict(op) + + d["inputgrid"] = bases["inputgrid"] + d["targetgrid"] = bases["targetgrid"] + d["inputpids"] = bases["inputpids"] + d["targetpids"] = bases["targetpids"] d["interpolation_xgrid"] = bases["xgrid"] d["pids"] = bases["pids"] del d["rotations"] - # build data - obj = output.EKO.new(theory={}, operator=legacy.upgrade(d)) - for q2, op in d["Q2grid"].items(): - obj[q2] = output.struct.Operator.from_dict(op) return obj, d diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index fc46dac4b..725765d93 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -72,7 +72,7 @@ def test_targetgrid(): tc = copy.deepcopy(theory_card) oc = copy.deepcopy(operators_card) tgrid = [0.1, 1.0] - oc["rotations"] = dict(targetgrid=tgrid) + oc["rotations"]["targetgrid"] = tgrid r = eko.runner.Runner(tc, oc) o = r.get_output() check_shapes(o, eko.interpolation.XGrid(np.array(tgrid)), o.xgrid, tc, oc) @@ -84,7 +84,6 @@ def test_rotate_pids(): # change pids tc = copy.deepcopy(theory_card) oc = copy.deepcopy(operators_card) - oc["rotations"] = {} oc["rotations"]["targetpids"] = np.eye(14) + 0.1 * np.random.rand(14, 14) oc["rotations"]["inputpids"] = np.eye(14) + 0.1 * np.random.rand(14, 14) r = eko.runner.Runner(tc, oc) @@ -101,9 +100,9 @@ def check_shapes(o, txs, ixs, theory_card, operators_card): op_shape = (tpids, len(txs), ipids, len(ixs)) # check output = input - np.testing.assert_allclose(o.xgrid.raw, operators_card["xgrid"]) - np.testing.assert_allclose(o.rotations.targetgrid, txs.raw) - np.testing.assert_allclose(o.rotations.inputgrid, ixs.raw) + np.testing.assert_allclose(o.xgrid.raw, operators_card["rotations"]["xgrid"]) + np.testing.assert_allclose(o.rotations.targetgrid.raw, txs.raw) + np.testing.assert_allclose(o.rotations.inputgrid.raw, ixs.raw) for k in ["interpolation_polynomial_degree", "interpolation_is_log"]: assert getattr(o.configs, k) == operators_card["configs"][k] np.testing.assert_allclose(o.Q02, theory_card["Q0"] ** 2) From 225c1be8c7620146b4975713a8081399708ea11b Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 19:35:04 +0200 Subject: [PATCH 124/148] Fix last output test --- tests/eko/test_output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/eko/test_output.py b/tests/eko/test_output.py index c176eca37..12ce82fa8 100644 --- a/tests/eko/test_output.py +++ b/tests/eko/test_output.py @@ -222,10 +222,10 @@ def test_to_evol(self, fake_factory, tmp_path): q2_out = 2 Q2grid = fake_factory.mk_g([q2_out], len(br.flavor_basis_pids), len(xgrid)) d = dict( - xgrid=xgrid, Q0=np.sqrt(q2_ref), Q2grid=Q2grid, rotations=dict( + xgrid=xgrid, targetgrid=xgrid, inputgrid=xgrid, inputpids=br.flavor_basis_pids, From e4d059a1adac4b80b27f5efae941ae02dec7aac2 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 19:45:03 +0200 Subject: [PATCH 125/148] Restore one further test in runner --- src/eko/runner.py | 4 ++++ tests/eko/test_runner.py | 9 +++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/eko/runner.py b/src/eko/runner.py index 5c354eaea..8f10f3bcc 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -120,6 +120,10 @@ def similar_to_none(name: str) -> Optional[np.ndarray]: inputgrid = similar_to_none("inputgrid") targetgrid = similar_to_none("targetgrid") if inputgrid is not None or targetgrid is not None: + if inputgrid is not None: + inputgrid = interpolation.XGrid(inputgrid) + if targetgrid is not None: + targetgrid = interpolation.XGrid(targetgrid) manipulate.xgrid_reshape( self.out, targetgrid=targetgrid, inputgrid=inputgrid ) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 725765d93..205a154c0 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -74,10 +74,11 @@ def test_targetgrid(): tgrid = [0.1, 1.0] oc["rotations"]["targetgrid"] = tgrid r = eko.runner.Runner(tc, oc) + __import__("pdb").set_trace() o = r.get_output() - check_shapes(o, eko.interpolation.XGrid(np.array(tgrid)), o.xgrid, tc, oc) + check_shapes(o, eko.interpolation.XGrid(tgrid), o.xgrid, tc, oc) # check actual value - np.testing.assert_allclose(o.rotations.targetgrid, tgrid) + np.testing.assert_allclose(o.rotations.targetgrid.raw, tgrid) def test_rotate_pids(): @@ -90,8 +91,8 @@ def test_rotate_pids(): o = r.get_output() check_shapes(o, o.xgrid, o.xgrid, tc, oc) # check actual values - assert o.rotations.targetpids == [0] * 14 - assert o.rotations.inputpids == [0] * 14 + assert all(o.rotations.targetpids == [0] * 14) + assert all(o.rotations.inputpids == [0] * 14) def check_shapes(o, txs, ixs, theory_card, operators_card): From df64b9935650ffbd43f02fcc9ff3b37d35fff872 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 20:43:57 +0200 Subject: [PATCH 126/148] Fix evolve pdf tests --- src/eko/output/legacy.py | 2 +- src/eko/output/struct.py | 6 ++++-- src/ekobox/evol_pdf.py | 6 ++++-- tests/eko/test_runner.py | 1 - 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index e81eff76d..089280dc6 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -43,7 +43,7 @@ def get_raw(eko: struct.EKO, binarize: bool = True): for key, value in obj[sec].items(): if key.startswith("_"): key = key[1:] - if "grid" in key: + if "grid" in key and value is not None: value = value["grid"] out[key] = value diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index ba75f693e..76136d309 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -12,7 +12,7 @@ import tempfile import time from dataclasses import dataclass, fields -from typing import BinaryIO, Dict, Literal, Optional +from typing import BinaryIO, Dict, Literal, Optional, Tuple import lz4.frame import numpy as np @@ -88,6 +88,8 @@ def raw(self): value = float(value) elif isinstance(value, interpolation.XGrid): value = value.dump() + elif isinstance(value, tuple): + value = list(value) dictionary[field.name] = value @@ -202,7 +204,7 @@ class Debug(DictLike): class Configs(DictLike): """Solution specific configurations.""" - ev_op_max_order: int + ev_op_max_order: Tuple[int] """Maximum order to use in U matrices expansion. Used only in ``perturbative`` solutions. """ diff --git a/src/ekobox/evol_pdf.py b/src/ekobox/evol_pdf.py index 391937031..66eec9025 100644 --- a/src/ekobox/evol_pdf.py +++ b/src/ekobox/evol_pdf.py @@ -2,6 +2,8 @@ """Tools to evolve actual PDFs.""" import pathlib +import numpy as np + import eko import eko.output.legacy from eko import basis_rotation as br @@ -62,7 +64,7 @@ def evolve_pdfs( evolved_PDF_list.append(apply.apply_pdf(eko_output, initial_PDF, targetgrid)) if targetgrid is None: - targetgrid = operators_card["xgrid"] + targetgrid = operators_card["interpolation_xgrid"] if info_update is None: info_update = {} info_update["XMin"] = targetgrid[0] @@ -81,7 +83,7 @@ def evolve_pdfs( * evolved_PDF[Q2]["pdfs"][pid][targetgrid.index(x)], xgrid=targetgrid, Q2grid=operators_card["Q2grid"], - pids=br.flavor_basis_pids, + pids=np.array(br.flavor_basis_pids), ) # all_blocks will be useful in case there will be necessity to dump many blocks # for a single member diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 205a154c0..03d267c12 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -74,7 +74,6 @@ def test_targetgrid(): tgrid = [0.1, 1.0] oc["rotations"]["targetgrid"] = tgrid r = eko.runner.Runner(tc, oc) - __import__("pdb").set_trace() o = r.get_output() check_shapes(o, eko.interpolation.XGrid(tgrid), o.xgrid, tc, oc) # check actual value From c00fc3becfac7bb86d53c282c702c30e67622488 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 20:59:31 +0200 Subject: [PATCH 127/148] Fix last runner tests --- src/eko/compatibility.py | 4 +--- src/eko/runner.py | 8 ++++---- tests/eko/test_runner.py | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index bc7fd0816..baa76fbff 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -27,7 +27,7 @@ def update(theory: dict, operators: Optional[dict]): """ new_theory = copy.deepcopy(theory) - new_operators = copy.deepcopy(operators) + new_operators = copy.deepcopy(operators) if operators is not None else {} if "alphaqed" in new_theory: new_theory["alphaem"] = new_theory.pop("alphaqed") @@ -35,8 +35,6 @@ def update(theory: dict, operators: Optional[dict]): new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) if operators is not None and "configs" not in operators: - assert new_operators is not None - new_operators["configs"] = {} for k in ( "interpolation_polynomial_degree", diff --git a/src/eko/runner.py b/src/eko/runner.py index 8f10f3bcc..8c3a364d5 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -77,10 +77,10 @@ def __init__(self, theory_card: dict, operators_card: dict): # save bases manipulations for a post processing step rot = operators_card.get("rotations", {}) - self.post_process = { - key: rot.get(key, None) - for key in ("inputgrid", "targetgrid", "inputpids", "targetpids") - } + self.post_process = {} + for key in ("inputgrid", "targetgrid", "inputpids", "targetpids"): + self.post_process[key] = rot.get(key, None) + new_operators["rotations"][key] = None self.out = EKO.new( theory=theory_card, operator=dict(Q0=np.sqrt(tc.q2_ref), **new_operators) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 03d267c12..0b9002846 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -90,8 +90,8 @@ def test_rotate_pids(): o = r.get_output() check_shapes(o, o.xgrid, o.xgrid, tc, oc) # check actual values - assert all(o.rotations.targetpids == [0] * 14) - assert all(o.rotations.inputpids == [0] * 14) + assert (o.rotations.targetpids == [0] * 14).all() + assert (o.rotations.inputpids == [0] * 14).all() def check_shapes(o, txs, ixs, theory_card, operators_card): From 73be7ef15b5ea5be6976ad314a618f905178f144 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 21:10:01 +0200 Subject: [PATCH 128/148] Propagate InterpolatorDispatcher update to isolated benchmarks --- benchmarks/eko/benchmark_evol_to_unity.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/benchmarks/eko/benchmark_evol_to_unity.py b/benchmarks/eko/benchmark_evol_to_unity.py index eb2e9d07d..f18d4584d 100644 --- a/benchmarks/eko/benchmark_evol_to_unity.py +++ b/benchmarks/eko/benchmark_evol_to_unity.py @@ -7,7 +7,7 @@ from eko.couplings import Couplings from eko.evolution_operator import Operator from eko.evolution_operator.grid import OperatorGrid -from eko.interpolation import InterpolatorDispatcher +from eko.interpolation import InterpolatorDispatcher, XGrid from eko.thresholds import ThresholdsAtlas # from eko.matching_conditions.operator_matrix_element import OperatorMatrixElement @@ -64,7 +64,13 @@ class BenchmarkBackwardForward: new_operators, ThresholdsAtlas.from_dict(new_theory), Couplings.from_dict(new_theory), - InterpolatorDispatcher.from_dict(new_operators), + InterpolatorDispatcher( + XGrid( + new_operators["xgrid"], + log=new_operators["configs"]["interpolation_is_log"], + ), + new_operators["configs"]["interpolation_polynomial_degree"], + ), ) def test_operator_grid( @@ -76,7 +82,13 @@ def test_operator_grid( self.new_operators, ThresholdsAtlas.from_dict(self.new_theory), Couplings.from_dict(self.new_theory), - InterpolatorDispatcher.from_dict(self.new_operators), + InterpolatorDispatcher( + XGrid( + self.new_operators["xgrid"], + log=self.new_operators["configs"]["interpolation_is_log"], + ), + self.new_operators["configs"]["interpolation_polynomial_degree"], + ), ) q20 = 30 q21 = 50 From 0d92a57e036c64b9cc1da0cb431bf557f19afacb Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 28 Jul 2022 21:21:17 +0200 Subject: [PATCH 129/148] Keep using legacy runcards in ekobox, including benchmarks --- benchmarks/ekobox/benchmark_evol_pdf.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/benchmarks/ekobox/benchmark_evol_pdf.py b/benchmarks/ekobox/benchmark_evol_pdf.py index 1b0c13d26..035b3a38d 100644 --- a/benchmarks/ekobox/benchmark_evol_pdf.py +++ b/benchmarks/ekobox/benchmark_evol_pdf.py @@ -55,11 +55,11 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): all_blocks = (load.load_blocks_from_file("EvPDF", 0))[1] info = load.load_info_from_file("EvPDF") ev_pdf = lhapdf.mkPDF("EvPDF", 0) - assert info["XMin"] == op["xgrid"][0] + assert info["XMin"] == op["interpolation_xgrid"][0] assert info["SetDesc"] == "MyEvolvedPDF" assert info["MZ"] == theory["MZ"] assert info["Debug"] == "Debug" - xgrid = op["xgrid"] + xgrid = op["interpolation_xgrid"] for Q2 in [20.0, 100.0, 10000.0]: for x in xgrid[10:40]: for pid in [21, 1, -1, 2, -2, 3, -3]: @@ -79,7 +79,9 @@ def benchmark_evolve_single_member(tmp_path, cd, lhapdf_path): @pytest.mark.isolated def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): - op = oc.generate([10, 100], update={"xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]}) + op = oc.generate( + [10, 100], update={"interpolation_xgrid": [1e-7, 0.01, 0.1, 0.2, 0.3]} + ) theory = tc.generate(0, 1.0) with lhapdf_path(test_pdf): pdfs = lhapdf.mkPDFs("myMSTW2008nlo90cl") @@ -93,7 +95,7 @@ def benchmark_evolve_more_members(tmp_path, cd, lhapdf_path): new_pdf_1 = lhapdf.mkPDF("Debug", 0) new_pdf_2 = lhapdf.mkPDF("Debug", 1) info = load.load_info_from_file("Debug") - assert info["XMin"] == op["xgrid"][0] + assert info["XMin"] == op["interpolation_xgrid"][0] assert len(pdfs) == len(new_pdfs) for Q2 in [10, 100]: for x in [1e-7, 0.01, 0.1, 0.2, 0.3]: From 3ba82e472e0bce9103096509663a26726c689357 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 11:32:24 +0200 Subject: [PATCH 130/148] Only support old layout of opcard in ekobox --- src/ekobox/operators_card.py | 40 +++++++++++++++--------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/ekobox/operators_card.py b/src/ekobox/operators_card.py index 49de99240..afcfe3332 100644 --- a/src/ekobox/operators_card.py +++ b/src/ekobox/operators_card.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +"""Tools to generate operator cards.""" import copy import yaml @@ -8,8 +9,7 @@ def generate(Q2grid, update=None, name=None): - """ - Generate operators card. + """Generate operators card. Generates an operators card with some mandatory user choice (in this case only the Q2 grid) and some default values which @@ -17,17 +17,17 @@ def generate(Q2grid, update=None, name=None): Parameters ---------- - Q2grid : list(float) - grid for Q2 - update : dict - dictionary of info to update in op. card - name : str - name of exported op.card (if name not None) + Q2grid : list(float) + grid for Q2 + update : dict + dictionary of info to update in op. card + name : str + name of exported op.card (if name not None) Returns ------- - dict - operators card + dict + operators card """ # Constructing the dictionary with some default value def_op = copy.deepcopy(operators.default_card) @@ -38,11 +38,7 @@ def generate(Q2grid, update=None, name=None): if k not in def_op.keys(): raise ValueError("Provided key not in operators card") for key, value in update.items(): - # properly update sections - if isinstance(value, dict): - def_op[key].update(value) - else: - def_op[key] = value + def_op[key] = value serialized = sql.serialize(def_op) def_op["hash"] = (sql.add_hash(serialized))[-1] if name is not None: @@ -51,8 +47,7 @@ def generate(Q2grid, update=None, name=None): def dump(name, op): - """ - Export the operators card in the current directory + """Export the operators card. Parameters ---------- @@ -67,18 +62,17 @@ def dump(name, op): def load(path): - """ - Import the operators card specified by path + """Import the operators card. Parameters ---------- - path : str - path to operators card in yaml format + path : str + path to operators card in yaml format Returns ------- - dict - operators card + dict + operators card """ with open(path, "r", encoding="utf-8") as o: op = yaml.safe_load(o) From 03db302a6560f5cd4a5a7a5be7fb07c1c809ead3 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 29 Jul 2022 11:34:12 +0200 Subject: [PATCH 131/148] Provide some more docstrings for output structs --- src/eko/output/struct.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 76136d309..274e80403 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -67,8 +67,12 @@ def from_dict(cls, dictionary): def raw(self): """Convert dataclass object to raw dictionary. - Normalize :class:`np.ndarray` to lists (possibly nested), and scalars to - the corresponding built-in type. + Normalize: + + - :class:`np.ndarray` to lists (possibly nested) + - scalars to the corresponding built-in type (e.g. :class:`float`) + - :class:`tuple` to lists + - :class:`interpolation.XGrid` to the intrinsic serialization format Returns ------- @@ -244,6 +248,7 @@ class Rotations(DictLike): _inputpids: Optional[np.ndarray] = None def __post_init__(self): + """Adjust types when loaded from serialized object.""" for attr in ("xgrid", "_inputgrid", "_targetgrid"): value = getattr(self, attr) if value is None: @@ -255,24 +260,28 @@ def __post_init__(self): @property def inputpids(self) -> np.ndarray: + """The pids expected on the input PDF.""" if self._inputpids is None: return self.pids return self._inputpids @property def targetpids(self) -> np.ndarray: + """The pids corresponding to the output PDF.""" if self._targetpids is None: return self.pids return self._targetpids @property def inputgrid(self) -> interpolation.XGrid: + """The :math:`x`-grid expected on the input PDF.""" if self._inputgrid is None: return self.xgrid return self._inputgrid @property def targetgrid(self) -> interpolation.XGrid: + """The :math:`x`-grid corresponding to the output PDF.""" if self._targetgrid is None: return self.xgrid return self._targetgrid @@ -350,6 +359,7 @@ def xgrid(self, value: interpolation.XGrid): self.rotations.xgrid = value def __post_init__(self): + """Validate class members.""" if self.path.suffix != ".tar": raise ValueError("Not a valid path for an EKO") if not tarfile.is_tarfile(self.path): @@ -357,6 +367,7 @@ def __post_init__(self): @staticmethod def opname(q2: float) -> str: + """Operator file name from :math:`Q^2` value.""" return f"operators/{q2:8.2f}" def __getitem__(self, q2: float): From e37e40d490b063d040c8d51b9b7c56f4e2911e00 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 11:44:16 +0200 Subject: [PATCH 132/148] Complete test_interpolation --- tests/eko/test_interpolation.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/eko/test_interpolation.py b/tests/eko/test_interpolation.py index 6096d4ffc..014ab2329 100644 --- a/tests/eko/test_interpolation.py +++ b/tests/eko/test_interpolation.py @@ -323,6 +323,27 @@ def test_reference_indices(self): a = interpolation.Area(0, 3, (0, 2), xgrid) +class TestXGrid: + def test_fromcard(self): + aa = [0.1, 1.0] + a = interpolation.XGrid.fromcard(aa, False) + np.testing.assert_array_almost_equal(a.raw, aa) + assert a.size == len(aa) + + bargs = (3, 3) + bb = interpolation.make_grid(*bargs) + b = interpolation.XGrid.fromcard(["make_grid", *bargs], False) + np.testing.assert_array_almost_equal(b.raw, bb) + + cargs = (10,) + cc = interpolation.make_lambert_grid(*cargs) + c = interpolation.XGrid.fromcard(["make_lambert_grid", *cargs], False) + np.testing.assert_array_almost_equal(c.raw, cc) + + with pytest.raises(ValueError): + interpolation.XGrid.fromcard([], False) + + def test_make_grid(): xg = interpolation.make_grid(3, 3) np.testing.assert_array_almost_equal(xg, np.array([1e-7, 1e-4, 1e-1, 0.55, 1.0])) From 67d53395e9a6788f0b392db12b7c542e5c83242c Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 11:48:06 +0200 Subject: [PATCH 133/148] Complete runner --- tests/eko/test_runner.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 0b9002846..796556b95 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -71,11 +71,15 @@ def test_targetgrid(): # change targetgrid tc = copy.deepcopy(theory_card) oc = copy.deepcopy(operators_card) + igrid = [0.1, 1.0] + oc["rotations"]["inputgrid"] = igrid tgrid = [0.1, 1.0] oc["rotations"]["targetgrid"] = tgrid r = eko.runner.Runner(tc, oc) o = r.get_output() - check_shapes(o, eko.interpolation.XGrid(tgrid), o.xgrid, tc, oc) + check_shapes( + o, eko.interpolation.XGrid(tgrid), eko.interpolation.XGrid(igrid), tc, oc + ) # check actual value np.testing.assert_allclose(o.rotations.targetgrid.raw, tgrid) From aeddb040bc1b17fac4e44bb7cc1d4170835fb3fe Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 29 Jul 2022 12:11:58 +0200 Subject: [PATCH 134/148] Provide even more docstrings --- src/eko/output/struct.py | 191 +++++++++++++++++++++++++++++++++------ 1 file changed, 161 insertions(+), 30 deletions(-) diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index 274e80403..eb9d20fa8 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -17,6 +17,7 @@ import lz4.frame import numpy as np import numpy.lib.npyio as npyio +import numpy.typing as npt import yaml from .. import basis_rotation as br @@ -114,9 +115,9 @@ class Operator(DictLike): """ - operator: np.ndarray + operator: npt.NDArray """Content of the evolution operator.""" - error: Optional[np.ndarray] = None + error: Optional[npt.NDArray] = None """Errors on individual operator elements (mainly used for integration error, but it can host any kind of error). """ @@ -240,12 +241,12 @@ class Rotations(DictLike): xgrid: interpolation.XGrid """Momentum fraction internal grid.""" - pids: np.ndarray + pids: npt.NDArray """Array of integers, corresponding to internal PIDs.""" _targetgrid: Optional[interpolation.XGrid] = None _inputgrid: Optional[interpolation.XGrid] = None - _targetpids: Optional[np.ndarray] = None - _inputpids: Optional[np.ndarray] = None + _targetpids: Optional[npt.NDArray] = None + _inputpids: Optional[npt.NDArray] = None def __post_init__(self): """Adjust types when loaded from serialized object.""" @@ -259,29 +260,29 @@ def __post_init__(self): setattr(self, attr, interpolation.XGrid.load(value)) @property - def inputpids(self) -> np.ndarray: - """The pids expected on the input PDF.""" + def inputpids(self) -> npt.NDArray: + """Provide pids expected on the input PDF.""" if self._inputpids is None: return self.pids return self._inputpids @property - def targetpids(self) -> np.ndarray: - """The pids corresponding to the output PDF.""" + def targetpids(self) -> npt.NDArray: + """Provide pids corresponding to the output PDF.""" if self._targetpids is None: return self.pids return self._targetpids @property def inputgrid(self) -> interpolation.XGrid: - """The :math:`x`-grid expected on the input PDF.""" + """Provide :math:`x`-grid expected on the input PDF.""" if self._inputgrid is None: return self.xgrid return self._inputgrid @property def targetgrid(self) -> interpolation.XGrid: - """The :math:`x`-grid corresponding to the output PDF.""" + """Provide :math:`x`-grid corresponding to the output PDF.""" if self._targetgrid is None: return self.xgrid return self._targetgrid @@ -370,8 +371,23 @@ def opname(q2: float) -> str: """Operator file name from :math:`Q^2` value.""" return f"operators/{q2:8.2f}" - def __getitem__(self, q2: float): - # TODO: autoload + def __getitem__(self, q2: float) -> Operator: + """Retrieve operator for given :math:`Q^2`. + + If the operator is not already in memory, it will be automatically + loaded. + + Parameters + ---------- + q2: float + :math:`Q^2` value labeling the operator to be retrieved + + Returns + ------- + Operator + the retrieved operator + + """ op = self._operators[q2] if op is not None: return op @@ -401,6 +417,20 @@ def __getitem__(self, q2: float): return op def __setitem__(self, q2: float, op: Operator, compress: bool = True): + """Set operator for given :math:`Q^2`. + + The operator is automatically dumped on disk, . + + Parameters + ---------- + q2: float + :math:`Q^2` value labeling the operator to be set + op: Operator + the retrieved operator + compress: bool + whether to save the operator compressed or not (default: `True`) + + """ if not isinstance(op, Operator): raise ValueError("Only an Operator can be added to an EKO") @@ -468,17 +498,29 @@ def __delitem__(self, q2: float): @contextlib.contextmanager def operator(self, q2: float): + """Retrieve operator and discard immediately. + + To be used as a contextmanager: the operator is automatically loaded as + usual, but after the context manager it is dropped from memory. + + Parameters + ---------- + q2: float + :math:`Q^2` value labeling the operator to be retrieved + + """ try: yield self[q2] finally: del self[q2] @property - def Q2grid(self): + def Q2grid(self) -> npt.NDArray: + """Provide the list of :math:`Q^2` as an array.""" return np.array(list(self._operators)) def __iter__(self): - """Iterate over keys (i.e. Q2 values) + """Iterate over keys (i.e. Q2 values). Yields ------ @@ -511,9 +553,45 @@ def items(self): del self[q2] def __contains__(self, q2: float) -> bool: + """Check whether :math:`Q^2` operators are present. + + 'Present' means, in this case, they are conceptually part of the + :class:`EKO`. But it is telling nothing about being loaded in memory or + not. + + Returns + ------- + bool + the result of checked condition + + """ return q2 in self._operators - def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: + def approx( + self, q2: float, rtol: float = 1e-6, atol: float = 1e-10 + ) -> Optional[float]: + """Look for close enough :math:`Q^2` value in the :class:`EKO`. + + Parameters + ---------- + q2: float + value of :math:`Q2` in which neighborhood to look + rtol: float + relative tolerance + atol: float + absolute tolerance + + Returns + ------- + float or None + retrieved value of :math:`Q^2`, if a single one is found + + Raises + ------ + ValueError + if multiple values are find in the neighborhood + + """ q2s = self.Q2grid close = q2s[np.isclose(q2, q2s, rtol=rtol, atol=atol)] @@ -525,7 +603,6 @@ def approx(self, q2, rtol=1e-6, atol=1e-10) -> Optional[float]: def unload(self): """Fully unload the operators in memory.""" - for q2 in self: del self[q2] @@ -563,16 +640,48 @@ def deepcopy(self, path: os.PathLike): return new @staticmethod - def bootstrap(tdir: os.PathLike, theory: dict, operator: dict): - tdir = pathlib.Path(tdir) - (tdir / THEORYFILE).write_text(yaml.dump(theory), encoding="utf-8") - (tdir / OPERATORFILE).write_text(yaml.dump(operator), encoding="utf-8") - (tdir / RECIPESDIR).mkdir() - (tdir / PARTSDIR).mkdir() - (tdir / OPERATORSDIR).mkdir() + def bootstrap(dirpath: os.PathLike, theory: dict, operator: dict): + """Create directory structure. + + Parameters + ---------- + dirpath: os.PathLike + path to create the directory into + theory: dict + theory card to be dumped + operator: dict + operator card to be dumped + + """ + dirpath = pathlib.Path(dirpath) + (dirpath / THEORYFILE).write_text(yaml.dump(theory), encoding="utf-8") + (dirpath / OPERATORFILE).write_text(yaml.dump(operator), encoding="utf-8") + (dirpath / RECIPESDIR).mkdir() + (dirpath / PARTSDIR).mkdir() + (dirpath / OPERATORSDIR).mkdir() @staticmethod def extract(path: os.PathLike, filename: str) -> str: + """Extract file from disk assets. + + Note + ---- + At the moment, it only support text files (since it is returning the + content as a string) + + Parameters + ---------- + path: os.PathLike + path to the disk dump + filename: str + relative path inside the archive of the file to extract + + Returns + ------- + str + file content + + """ path = pathlib.Path(path) with tarfile.open(path, "r") as tar: @@ -587,17 +696,20 @@ def extract(path: os.PathLike, filename: str) -> str: @property def theory(self) -> dict: + """Provide theory card, retrieving from the dump.""" # TODO: make an actual attribute, move load to `theory_card`, and the # type of the attribute will be `eko.runcards.TheoryCard` return yaml.safe_load(self.extract(self.path, THEORYFILE)) @property def theory_card(self) -> dict: + """Provide theory card, retrieving from the dump.""" # TODO: return `eko.runcards.TheoryCard` return self.theory @property def operator_card(self) -> dict: + """Provide operator card, retrieving from the dump.""" # TODO: return `eko.runcards.OperatorCard` return yaml.safe_load(self.extract(self.path, OPERATORFILE)) @@ -732,6 +844,19 @@ def new(cls, theory: dict, operator: dict, path: Optional[os.PathLike] = None): @classmethod def load(cls, path: os.PathLike): + """Load dump into an :class:`EKO` object. + + Parameters + ---------- + path:: os.PathLike + path to the dump to load + + Returns + ------- + EKO + the loaded instance + + """ path = pathlib.Path(path) if not tarfile.is_tarfile(path): raise ValueError("EKO: the corresponding file is not a valid tar archive") @@ -744,7 +869,16 @@ def load(cls, path: os.PathLike): return eko @property - def raw(self): + def raw(self) -> dict: + """Provide raw representation of the full content. + + Returns + ------- + dict + nested dictionary, storing all the values in the structure, but the + operators themselves + + """ return dict( path=str(self.path), Q0=float(np.sqrt(self.Q02)), @@ -754,9 +888,6 @@ def raw(self): debug=self.debug.raw, ) - def close(self): - for q2 in self.Q2grid: - del self[q2] - def __del__(self): - self.close() + """Destroy the memory structure gracefully.""" + self.unload() From af88b47d386c7ad5d419971aa982ff368cf66fbc Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 13:24:52 +0200 Subject: [PATCH 135/148] Add more struct tests --- tests/eko/test_output_struct.py | 76 ++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/eko/test_output_struct.py b/tests/eko/test_output_struct.py index c850766a0..f66977e14 100644 --- a/tests/eko/test_output_struct.py +++ b/tests/eko/test_output_struct.py @@ -1,8 +1,82 @@ # -*- coding: utf-8 -*- -from eko import output +import io +from dataclasses import dataclass + +import numpy as np +import numpy.typing as npt +import pytest +import yaml + +from eko import interpolation, output from eko.output import struct +@dataclass +class MyDictLike(struct.DictLike): + l: npt.NDArray + f: float + x: interpolation.XGrid + t: tuple + s: str + + +def test_DictLike(): + d = MyDictLike.from_dict( + dict( + l=np.arange(5.0), + f=np.arange(5.0)[-1], + x=interpolation.XGrid([0.1, 1.0]), + t=(1.0, 2.0), + s="s", + ) + ) + assert d.f == 4.0 + dd = MyDictLike.from_dict(d.raw) + assert dd.f == 4.0 + # check we can dump and reload + stream = io.StringIO() + yaml.safe_dump(d.raw, stream) + stream.seek(0) + ddd = yaml.safe_load(stream) + assert "l" in ddd + np.testing.assert_allclose(ddd["l"], np.arange(5.0)) + + +class TestOperator: + def test_value_only(self): + v = np.random.rand(2, 2) + opv = struct.Operator(operator=v) + assert opv.error is None + for compress in (True, False): + stream = io.BytesIO() + opv.save(stream, compress) + stream.seek(0) + opv_ = struct.Operator.load(stream, compress) + np.testing.assert_allclose(opv.operator, opv_.operator) + np.testing.assert_allclose(v, opv_.operator) + assert opv_.error is None + + def test_value_and_error(self): + v, e = np.random.rand(2, 2, 2) + opve = struct.Operator(operator=v, error=e) + for compress in (True, False): + stream = io.BytesIO() + opve.save(stream, compress) + stream.seek(0) + opve_ = struct.Operator.load(stream, compress) + np.testing.assert_allclose(opve.operator, opve_.operator) + np.testing.assert_allclose(v, opve_.operator) + np.testing.assert_allclose(opve.error, opve_.error) + np.testing.assert_allclose(e, opve_.error) + + def test_load_error(self, monkeypatch): + # We might consider dropping this exception since np.load will always return a array (or fail on it's own) + stream = io.BytesIO() + monkeypatch.setattr(np, "load", lambda _: None) + with pytest.raises(ValueError): + struct.Operator.load(stream, False) + + class TestLegacy: def test_items(self, fake_output): """Test autodump, autoload, and manual unload.""" From bc89ac6081fd11df0f9ec022f509e31adb57b00d Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 15:01:06 +0200 Subject: [PATCH 136/148] Add rotations tests --- tests/eko/test_output_struct.py | 48 +++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/eko/test_output_struct.py b/tests/eko/test_output_struct.py index f66977e14..9738fecb8 100644 --- a/tests/eko/test_output_struct.py +++ b/tests/eko/test_output_struct.py @@ -77,6 +77,54 @@ def test_load_error(self, monkeypatch): struct.Operator.load(stream, False) +class TestRotations: + def test_fallback(self): + pids = np.array([1, 2]) + xg = interpolation.XGrid([0.1, 1.0]) + r = struct.Rotations(xgrid=xg, pids=pids) + np.testing.assert_allclose(r.pids, pids) + np.testing.assert_allclose(r.targetpids, pids) + np.testing.assert_allclose(r.inputpids, pids) + assert r.xgrid == xg + assert r.targetgrid == xg + assert r.inputgrid == xg + + def test_overwrite(self): + pids = np.array([1, 2]) + tpids = np.array([3, 4]) + ipids = np.array([5, 6]) + xg = interpolation.XGrid([0.1, 1.0]) + txg = interpolation.XGrid([0.2, 1.0]) + ixg = interpolation.XGrid([0.3, 1.0]) + r = struct.Rotations( + xgrid=xg, + pids=pids, + _targetgrid=txg, + _inputgrid=ixg, + _targetpids=tpids, + _inputpids=ipids, + ) + np.testing.assert_allclose(r.pids, pids) + np.testing.assert_allclose(r.targetpids, tpids) + np.testing.assert_allclose(r.inputpids, ipids) + assert r.xgrid == xg + assert r.targetgrid == txg + assert r.inputgrid == ixg + + def test_init(self): + pids = np.array([1, 2]) + xg = interpolation.XGrid([0.1, 1.0]) + txg = np.array([0.2, 1.0]) + ixg = {"grid": [0.3, 1.0], "log": True} + r = struct.Rotations(xgrid=xg, pids=pids, _targetgrid=txg, _inputgrid=ixg) + assert isinstance(r.xgrid, interpolation.XGrid) + assert isinstance(r.targetgrid, interpolation.XGrid) + assert isinstance(r.inputgrid, interpolation.XGrid) + assert r.xgrid == xg + assert r.targetgrid == interpolation.XGrid(txg) + assert r.inputgrid == interpolation.XGrid.load(ixg) + + class TestLegacy: def test_items(self, fake_output): """Test autodump, autoload, and manual unload.""" From f8eb45607422edf275451b383f783dc77dedf5ec Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 16:44:20 +0200 Subject: [PATCH 137/148] Complete most tests --- src/eko/compatibility.py | 7 +- src/eko/output/struct.py | 33 +++------ src/eko/runner.py | 4 +- tests/eko/test_compatibility.py | 7 +- tests/eko/test_output_struct.py | 124 +++++++++++++++++++++++++++++++- tests/eko/test_runner.py | 1 + 6 files changed, 140 insertions(+), 36 deletions(-) diff --git a/src/eko/compatibility.py b/src/eko/compatibility.py index baa76fbff..86a89f9ca 100644 --- a/src/eko/compatibility.py +++ b/src/eko/compatibility.py @@ -32,7 +32,7 @@ def update(theory: dict, operators: Optional[dict]): if "alphaqed" in new_theory: new_theory["alphaem"] = new_theory.pop("alphaqed") if "QED" in new_theory: - new_theory["order"] = (new_theory.pop("PTO") + 1, new_theory.pop("QED")) + new_theory["order"] = [new_theory.pop("PTO") + 1, new_theory.pop("QED")] if operators is not None and "configs" not in operators: new_operators["configs"] = {} @@ -51,14 +51,15 @@ def update(theory: dict, operators: Optional[dict]): max_order = operators["ev_op_max_order"] if isinstance(max_order, int): - new_operators["configs"]["ev_op_max_order"] = ( + new_operators["configs"]["ev_op_max_order"] = [ max_order, new_theory["order"][1], - ) + ] new_operators["rotations"]["xgrid"] = operators["interpolation_xgrid"] for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): new_operators["rotations"][f"{basis}"] = operators[basis] + new_operators["Q0"] = new_theory["Q0"] return new_theory, new_operators diff --git a/src/eko/output/struct.py b/src/eko/output/struct.py index eb9d20fa8..239336f3e 100644 --- a/src/eko/output/struct.py +++ b/src/eko/output/struct.py @@ -363,13 +363,11 @@ def __post_init__(self): """Validate class members.""" if self.path.suffix != ".tar": raise ValueError("Not a valid path for an EKO") - if not tarfile.is_tarfile(self.path): - raise ValueError("EKO: the corresponding file is not a valid tar archive") @staticmethod def opname(q2: float) -> str: """Operator file name from :math:`Q^2` value.""" - return f"operators/{q2:8.2f}" + return f"{OPERATORSDIR}/{q2:8.2f}" def __getitem__(self, q2: float) -> Operator: """Retrieve operator for given :math:`Q^2`. @@ -379,7 +377,7 @@ def __getitem__(self, q2: float) -> Operator: Parameters ---------- - q2: float + q2 : float :math:`Q^2` value labeling the operator to be retrieved Returns @@ -388,9 +386,10 @@ def __getitem__(self, q2: float) -> Operator: the retrieved operator """ - op = self._operators[q2] - if op is not None: - return op + if q2 in self._operators: + op = self._operators[q2] + if op is not None: + return op with tarfile.open(self.path) as tar: names = list( @@ -399,18 +398,11 @@ def __getitem__(self, q2: float) -> Operator: if len(names) == 0: raise ValueError(f"Q2 value '{q2}' not available in '{self.path}'") - if len(names) > 1: - raise ValueError( - f"Q2 value '{q2}' occurs multiple times in '{self.path}'" - ) name = names[0] compressed = name.endswith(".lz4") stream = tar.extractfile(name) - if stream is None: - raise ValueError - op = Operator.load(stream, compressed=compressed) self._operators[q2] = op @@ -686,10 +678,6 @@ def extract(path: os.PathLike, filename: str) -> str: with tarfile.open(path, "r") as tar: fd = tar.extractfile(filename) - if fd is None: - raise ValueError( - f"The member '{filename}' is not a readable file inside EKO tar" - ) content = fd.read().decode() return content @@ -763,6 +751,9 @@ def detached(cls, theory: dict, operator: dict, path: pathlib.Path): """ bases = operator["rotations"] + for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): + bases[f"_{basis}"] = bases[basis] + del bases[basis] bases["pids"] = np.array(br.flavor_basis_pids) for k in ("xgrid", "_inputgrid", "_targetgrid"): if operator["rotations"][k] is None: @@ -834,10 +825,6 @@ def new(cls, theory: dict, operator: dict, path: Optional[os.PathLike] = None): shutil.rmtree(td) - for basis in ("inputgrid", "targetgrid", "inputpids", "targetpids"): - operator["rotations"][f"_{basis}"] = operator["rotations"][basis] - del operator["rotations"][basis] - eko = cls.detached(theory, operator, path=path) logger.info(f"New operator created at path '{path}'") return eko @@ -882,7 +869,7 @@ def raw(self) -> dict: return dict( path=str(self.path), Q0=float(np.sqrt(self.Q02)), - Q2grid=self.Q2grid, + Q2grid=self.Q2grid.tolist(), configs=self.configs.raw, rotations=self.rotations.raw, debug=self.debug.raw, diff --git a/src/eko/runner.py b/src/eko/runner.py index 8c3a364d5..37f37a3e6 100644 --- a/src/eko/runner.py +++ b/src/eko/runner.py @@ -82,9 +82,7 @@ def __init__(self, theory_card: dict, operators_card: dict): self.post_process[key] = rot.get(key, None) new_operators["rotations"][key] = None - self.out = EKO.new( - theory=theory_card, operator=dict(Q0=np.sqrt(tc.q2_ref), **new_operators) - ) + self.out = EKO.new(theory=theory_card, operator=new_operators) def get_output(self) -> EKO: """Run evolution and generate output operator. diff --git a/tests/eko/test_compatibility.py b/tests/eko/test_compatibility.py index 8bc46d3f0..1a9de4d0b 100644 --- a/tests/eko/test_compatibility.py +++ b/tests/eko/test_compatibility.py @@ -1,12 +1,7 @@ # -*- coding: utf-8 -*- from eko import compatibility -theory1 = { - "alphas": 0.1180, - "alphaqed": 0.007496, - "PTO": 2, - "QED": 0, -} +theory1 = {"alphas": 0.1180, "alphaqed": 0.007496, "PTO": 2, "QED": 0, "Q0": 1.0} def test_compatibility(): diff --git a/tests/eko/test_output_struct.py b/tests/eko/test_output_struct.py index 9738fecb8..ea8324c2d 100644 --- a/tests/eko/test_output_struct.py +++ b/tests/eko/test_output_struct.py @@ -7,8 +7,10 @@ import pytest import yaml -from eko import interpolation, output +from eko import compatibility, interpolation, output from eko.output import struct +from ekobox import operators_card as oc +from ekobox import theory_card as tc @dataclass @@ -125,6 +127,126 @@ def test_init(self): assert r.inputgrid == interpolation.XGrid.load(ixg) +class TestEKO: + def _default_cards(self): + t = tc.generate(0, 1.0) + o = oc.generate([10.0]) + return compatibility.update(t, o) + + def test_new_error(self, tmp_path): + nt, no = self._default_cards() + # try to write to a file different from bla + no_tar_path = tmp_path / "Blub.bla" + with pytest.raises(ValueError): + struct.EKO.new(nt, no, no_tar_path) + # try to overwrite an existing file + exists_path = tmp_path / "Blub.tar" + exists_path.write_text("Blub", encoding="utf-8") + with pytest.raises(FileExistsError): + struct.EKO.new(nt, no, exists_path) + + def test_load_error(self, tmp_path): + # try to read from a non-tar path + no_tar_path = tmp_path / "Blub.tar" + no_tar_path.write_text("Blub", encoding="utf-8") + with pytest.raises(ValueError): + struct.EKO.load(no_tar_path) + + def test_properties(self): + eko = struct.EKO.new(*self._default_cards()) + assert "mc" in eko.theory_card + assert "debug" in eko.operator_card + np.testing.assert_allclose(eko.Q2grid, np.array([10.0])) + assert 10.0 in eko + default_grid = interpolation.XGrid(eko.operator_card["rotations"]["xgrid"]) + assert eko.xgrid == default_grid + for use_target in (True, False): + assert eko.interpolator(False, use_target).xgrid == default_grid + xg = interpolation.XGrid([0.1, 1.0]) + eko.xgrid = xg + assert eko.xgrid == xg + assert "debug" in eko.raw + # check we can dump and reload + stream = io.StringIO() + yaml.safe_dump(eko.raw, stream) + stream.seek(0) + raw_eko = yaml.safe_load(stream) + assert "debug" in raw_eko + + def test_ops(self): + v = np.random.rand(2, 2) + opv = struct.Operator(operator=v) + eko = struct.EKO.new(*self._default_cards()) + # try setting not an operator + with pytest.raises(ValueError): + eko[10.0] = "bla" + # approx + eko[10.0] = opv + assert eko.approx(20.0) is None + assert eko.approx(11.0, atol=2) == 10.0 + eko[11.0] = opv + with pytest.raises(ValueError): + eko.approx(10.5, atol=2) + # iterate + for q2, q2eko in zip((10.0, 11.0), eko): + assert q2 == q2eko + np.testing.assert_allclose(v, eko[q2].operator) + for q2, (q2eko, op) in zip((10.0, 11.0), eko.items()): + assert q2 == q2eko + np.testing.assert_allclose(v, op.operator) + # getter + with pytest.raises(ValueError): + eko[12.0] + with eko.operator(10.0) as op: + np.testing.assert_allclose(v, op.operator) + # overwrite + vv = np.random.rand(2, 2) + opvv = struct.Operator(operator=vv) + eko[11.0] = opvv + np.testing.assert_allclose(vv, eko[11.0].operator) + + def test_interpolator(self): + nt, no = self._default_cards() + txg = np.geomspace(0.1, 1.0, 5) + ixg = np.geomspace(0.01, 1.0, 5) + no["rotations"]["targetgrid"] = txg + no["rotations"]["inputgrid"] = ixg + eko = struct.EKO.new(nt, no) + assert eko.interpolator(False, True).xgrid == interpolation.XGrid(txg) + assert eko.interpolator(False, False).xgrid == interpolation.XGrid(ixg) + + def test_copy(self, tmp_path): + v = np.random.rand(2, 2) + opv = struct.Operator(operator=v) + eko1 = struct.EKO.new(*self._default_cards()) + eko1[10.0] = opv + np.testing.assert_allclose(eko1[10.0].operator, v) + p = tmp_path / "eko2.tar" + eko2 = eko1.deepcopy(p) + np.testing.assert_allclose(eko1[10.0].operator, v) + np.testing.assert_allclose(eko2[10.0].operator, v) + vv = np.random.rand(2, 2) + opvv = struct.Operator(operator=vv) + eko2[10.0] = opvv + np.testing.assert_allclose(eko1[10.0].operator, v) + np.testing.assert_allclose(eko2[10.0].operator, vv) + # try loading again + eko2_ = struct.EKO.load(p) + assert eko2.raw == eko2_.raw + + def test_extract(self, tmp_path): + p = tmp_path / "test.tar" + eko = struct.EKO.new(*self._default_cards(), p) + # check theory file + t = struct.EKO.extract(p, struct.THEORYFILE) + assert isinstance(t, str) + tt = yaml.safe_load(io.StringIO(t)) + assert tt == eko.theory_card + # try a wrong file + with pytest.raises(KeyError): + t = struct.EKO.extract(p, "Blub.bla") + + class TestLegacy: def test_items(self, fake_output): """Test autodump, autoload, and manual unload.""" diff --git a/tests/eko/test_runner.py b/tests/eko/test_runner.py index 796556b95..c1d491022 100644 --- a/tests/eko/test_runner.py +++ b/tests/eko/test_runner.py @@ -38,6 +38,7 @@ } operators_card = { "Q2grid": [10, 100], + "Q0": np.sqrt(2), "configs": { "interpolation_polynomial_degree": 1, "interpolation_is_log": True, From 44b581cc497008e0c0da33162cb94e26a818a584 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 17:09:49 +0200 Subject: [PATCH 138/148] Fix numba and LHA --- benchmarks/lha_paper_bench.py | 4 ++-- src/eko/evolution_operator/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/lha_paper_bench.py b/benchmarks/lha_paper_bench.py index 451c638d1..f2ee934e3 100644 --- a/benchmarks/lha_paper_bench.py +++ b/benchmarks/lha_paper_bench.py @@ -112,8 +112,8 @@ def run_lha(self, theory_updates): [ { "Q2grid": [1e4], - "configs": {"ev_op_iterations": 10}, - "xgrid": make_lambert_grid(60).tolist(), + "ev_op_iterations": 10, + "interpolation_xgrid": make_lambert_grid(60).tolist(), } ], ["ToyLH"], diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index 4d2a17678..be5f8a690 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -265,7 +265,7 @@ def __init__( self._mellin_cut = mellin_cut self.is_threshold = is_threshold self.op_members = {} - self.order = config["order"] + self.order = tuple(config["order"]) @property def n_pools(self): @@ -376,7 +376,7 @@ def quad_ker(self, label, logx, areas): nf=self.nf, L=np.log(self.fact_to_ren), ev_op_iterations=self.config["ev_op_iterations"], - ev_op_max_order=self.config["ev_op_max_order"], + ev_op_max_order=tuple(self.config["ev_op_max_order"]), sv_mode=self.sv_mode, is_threshold=self.is_threshold, ) From 4e6c8a3e317ef0b2e0f9373b6b868ae579651dc5 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 17:22:24 +0200 Subject: [PATCH 139/148] Recover test_ev_operator --- tests/eko/test_ev_operator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/eko/test_ev_operator.py b/tests/eko/test_ev_operator.py index f3e1b6935..1e10b4058 100644 --- a/tests/eko/test_ev_operator.py +++ b/tests/eko/test_ev_operator.py @@ -161,7 +161,7 @@ def test_quad_ker(monkeypatch): "configs": { "interpolation_polynomial_degree": 1, "interpolation_is_log": True, - "ev_op_max_order": 1, + "ev_op_max_order": [1, 1], "ev_op_iterations": 1, "backward_inversion": "exact", "n_integration_cores": 1, From 7f38cad48d9c12f56943f379be48106cd3e805a6 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 29 Jul 2022 17:29:14 +0200 Subject: [PATCH 140/148] Add tests for runcards objects --- src/eko/runcards.py | 49 +++++++++++++++++++++++++++++++++++++- tests/eko/test_runcards.py | 32 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 tests/eko/test_runcards.py diff --git a/src/eko/runcards.py b/src/eko/runcards.py index b9be63c4c..44dc47618 100644 --- a/src/eko/runcards.py +++ b/src/eko/runcards.py @@ -1,12 +1,59 @@ # -*- coding: utf-8 -*- +"""Structures to hold runcard information. + +.. todo:: + Inherit from :class:`eko.output.struct.DictLike`, to get + :meth:`eko.output.struct.DictLike.raw` for free and avoid duplication. + +""" from dataclasses import dataclass @dataclass class TheoryCard: - pto: int + """Represent theory card content.""" + + order: tuple[int, int] + """Perturbatiive order tuple, ``(QCD, QED)``.""" + + @classmethod + def load(cls, card: dict): + """Load from runcard raw content. + + Parameters + ---------- + card: dict + content of the theory runcard + + Returns + ------- + TheoryCard + the loaded instance + + """ + return cls(order=tuple(card["order"])) @dataclass class OperatorCard: + """Represent operator card content.""" + xgrid: list + """Grid defining internal interpolation basis.""" + + @classmethod + def load(cls, card: dict): + """Load from runcard raw content. + + Parameters + ---------- + card: dict + content of the theory runcard + + Returns + ------- + TheoryCard + the loaded instance + + """ + return cls(xgrid=card["xgrid"]) diff --git a/tests/eko/test_runcards.py b/tests/eko/test_runcards.py new file mode 100644 index 000000000..e8123ec2d --- /dev/null +++ b/tests/eko/test_runcards.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from eko import runcards as rc + +fake_theory = dict(order=(2, 0)) + +fake_operator = dict(xgrid=[1e-3, 1e-2, 1e-1, 1.0]) + + +class TestTheory: + def test_init(self): + t = rc.TheoryCard(order=fake_theory["order"]) + + assert t.order == fake_theory["order"] + + def test_load(self): + t0 = rc.TheoryCard(order=fake_theory["order"]) + t1 = rc.TheoryCard.load(fake_theory) + + assert t0 == t1 + + +class TestOperator: + def test_init(self): + o = rc.OperatorCard(xgrid=fake_operator["xgrid"]) + + assert o.xgrid == fake_operator["xgrid"] + + def test_load(self): + o0 = rc.OperatorCard(xgrid=fake_operator["xgrid"]) + o1 = rc.OperatorCard.load(fake_operator) + + assert o0 == o1 From 67bd77d0de248fd2e0d9235d4bd62fc06962584e Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Fri, 29 Jul 2022 17:33:07 +0200 Subject: [PATCH 141/148] Fix legacy typing compatibility --- src/eko/runcards.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eko/runcards.py b/src/eko/runcards.py index 44dc47618..0c46b51ed 100644 --- a/src/eko/runcards.py +++ b/src/eko/runcards.py @@ -7,13 +7,14 @@ """ from dataclasses import dataclass +from typing import Tuple @dataclass class TheoryCard: """Represent theory card content.""" - order: tuple[int, int] + order: Tuple[int, int] """Perturbatiive order tuple, ``(QCD, QED)``.""" @classmethod From 2090ddd1bb2259a7ac8b80261b56de1b707a88cd Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Fri, 29 Jul 2022 17:44:16 +0200 Subject: [PATCH 142/148] Revert HERA20 fix --- benchmarks/HERA20_bench.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/HERA20_bench.py b/benchmarks/HERA20_bench.py index 79f17c92b..a09d83f3d 100644 --- a/benchmarks/HERA20_bench.py +++ b/benchmarks/HERA20_bench.py @@ -22,7 +22,7 @@ } # LHAPDF x-range is smaller -base_op = {"xgrid": interpolation.make_lambert_grid(50, 1.0e-6)} +base_op = {"interpolation_xgrid": interpolation.make_lambert_grid(50, 1.0e-6)} class BenchmarkHERA20(Runner): From f5a730a7e07fb339c91da1562cf19ca43648a0c4 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Sat, 30 Jul 2022 08:59:16 +0200 Subject: [PATCH 143/148] Poetry update --- poetry.lock | 551 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 416 insertions(+), 135 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5fd358c7a..5c8257aec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -81,17 +81,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "babel" @@ -232,7 +232,7 @@ optional = true python-versions = "*" [package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] [[package]] name = "coverage" @@ -293,7 +293,7 @@ graph = ["objgraph (>=1.7.2)"] [[package]] name = "distlib" -version = "0.3.4" +version = "0.3.5" description = "Distribution utilities" category = "dev" optional = false @@ -317,7 +317,7 @@ python-versions = ">=3.6" [[package]] name = "executing" -version = "0.8.3" +version = "0.9.1" description = "Get the currently executing AST node of a frame, and other information" category = "main" optional = false @@ -337,7 +337,7 @@ pyrepl = ">=0.8.2" [[package]] name = "fastjsonschema" -version = "2.15.3" +version = "2.16.1" description = "Fastest Python implementation of JSON schema" category = "main" optional = false @@ -393,7 +393,7 @@ docs = ["sphinx"] [[package]] name = "identify" -version = "2.5.1" +version = "2.5.2" description = "File identification library for Python" category = "dev" optional = false @@ -436,7 +436,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [[package]] name = "importlib-resources" -version = "5.8.0" +version = "5.9.0" description = "Read resources from Python packages" category = "main" optional = false @@ -446,8 +446,8 @@ python-versions = ">=3.7" zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" @@ -561,7 +561,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.7.2" +version = "4.8.0" description = "An implementation of JSON Schema validation for Python" category = "main" optional = false @@ -622,7 +622,7 @@ python-versions = ">=3.7" [[package]] name = "kiwisolver" -version = "1.4.3" +version = "1.4.4" description = "A fast implementation of the Cassowary constraint solver" category = "main" optional = true @@ -975,8 +975,8 @@ optional = false python-versions = ">=3.6" [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["pytest-benchmark", "pytest"] +dev = ["tox", "pre-commit"] [[package]] name = "pre-commit" @@ -1089,7 +1089,7 @@ python-versions = ">=3.6" [[package]] name = "pylint" -version = "2.14.4" +version = "2.14.5" description = "python code static checker" category = "dev" optional = false @@ -1179,7 +1179,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] +testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"] [[package]] name = "pytest-env" @@ -1283,14 +1283,14 @@ jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] [[package]] name = "scipy" -version = "1.8.1" +version = "1.9.0" description = "SciPy: Scientific Library for Python" category = "main" optional = false -python-versions = ">=3.8,<3.11" +python-versions = ">=3.8,<3.12" [package.dependencies] -numpy = ">=1.17.3,<1.25.0" +numpy = ">=1.18.5,<1.25.0" [[package]] name = "setuptools-scm" @@ -1378,7 +1378,7 @@ docutils = "<0.18" sphinx = ">=1.6" [package.extras] -dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] [[package]] name = "sphinxcontrib-applehelp" @@ -1525,8 +1525,8 @@ python-versions = ">=3.6" webencodings = ">=0.4" [package.extras] -doc = ["sphinx", "sphinx-rtd-theme"] -test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"] +test = ["coverage", "pytest-isort", "pytest-flake8", "pytest-cov", "pytest"] +doc = ["sphinx-rtd-theme", "sphinx"] [[package]] name = "toml" @@ -1581,7 +1581,7 @@ python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.10" +version = "1.26.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1594,21 +1594,20 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.15.1" +version = "20.16.2" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] distlib = ">=0.3.1,<1" filelock = ">=3.2,<4" platformdirs = ">=2,<3" -six = ">=1.9.0,<2" [package.extras] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] [[package]] name = "wcwidth" @@ -1686,7 +1685,10 @@ appnope = [ {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, ] -astroid = [] +astroid = [ + {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, + {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, +] asttokens = [ {file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"}, {file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"}, @@ -1694,10 +1696,12 @@ asttokens = [ asv = [ {file = "asv-0.4.2.tar.gz", hash = "sha256:9134f56b7a2f465420f17b5bb0dee16047a70f01029c996b7ab3f197de2d0779"}, ] -atomicwrites = [] +atomicwrites = [ + {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, +] attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, + {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, + {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, ] babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, @@ -1715,12 +1719,80 @@ beautifulsoup4 = [ {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, ] -bleach = [] +bleach = [ + {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, + {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, +] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] -cffi = [] +cffi = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -1741,12 +1813,73 @@ commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] -coverage = [] +coverage = [ + {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"}, + {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"}, + {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"}, + {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"}, + {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"}, + {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"}, + {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"}, + {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"}, + {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"}, + {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"}, + {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"}, + {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"}, + {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"}, + {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"}, + {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"}, + {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"}, + {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"}, + {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"}, + {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"}, + {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"}, + {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"}, + {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"}, + {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"}, + {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"}, + {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"}, + {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"}, + {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"}, + {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"}, + {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"}, + {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"}, + {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"}, + {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"}, + {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"}, + {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"}, + {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"}, + {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"}, + {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"}, + {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"}, + {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"}, + {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, + {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, +] cycler = [ {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, ] -debugpy = [] +debugpy = [ + {file = "debugpy-1.6.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:77a47d596ce8c69673d5f0c9876a80cb5a6cbc964f3b31b2d44683c7c01b6634"}, + {file = "debugpy-1.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:726e5cc0ed5bc63e821dc371d88ddae5cba85e2ad207bf5fefc808b29421cb4c"}, + {file = "debugpy-1.6.2-cp310-cp310-win32.whl", hash = "sha256:9809bd1cdc0026fab711e280e0cb5d8f89ae5f4f74701aba5bda9a20a6afb567"}, + {file = "debugpy-1.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:40741d4bbf59baca1e97a5123514afcc036423caae5f24db23a865c0b4167c34"}, + {file = "debugpy-1.6.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:67749e972213c395647a8798cc8377646e581e1fe97d0b1b7607e6b112ae4511"}, + {file = "debugpy-1.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e3c43d650a1e5fa7110af380fb59061bcba1e7348c00237e7473c55ae499b96"}, + {file = "debugpy-1.6.2-cp37-cp37m-win32.whl", hash = "sha256:9e572c2ac3dd93f3f1a038a9226e7cc0d7326b8d345c9b9ce6fbf9cb9822e314"}, + {file = "debugpy-1.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ac5d9e625d291a041ff3eaf65bdb816eb79a5b204cf9f1ffaf9617c0eadf96fa"}, + {file = "debugpy-1.6.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:9f72435bc9a2026a35a41221beff853dd4b6b17567ba9b9d349ee9512eb71ce6"}, + {file = "debugpy-1.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaf579de5ecd02634d601d7cf5b6baae5f5bab89a55ef78e0904d766ef477729"}, + {file = "debugpy-1.6.2-cp38-cp38-win32.whl", hash = "sha256:0984086a670f46c75b5046b39a55f34e4120bee78928ac4c3c7f1c7b8be1d8be"}, + {file = "debugpy-1.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:19337bb8ff87da2535ac00ea3877ceaf40ff3c681421d1a96ab4d67dad031a16"}, + {file = "debugpy-1.6.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:163f282287ce68b00a51e9dcd7ad461ef288d740dcb3a2f22c01c62f31b62696"}, + {file = "debugpy-1.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4909bb2f8e5c8fe33d6ec5b7764100b494289252ebe94ec7838b30467435f1cb"}, + {file = "debugpy-1.6.2-cp39-cp39-win32.whl", hash = "sha256:3b4657d3cd20aa454b62a70040524d3e785efc9a8488d16cd0e6caeb7b2a3f07"}, + {file = "debugpy-1.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:79d9ac34542b830a7954ab111ad8a4c790f1f836b895d03223aea4216b739208"}, + {file = "debugpy-1.6.2-py2.py3-none-any.whl", hash = "sha256:0bfdcf261f97a603d7ef7ab6972cdf7136201fde93d19bf3f917d0d2e43a5694"}, + {file = "debugpy-1.6.2.zip", hash = "sha256:e6047272e97a11aa6898138c1c88c8cf61838deeb2a4f0a74e63bb567f8dafc6"}, +] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, @@ -1760,8 +1893,8 @@ dill = [ {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, ] distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, + {file = "distlib-0.3.5-py2.py3-none-any.whl", hash = "sha256:b710088c59f06338ca514800ad795a132da19fda270e3ce4affc74abf955a26c"}, + {file = "distlib-0.3.5.tar.gz", hash = "sha256:a7f75737c70be3b25e2bee06288cec4e4c221de18455b2dd037fe2a795cab2fe"}, ] docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, @@ -1772,22 +1905,25 @@ entrypoints = [ {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, ] executing = [ - {file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"}, - {file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"}, + {file = "executing-0.9.1-py2.py3-none-any.whl", hash = "sha256:4ce4d6082d99361c0231fc31ac1a0f56979363cc6819de0b1410784f99e49105"}, + {file = "executing-0.9.1.tar.gz", hash = "sha256:ea278e2cf90cbbacd24f1080dd1f0ac25b71b2e21f50ab439b7ba45dd3195587"}, ] fancycompleter = [ {file = "fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080"}, {file = "fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272"}, ] fastjsonschema = [ - {file = "fastjsonschema-2.15.3-py3-none-any.whl", hash = "sha256:ddb0b1d8243e6e3abb822bd14e447a89f4ab7439342912d590444831fa00b6a0"}, - {file = "fastjsonschema-2.15.3.tar.gz", hash = "sha256:0a572f0836962d844c1fc435e200b2e4f4677e4e6611a2e3bdd01ba697c275ec"}, + {file = "fastjsonschema-2.16.1-py3-none-any.whl", hash = "sha256:2f7158c4de792555753d6c2277d6a2af2d406dfd97aeca21d17173561ede4fe6"}, + {file = "fastjsonschema-2.16.1.tar.gz", hash = "sha256:d6fa3ffbe719768d70e298b9fb847484e2bdfdb7241ed052b8d57a9294a8c334"}, ] filelock = [ {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, ] -fonttools = [] +fonttools = [ + {file = "fonttools-4.34.4-py3-none-any.whl", hash = "sha256:d73f25b283cd8033367451122aa868a23de0734757a01984e4b30b18b9050c72"}, + {file = "fonttools-4.34.4.zip", hash = "sha256:9a1c52488045cd6c6491fd07711a380f932466e317cb8e016fc4e99dc7eac2f0"}, +] greenlet = [ {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, @@ -1800,6 +1936,7 @@ greenlet = [ {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, + {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, @@ -1812,6 +1949,7 @@ greenlet = [ {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, + {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, @@ -1820,6 +1958,7 @@ greenlet = [ {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, + {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, @@ -1828,6 +1967,7 @@ greenlet = [ {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, + {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, @@ -1836,32 +1976,39 @@ greenlet = [ {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, + {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, ] identify = [ - {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, - {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, + {file = "identify-2.5.2-py2.py3-none-any.whl", hash = "sha256:feaa9db2dc0ce333b453ce171c0cf1247bbfde2c55fc6bb785022d411a1b78b5"}, + {file = "identify-2.5.2.tar.gz", hash = "sha256:a3d4c096b384d50d5e6dc5bc8b9bc44f1f61cefebd750a7b3e9f939b53fb214d"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] -imagesize = [] +imagesize = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] importlib-metadata = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, ] importlib-resources = [ - {file = "importlib_resources-5.8.0-py3-none-any.whl", hash = "sha256:7952325ffd516c05a8ad0858c74dff2c3343f136fe66a6002b2623dd1d43f223"}, - {file = "importlib_resources-5.8.0.tar.gz", hash = "sha256:568c9f16cb204f9decc8d6d24a572eeea27dacbb4cee9e6b03a8025736769751"}, + {file = "importlib_resources-5.9.0-py3-none-any.whl", hash = "sha256:f78a8df21a79bcc30cfd400bdc38f314333de7c0fb619763f6b9dabab8268bb7"}, + {file = "importlib_resources-5.9.0.tar.gz", hash = "sha256:5481e97fb45af8dcf2f798952625591c58fe599d0735d86b10f54de086a61681"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] -ipykernel = [] +ipykernel = [ + {file = "ipykernel-6.15.1-py3-none-any.whl", hash = "sha256:d8969c5b23b0e453a23166da5a669c954db399789293fcb03fec5cb25367e43c"}, + {file = "ipykernel-6.15.1.tar.gz", hash = "sha256:37acc3254caa8a0dafcddddc8dc863a60ad1b46487b68aee361d9a15bda98112"}, +] ipython = [ {file = "ipython-8.4.0-py3-none-any.whl", hash = "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1"}, {file = "ipython-8.4.0.tar.gz", hash = "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd"}, @@ -1878,60 +2025,66 @@ jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] -jsonschema = [] +jsonschema = [ + {file = "jsonschema-4.8.0-py3-none-any.whl", hash = "sha256:58bb77251318cef5e1179e33dd6e7a008a3c6c638487ab4d943c2f370cc31a1a"}, + {file = "jsonschema-4.8.0.tar.gz", hash = "sha256:c1d410e379b210ba903bee6adf3fce6d5204cea4c2b622d63f914d2dbfef0993"}, +] jupyter-client = [ {file = "jupyter_client-7.3.4-py3-none-any.whl", hash = "sha256:17d74b0d0a7b24f1c8c527b24fcf4607c56bee542ffe8e3418e50b21e514b621"}, {file = "jupyter_client-7.3.4.tar.gz", hash = "sha256:aa9a6c32054b290374f95f73bb0cae91455c58dfb84f65c8591912b8f65e6d56"}, ] -jupyter-core = [] +jupyter-core = [ + {file = "jupyter_core-4.11.1-py3-none-any.whl", hash = "sha256:715e22bb6cc7db3718fddfac1f69f1c7e899ca00e42bdfd4bf3705452b9fd84a"}, + {file = "jupyter_core-4.11.1.tar.gz", hash = "sha256:2e5f244d44894c4154d06aeae3419dd7f1b0ef4494dc5584929b398c61cfd314"}, +] jupyterlab-pygments = [ {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"}, {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"}, ] kiwisolver = [ - {file = "kiwisolver-1.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fd2842a0faed9ab9aba0922c951906132d9384be89690570f0ed18cd4f20e658"}, - {file = "kiwisolver-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:caa59e2cae0e23b1e225447d7a9ddb0f982f42a6a22d497a484dfe62a06f7c0e"}, - {file = "kiwisolver-1.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d2c744aeedce22c122bb42d176b4aa6d063202a05a4abdacb3e413c214b3694"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:afe173ac2646c2636305ab820cc0380b22a00a7bca4290452e7166b4f4fa49d0"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40240da438c0ebfe2aa76dd04b844effac6679423df61adbe3437d32f23468d9"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21a3a98f0a21fc602663ca9bce2b12a4114891bdeba2dea1e9ad84db59892fca"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51078855a16b7a4984ed2067b54e35803d18bca9861cb60c60f6234b50869a56"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16635f8dddbeb1b827977d0b00d07b644b040aeb9ff8607a9fc0997afa3e567"}, - {file = "kiwisolver-1.4.3-cp310-cp310-win32.whl", hash = "sha256:2d76780d9c65c7529cedd49fa4802d713e60798d8dc3b0d5b12a0a8f38cca51c"}, - {file = "kiwisolver-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:3a297d77b3d6979693f5948df02b89431ae3645ec95865e351fb45578031bdae"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ca3eefb02ef17257fae8b8555c85e7c1efdfd777f671384b0e4ef27409b02720"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d248c46c0aa406695bda2abf99632db991f8b3a6d46018721a2892312a99f069"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb55258931448d61e2d50187de4ee66fc9d9f34908b524949b8b2b93d0c57136"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bcf0009f2012847a688f2f4f9b16203ca4c835979a02549aa0595d9f457cc8"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7cf940af5fee00a92e281eb157abe8770227a5255207818ea9a34e54a29f5b2"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dd22085446f3eca990d12a0878eeb5199dc9553b2e71716bfe7bed9915a472ab"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:d2578e5149ff49878934debfacf5c743fab49eca5ecdb983d0b218e1e554c498"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:5fb73cc8a34baba1dfa546ae83b9c248ef6150c238b06fc53d2773685b67ec67"}, - {file = "kiwisolver-1.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f70f3d028794e31cf9d1a822914efc935aadb2438ec4e8d4871d95eb1ce032d6"}, - {file = "kiwisolver-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71af5b43e4fa286a35110fc5bb740fdeae2b36ca79fbcf0a54237485baeee8be"}, - {file = "kiwisolver-1.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26b5a70bdab09e6a2f40babc4f8f992e3771751e144bda1938084c70d3001c09"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1858ad3cb686eccc7c6b7c5eac846a1cfd45aacb5811b2cf575e80b208f5622a"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc350cb65fe4e3f737d50f0465fa6ea0dcae0e5722b7edf5d5b0a0e3cd2c3c7"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:007799c7fa934646318fc128b033bb6e6baabe7fbad521bfb2279aac26225cd7"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46fb56fde006b7ef5f8eaa3698299b0ea47444238b869ff3ced1426aa9fedcb5"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b9eb88593159a53a5ee0b0159daee531ff7dd9c87fa78f5d807ca059c7eb1b2b"}, - {file = "kiwisolver-1.4.3-cp38-cp38-win32.whl", hash = "sha256:3b1dcbc49923ac3c973184a82832e1f018dec643b1e054867d04a3a22255ec6a"}, - {file = "kiwisolver-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:7118ca592d25b2957ff7b662bc0fe4f4c2b5d5b27814b9b1bc9f2fb249a970e7"}, - {file = "kiwisolver-1.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:747190fcdadc377263223f8f72b038381b3b549a8a3df5baf4d067da4749b046"}, - {file = "kiwisolver-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd628e63ffdba0112e3ddf1b1e9f3db29dd8262345138e08f4938acbc6d0805a"}, - {file = "kiwisolver-1.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:22ccba48abae827a0f952a78a7b1a7ff01866131e5bbe1f826ce9bda406bf051"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:af24b21c2283ca69c416a8a42cde9764dc36c63d3389645d28c69b0e93db3cd7"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:547111ef7cf13d73546c2de97ce434935626c897bdec96a578ca100b5fcd694b"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f85adfebd7d3c3db649efdf73659e1677a2cf3fa6e2556a3f373578af14bf7"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd7cf165ff71afb202b3f36daafbf298932bee325aac9f58e1c9cd55838bef0"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b3136eecf7e1b4a4d23e4b19d6c4e7a8e0b42d55f30444e3c529700cdacaa0d"}, - {file = "kiwisolver-1.4.3-cp39-cp39-win32.whl", hash = "sha256:46c6e5018ba31d5ee7582f323d8661498a154dea1117486a571db4c244531f24"}, - {file = "kiwisolver-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:8395064d63b26947fa2c9faeea9c3eee35e52148c5339c37987e1d96fbf009b3"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:325fa1b15098e44fe4590a6c5c09a212ca10c6ebb5d96f7447d675f6c8340e4e"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:654280c5f41831ddcc5a331c0e3ce2e480bbc3d7c93c18ecf6236313aae2d61a"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae7aa0784aeadfbd693c27993727792fbe1455b84d49970bad5886b42976b18"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb"}, - {file = "kiwisolver-1.4.3.tar.gz", hash = "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f5e60fabb7343a836360c4f0919b8cd0d6dbf08ad2ca6b9cf90bf0c76a3c4f6"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10ee06759482c78bdb864f4109886dff7b8a56529bc1609d4f1112b93fe6423c"}, + {file = "kiwisolver-1.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c79ebe8f3676a4c6630fd3f777f3cfecf9289666c84e775a67d1d358578dc2e3"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abbe9fa13da955feb8202e215c4018f4bb57469b1b78c7a4c5c7b93001699938"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7577c1987baa3adc4b3c62c33bd1118c3ef5c8ddef36f0f2c950ae0b199e100d"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8ad8285b01b0d4695102546b342b493b3ccc6781fc28c8c6a1bb63e95d22f09"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed58b8acf29798b036d347791141767ccf65eee7f26bde03a71c944449e53de"}, + {file = "kiwisolver-1.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a68b62a02953b9841730db7797422f983935aeefceb1679f0fc85cbfbd311c32"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win32.whl", hash = "sha256:e92a513161077b53447160b9bd8f522edfbed4bd9759e4c18ab05d7ef7e49408"}, + {file = "kiwisolver-1.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:3fe20f63c9ecee44560d0e7f116b3a747a5d7203376abeea292ab3152334d004"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:62ac9cc684da4cf1778d07a89bf5f81b35834cb96ca523d3a7fb32509380cbf6"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41dae968a94b1ef1897cb322b39360a0812661dba7c682aa45098eb8e193dbdf"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02f79693ec433cb4b5f51694e8477ae83b3205768a6fb48ffba60549080e295b"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0611a0a2a518464c05ddd5a3a1a0e856ccc10e67079bb17f265ad19ab3c7597"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:db5283d90da4174865d520e7366801a93777201e91e79bacbac6e6927cbceede"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1041feb4cda8708ce73bb4dcb9ce1ccf49d553bf87c3954bdfa46f0c3f77252c"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win32.whl", hash = "sha256:a553dadda40fef6bfa1456dc4be49b113aa92c2a9a9e8711e955618cd69622e3"}, + {file = "kiwisolver-1.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:03baab2d6b4a54ddbb43bba1a3a2d1627e82d205c5cf8f4c924dc49284b87166"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:841293b17ad704d70c578f1f0013c890e219952169ce8a24ebc063eecf775454"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4f270de01dd3e129a72efad823da90cc4d6aafb64c410c9033aba70db9f1ff0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f9f39e2f049db33a908319cf46624a569b36983c7c78318e9726a4cb8923b26c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97528e64cb9ebeff9701e7938653a9951922f2a38bd847787d4a8e498cc83ae"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d1573129aa0fd901076e2bfb4275a35f5b7aa60fbfb984499d661ec950320b0"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad881edc7ccb9d65b0224f4e4d05a1e85cf62d73aab798943df6d48ab0cd79a1"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b428ef021242344340460fa4c9185d0b1f66fbdbfecc6c63eff4b7c29fad429d"}, + {file = "kiwisolver-1.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2e407cb4bd5a13984a6c2c0fe1845e4e41e96f183e5e5cd4d77a857d9693494c"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win32.whl", hash = "sha256:75facbe9606748f43428fc91a43edb46c7ff68889b91fa31f53b58894503a191"}, + {file = "kiwisolver-1.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:5bce61af018b0cb2055e0e72e7d65290d822d3feee430b7b8203d8a855e78766"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8c808594c88a025d4e322d5bb549282c93c8e1ba71b790f539567932722d7bd8"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0a71d85ecdd570ded8ac3d1c0f480842f49a40beb423bb8014539a9f32a5897"}, + {file = "kiwisolver-1.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b533558eae785e33e8c148a8d9921692a9fe5aa516efbdff8606e7d87b9d5824"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:efda5fc8cc1c61e4f639b8067d118e742b812c930f708e6667a5ce0d13499e29"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7c43e1e1206cd421cd92e6b3280d4385d41d7166b3ed577ac20444b6995a445f"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc8d3bd6c72b2dd9decf16ce70e20abcb3274ba01b4e1c96031e0c4067d1e7cd"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ea39b0ccc4f5d803e3337dd46bcce60b702be4d86fd0b3d7531ef10fd99a1ac"}, + {file = "kiwisolver-1.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968f44fdbf6dd757d12920d63b566eeb4d5b395fd2d00d29d7ef00a00582aac9"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win32.whl", hash = "sha256:da7e547706e69e45d95e116e6939488d62174e033b763ab1496b4c29b76fabea"}, + {file = "kiwisolver-1.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:ba59c92039ec0a66103b1d5fe588fa546373587a7d68f5c96f743c3396afc04b"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:91672bacaa030f92fc2f43b620d7b337fd9a5af28b0d6ed3f77afc43c4a64b5a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:787518a6789009c159453da4d6b683f468ef7a65bbde796bcea803ccf191058d"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da152d8cdcab0e56e4f45eb08b9aea6455845ec83172092f09b0e077ece2cf7a"}, + {file = "kiwisolver-1.4.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ecb1fa0db7bf4cff9dac752abb19505a233c7f16684c5826d1f11ebd9472b871"}, + {file = "kiwisolver-1.4.4.tar.gz", hash = "sha256:d41997519fcba4a1e46eb4a2fe31bc12f0ff957b2b81bac28db24744f333e955"}, ] latexcodec = [ {file = "latexcodec-2.0.1-py2.py3-none-any.whl", hash = "sha256:c277a193638dc7683c4c30f6684e3db728a06efb0dc9cf346db8bd0aa6c5d271"}, @@ -2120,7 +2273,10 @@ mistune = [ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, ] -nbclient = [] +nbclient = [ + {file = "nbclient-0.6.6-py3-none-any.whl", hash = "sha256:09bae4ea2df79fa6bc50aeb8278d8b79d2036792824337fa6eee834afae17312"}, + {file = "nbclient-0.6.6.tar.gz", hash = "sha256:0df76a7961d99a681b4796c74a1f2553b9f998851acc01896dce064ad19a9027"}, +] nbconvert = [ {file = "nbconvert-6.5.0-py3-none-any.whl", hash = "sha256:c56dd0b8978a1811a5654f74c727ff16ca87dd5a43abd435a1c49b840fcd8360"}, {file = "nbconvert-6.5.0.tar.gz", hash = "sha256:223e46e27abe8596b8aed54301fadbba433b7ffea8196a68fd7b1ff509eee99d"}, @@ -2265,7 +2421,66 @@ pickleshare = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] -pillow = [] +pillow = [ + {file = "Pillow-9.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a9c9bc489f8ab30906d7a85afac4b4944a572a7432e00698a7239f44a44e6efb"}, + {file = "Pillow-9.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:510cef4a3f401c246cfd8227b300828715dd055463cdca6176c2e4036df8bd4f"}, + {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7888310f6214f19ab2b6df90f3f06afa3df7ef7355fc025e78a3044737fab1f5"}, + {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831e648102c82f152e14c1a0938689dbb22480c548c8d4b8b248b3e50967b88c"}, + {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cc1d2451e8a3b4bfdb9caf745b58e6c7a77d2e469159b0d527a4554d73694d1"}, + {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:136659638f61a251e8ed3b331fc6ccd124590eeff539de57c5f80ef3a9594e58"}, + {file = "Pillow-9.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:6e8c66f70fb539301e064f6478d7453e820d8a2c631da948a23384865cd95544"}, + {file = "Pillow-9.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:37ff6b522a26d0538b753f0b4e8e164fdada12db6c6f00f62145d732d8a3152e"}, + {file = "Pillow-9.2.0-cp310-cp310-win32.whl", hash = "sha256:c79698d4cd9318d9481d89a77e2d3fcaeff5486be641e60a4b49f3d2ecca4e28"}, + {file = "Pillow-9.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:254164c57bab4b459f14c64e93df11eff5ded575192c294a0c49270f22c5d93d"}, + {file = "Pillow-9.2.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:408673ed75594933714482501fe97e055a42996087eeca7e5d06e33218d05aa8"}, + {file = "Pillow-9.2.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:727dd1389bc5cb9827cbd1f9d40d2c2a1a0c9b32dd2261db522d22a604a6eec9"}, + {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50dff9cc21826d2977ef2d2a205504034e3a4563ca6f5db739b0d1026658e004"}, + {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb6259196a589123d755380b65127ddc60f4c64b21fc3bb46ce3a6ea663659b0"}, + {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b0554af24df2bf96618dac71ddada02420f946be943b181108cac55a7a2dcd4"}, + {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:15928f824870535c85dbf949c09d6ae7d3d6ac2d6efec80f3227f73eefba741c"}, + {file = "Pillow-9.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:bdd0de2d64688ecae88dd8935012c4a72681e5df632af903a1dca8c5e7aa871a"}, + {file = "Pillow-9.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5b87da55a08acb586bad5c3aa3b86505f559b84f39035b233d5bf844b0834b1"}, + {file = "Pillow-9.2.0-cp311-cp311-win32.whl", hash = "sha256:b6d5e92df2b77665e07ddb2e4dbd6d644b78e4c0d2e9272a852627cdba0d75cf"}, + {file = "Pillow-9.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6bf088c1ce160f50ea40764f825ec9b72ed9da25346216b91361eef8ad1b8f8c"}, + {file = "Pillow-9.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:2c58b24e3a63efd22554c676d81b0e57f80e0a7d3a5874a7e14ce90ec40d3069"}, + {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef7592281f7c174d3d6cbfbb7ee5984a671fcd77e3fc78e973d492e9bf0eb3f"}, + {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dcd7b9c7139dc8258d164b55696ecd16c04607f1cc33ba7af86613881ffe4ac8"}, + {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a138441e95562b3c078746a22f8fca8ff1c22c014f856278bdbdd89ca36cff1b"}, + {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:93689632949aff41199090eff5474f3990b6823404e45d66a5d44304e9cdc467"}, + {file = "Pillow-9.2.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:f3fac744f9b540148fa7715a435d2283b71f68bfb6d4aae24482a890aed18b59"}, + {file = "Pillow-9.2.0-cp37-cp37m-win32.whl", hash = "sha256:fa768eff5f9f958270b081bb33581b4b569faabf8774726b283edb06617101dc"}, + {file = "Pillow-9.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:69bd1a15d7ba3694631e00df8de65a8cb031911ca11f44929c97fe05eb9b6c1d"}, + {file = "Pillow-9.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:030e3460861488e249731c3e7ab59b07c7853838ff3b8e16aac9561bb345da14"}, + {file = "Pillow-9.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:74a04183e6e64930b667d321524e3c5361094bb4af9083db5c301db64cd341f3"}, + {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d33a11f601213dcd5718109c09a52c2a1c893e7461f0be2d6febc2879ec2402"}, + {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fd6f5e3c0e4697fa7eb45b6e93996299f3feee73a3175fa451f49a74d092b9f"}, + {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a647c0d4478b995c5e54615a2e5360ccedd2f85e70ab57fbe817ca613d5e63b8"}, + {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:4134d3f1ba5f15027ff5c04296f13328fecd46921424084516bdb1b2548e66ff"}, + {file = "Pillow-9.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:bc431b065722a5ad1dfb4df354fb9333b7a582a5ee39a90e6ffff688d72f27a1"}, + {file = "Pillow-9.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1536ad017a9f789430fb6b8be8bf99d2f214c76502becc196c6f2d9a75b01b76"}, + {file = "Pillow-9.2.0-cp38-cp38-win32.whl", hash = "sha256:2ad0d4df0f5ef2247e27fc790d5c9b5a0af8ade9ba340db4a73bb1a4a3e5fb4f"}, + {file = "Pillow-9.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:ec52c351b35ca269cb1f8069d610fc45c5bd38c3e91f9ab4cbbf0aebc136d9c8"}, + {file = "Pillow-9.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ed2c4ef2451de908c90436d6e8092e13a43992f1860275b4d8082667fbb2ffc"}, + {file = "Pillow-9.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ad2f835e0ad81d1689f1b7e3fbac7b01bb8777d5a985c8962bedee0cc6d43da"}, + {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea98f633d45f7e815db648fd7ff0f19e328302ac36427343e4432c84432e7ff4"}, + {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7761afe0126d046974a01e030ae7529ed0ca6a196de3ec6937c11df0df1bc91c"}, + {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a54614049a18a2d6fe156e68e188da02a046a4a93cf24f373bffd977e943421"}, + {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:5aed7dde98403cd91d86a1115c78d8145c83078e864c1de1064f52e6feb61b20"}, + {file = "Pillow-9.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:13b725463f32df1bfeacbf3dd197fb358ae8ebcd8c5548faa75126ea425ccb60"}, + {file = "Pillow-9.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:808add66ea764ed97d44dda1ac4f2cfec4c1867d9efb16a33d158be79f32b8a4"}, + {file = "Pillow-9.2.0-cp39-cp39-win32.whl", hash = "sha256:337a74fd2f291c607d220c793a8135273c4c2ab001b03e601c36766005f36885"}, + {file = "Pillow-9.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:fac2d65901fb0fdf20363fbd345c01958a742f2dc62a8dd4495af66e3ff502a4"}, + {file = "Pillow-9.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ad2277b185ebce47a63f4dc6302e30f05762b688f8dc3de55dbae4651872cdf3"}, + {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c7b502bc34f6e32ba022b4a209638f9e097d7a9098104ae420eb8186217ebbb"}, + {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1f14f5f691f55e1b47f824ca4fdcb4b19b4323fe43cc7bb105988cad7496be"}, + {file = "Pillow-9.2.0-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:dfe4c1fedfde4e2fbc009d5ad420647f7730d719786388b7de0999bf32c0d9fd"}, + {file = "Pillow-9.2.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:f07f1f00e22b231dd3d9b9208692042e29792d6bd4f6639415d2f23158a80013"}, + {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1802f34298f5ba11d55e5bb09c31997dc0c6aed919658dfdf0198a2fe75d5490"}, + {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17d4cafe22f050b46d983b71c707162d63d796a1235cdf8b9d7a112e97b15bac"}, + {file = "Pillow-9.2.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96b5e6874431df16aee0c1ba237574cb6dff1dcb173798faa6a9d8b399a05d0e"}, + {file = "Pillow-9.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927"}, + {file = "Pillow-9.2.0.tar.gz", hash = "sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04"}, +] platformdirs = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, @@ -2274,12 +2489,48 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [] +pre-commit = [ + {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, + {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, +] prompt-toolkit = [ {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, ] -psutil = [] +psutil = [ + {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, + {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, + {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, + {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, + {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, + {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, + {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, + {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, + {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, + {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, + {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, + {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, + {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, + {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, + {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, + {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, + {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, + {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, + {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, + {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, + {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, + {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, + {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, + {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, + {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, + {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, + {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, + {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, + {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, + {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, + {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, + {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, +] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -2309,8 +2560,8 @@ pygments = [ {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, ] pylint = [ - {file = "pylint-2.14.4-py3-none-any.whl", hash = "sha256:89b61867db16eefb7b3c5b84afc94081edaf11544189e2b238154677529ad69f"}, - {file = "pylint-2.14.4.tar.gz", hash = "sha256:47705453aa9dce520e123a7d51843d5f0032cbfa06870f89f00927aa1f735a4a"}, + {file = "pylint-2.14.5-py3-none-any.whl", hash = "sha256:fabe30000de7d07636d2e82c9a518ad5ad7908590fe135ace169b44839c15f90"}, + {file = "pylint-2.14.5.tar.gz", hash = "sha256:487ce2192eee48211269a0e976421f334cf94de1806ca9d0a99449adcdf0285e"}, ] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, @@ -2484,33 +2735,39 @@ requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] -rich = [] +rich = [ + {file = "rich-12.5.1-py3-none-any.whl", hash = "sha256:2eb4e6894cde1e017976d2975ac210ef515d7548bc595ba20e195fb9628acdeb"}, + {file = "rich-12.5.1.tar.gz", hash = "sha256:63a5c5ce3673d3d5fbbf23cd87e11ab84b6b451436f1b7f19ec54b6bc36ed7ca"}, +] scipy = [ - {file = "scipy-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65b77f20202599c51eb2771d11a6b899b97989159b7975e9b5259594f1d35ef4"}, - {file = "scipy-1.8.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e013aed00ed776d790be4cb32826adb72799c61e318676172495383ba4570aa4"}, - {file = "scipy-1.8.1-cp310-cp310-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:02b567e722d62bddd4ac253dafb01ce7ed8742cf8031aea030a41414b86c1125"}, - {file = "scipy-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1da52b45ce1a24a4a22db6c157c38b39885a990a566748fc904ec9f03ed8c6ba"}, - {file = "scipy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0aa8220b89b2e3748a2836fbfa116194378910f1a6e78e4675a095bcd2c762d"}, - {file = "scipy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:4e53a55f6a4f22de01ffe1d2f016e30adedb67a699a310cdcac312806807ca81"}, - {file = "scipy-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28d2cab0c6ac5aa131cc5071a3a1d8e1366dad82288d9ec2ca44df78fb50e649"}, - {file = "scipy-1.8.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:6311e3ae9cc75f77c33076cb2794fb0606f14c8f1b1c9ff8ce6005ba2c283621"}, - {file = "scipy-1.8.1-cp38-cp38-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:3b69b90c9419884efeffaac2c38376d6ef566e6e730a231e15722b0ab58f0328"}, - {file = "scipy-1.8.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6cc6b33139eb63f30725d5f7fa175763dc2df6a8f38ddf8df971f7c345b652dc"}, - {file = "scipy-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c4e3ae8a716c8b3151e16c05edb1daf4cb4d866caa385e861556aff41300c14"}, - {file = "scipy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b22fbeef3807966ea42d8163322366dd89da9bebdc075da7034cee3a1441ca"}, - {file = "scipy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:4b93ec6f4c3c4d041b26b5f179a6aab8f5045423117ae7a45ba9710301d7e462"}, - {file = "scipy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:70ebc84134cf0c504ce6a5f12d6db92cb2a8a53a49437a6bb4edca0bc101f11c"}, - {file = "scipy-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f3e7a8867f307e3359cc0ed2c63b61a1e33a19080f92fe377bc7d49f646f2ec1"}, - {file = "scipy-1.8.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:2ef0fbc8bcf102c1998c1f16f15befe7cffba90895d6e84861cd6c6a33fb54f6"}, - {file = "scipy-1.8.1-cp39-cp39-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:83606129247e7610b58d0e1e93d2c5133959e9cf93555d3c27e536892f1ba1f2"}, - {file = "scipy-1.8.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93d07494a8900d55492401917a119948ed330b8c3f1d700e0b904a578f10ead4"}, - {file = "scipy-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3b3c8924252caaffc54d4a99f1360aeec001e61267595561089f8b5900821bb"}, - {file = "scipy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70de2f11bf64ca9921fda018864c78af7147025e467ce9f4a11bc877266900a6"}, - {file = "scipy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:1166514aa3bbf04cb5941027c6e294a000bba0cf00f5cdac6c77f2dad479b434"}, - {file = "scipy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:9dd4012ac599a1e7eb63c114d1eee1bcfc6dc75a29b589ff0ad0bb3d9412034f"}, - {file = "scipy-1.8.1.tar.gz", hash = "sha256:9e3fb1b0e896f14a85aa9a28d5f755daaeeb54c897b746df7a55ccb02b340f33"}, -] -setuptools-scm = [] + {file = "scipy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0424d1bbbfa51d5ddaa16d067fd593863c9f2fb7c6840c32f8a08a8832f8e7a4"}, + {file = "scipy-1.9.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:8f2232c9d9119ec356240255a715a289b3a33be828c3e4abac11fd052ce15b1e"}, + {file = "scipy-1.9.0-cp310-cp310-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:e2004d2a3c397b26ca78e67c9d320153a1a9b71ae713ad33f4a3a3ab3d79cc65"}, + {file = "scipy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f0d6c0d6e55582d3b8f5c58ad4ca4259a02affb190f89f06c8cc02e21bba81"}, + {file = "scipy-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79dd7876614fc2869bf5d311ef33962d2066ea888bc66c80fd4fa80f8772e5a9"}, + {file = "scipy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:10417935486b320d98536d732a58362e3d37e84add98c251e070c59a6bfe0863"}, + {file = "scipy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:adb6c438c6ef550e2bb83968e772b9690cb421f2c6073f9c2cb6af15ee538bc9"}, + {file = "scipy-1.9.0-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:8d541db2d441ef87afb60c4a2addb00c3af281633602a4967e733ef4b7050504"}, + {file = "scipy-1.9.0-cp38-cp38-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:97a1f1e51ea30782d7baa8d0c52f72c3f9f05cb609cf1b990664231c5102bccd"}, + {file = "scipy-1.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:16207622570af10f9e6a2cdc7da7a9660678852477adbcd056b6d1057a036fef"}, + {file = "scipy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb687d245b6963673c639f318eea7e875d1ba147a67925586abed3d6f39bb7d8"}, + {file = "scipy-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73b704c5eea9be811919cae4caacf3180dd9212d9aed08477c1d2ba14900a9de"}, + {file = "scipy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:12005d30894e4fe7b247f7233ba0801a341f887b62e2eb99034dd6f2a8a33ad6"}, + {file = "scipy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc58c3fcb8a724b703ffbc126afdca5a8353d4d5945d5c92db85617e165299e7"}, + {file = "scipy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:01c2015e132774feefe059d5354055fec6b751d7a7d70ad2cf5ce314e7426e2a"}, + {file = "scipy-1.9.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:f7c3c578ff556333f3890c2df6c056955d53537bb176698359088108af73a58f"}, + {file = "scipy-1.9.0-cp39-cp39-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:e2ac088ea4aa61115b96b47f5f3d94b3fa29554340b6629cd2bfe6b0521ee33b"}, + {file = "scipy-1.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5d1b9cf3771fd921f7213b4b886ab2606010343bb36259b544a816044576d69e"}, + {file = "scipy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3a326673ac5afa9ef5613a61626b9ec15c8f7222b4ecd1ce0fd8fcba7b83c59"}, + {file = "scipy-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693b3fe2e7736ce0dbc72b4d933798eb6ca8ce51b8b934e3f547cc06f48b2afb"}, + {file = "scipy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:7bad16b91918bf3288089a78a4157e04892ea6475fb7a1d9bcdf32c30c8a3dba"}, + {file = "scipy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:bd490f77f35800d5620f4d9af669e372d9a88db1f76ef219e1609cc4ecdd1a24"}, + {file = "scipy-1.9.0.tar.gz", hash = "sha256:c0dfd7d2429452e7e94904c6a3af63cbaa3cf51b348bd9d35b42db7e9ad42791"}, +] +setuptools-scm = [ + {file = "setuptools_scm-7.0.5-py3-none-any.whl", hash = "sha256:7930f720905e03ccd1e1d821db521bff7ec2ac9cf0ceb6552dd73d24a45d3b02"}, + {file = "setuptools_scm-7.0.5.tar.gz", hash = "sha256:031e13af771d6f892b941adb6ea04545bbf91ebc5ce68c78aaf3fff6e1fb4844"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -2613,17 +2870,38 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [] -tornado = [] +tomlkit = [ + {file = "tomlkit-0.11.1-py3-none-any.whl", hash = "sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5"}, + {file = "tomlkit-0.11.1.tar.gz", hash = "sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e"}, +] +tornado = [ + {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, + {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, + {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, + {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, + {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, + {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, + {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, +] traitlets = [ {file = "traitlets-5.3.0-py3-none-any.whl", hash = "sha256:65fa18961659635933100db8ca120ef6220555286949774b9cfc106f941d1c7a"}, {file = "traitlets-5.3.0.tar.gz", hash = "sha256:0bb9f1f9f017aa8ec187d8b1b2a7a6626a2a1d877116baba52a129bfa124f8e2"}, ] -typing-extensions = [] -urllib3 = [] +typing-extensions = [ + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, +] +urllib3 = [ + {file = "urllib3-1.26.11-py2.py3-none-any.whl", hash = "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc"}, + {file = "urllib3-1.26.11.tar.gz", hash = "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"}, +] virtualenv = [ - {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, - {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, + {file = "virtualenv-20.16.2-py2.py3-none-any.whl", hash = "sha256:635b272a8e2f77cb051946f46c60a54ace3cb5e25568228bd6b57fc70eca9ff3"}, + {file = "virtualenv-20.16.2.tar.gz", hash = "sha256:0ef5be6d07181946891f5abc8047fda8bc2f0b4b9bf222c64e6e8963baee76db"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -2702,4 +2980,7 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -zipp = [] +zipp = [ + {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, + {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, +] From 8b8e3e367335ac84a3bd4bf6458cde9cfde8e85a Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 5 Sep 2022 12:28:49 +0200 Subject: [PATCH 144/148] Revert logger.info to lazy evaluation --- src/eko/evolution_operator/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/eko/evolution_operator/__init__.py b/src/eko/evolution_operator/__init__.py index be5f8a690..23d88b6cb 100644 --- a/src/eko/evolution_operator/__init__.py +++ b/src/eko/evolution_operator/__init__.py @@ -436,8 +436,11 @@ def run_op_integration( temp_dict[label] = res[:2] column.append(temp_dict) logger.info( - f"{self.log_label}: computing operators: - {k+1}/{self.grid_size}" - f" took: {(time.perf_counter() - start_time):6f} s" + "%s: computing operators - %u/%u took: %6f s", + self.log_label, + k + 1, + self.grid_size, + (time.perf_counter() - start_time), ) return column From 3f7dc23d605b77ef0344d6ad077b2dc79b73a3e8 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 5 Sep 2022 12:30:45 +0200 Subject: [PATCH 145/148] Try fix poetry --- poetry.lock | 449 ++++++---------------------------------------------- 1 file changed, 50 insertions(+), 399 deletions(-) diff --git a/poetry.lock b/poetry.lock index 01a168061..b3412f5e2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -232,7 +232,7 @@ optional = true python-versions = "*" [package.extras] -test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] [[package]] name = "coverage" @@ -317,7 +317,7 @@ python-versions = ">=3.6" [[package]] name = "executing" -version = "0.8.3" +version = "0.10.0" description = "Get the currently executing AST node of a frame, and other information" category = "main" optional = false @@ -393,7 +393,7 @@ docs = ["sphinx"] [[package]] name = "identify" -version = "2.5.1" +version = "2.5.3" description = "File identification library for Python" category = "dev" optional = false @@ -561,7 +561,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.7.2" +version = "4.14.0" description = "An implementation of JSON Schema validation for Python" category = "main" optional = false @@ -1001,8 +1001,8 @@ optional = false python-versions = ">=3.6" [package.extras] -testing = ["pytest-benchmark", "pytest"] -dev = ["tox", "pre-commit"] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" @@ -1208,7 +1208,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["virtualenv", "pytest-xdist", "six", "process-tests", "hunter", "fields"] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-env" @@ -1406,7 +1406,7 @@ docutils = "<0.18" sphinx = ">=1.6" [package.extras] -dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] +dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] [[package]] name = "sphinxcontrib-applehelp" @@ -1554,8 +1554,8 @@ python-versions = ">=3.6" webencodings = ">=0.4" [package.extras] -test = ["coverage", "pytest-isort", "pytest-flake8", "pytest-cov", "pytest"] -doc = ["sphinx-rtd-theme", "sphinx"] +doc = ["sphinx", "sphinx-rtd-theme"] +test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"] [[package]] name = "toml" @@ -1610,7 +1610,7 @@ python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.10" +version = "1.26.12" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1623,21 +1623,20 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.15.1" +version = "20.16.3" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.6" [package.dependencies] -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -platformdirs = ">=2,<3" -six = ">=1.9.0,<2" +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] name = "wcwidth" @@ -1716,18 +1715,12 @@ appnope = [ {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, ] astroid = [] -asttokens = [ - {file = "asttokens-2.0.5-py2.py3-none-any.whl", hash = "sha256:0844691e88552595a6f4a4281a9f7f79b8dd45ca4ccea82e5e05b4bbdb76705c"}, - {file = "asttokens-2.0.5.tar.gz", hash = "sha256:9a54c114f02c7a9480d56550932546a3f1fe71d8a02f1bc7ccd0ee3ee35cf4d5"}, -] +asttokens = [] asv = [ {file = "asv-0.4.2.tar.gz", hash = "sha256:9134f56b7a2f465420f17b5bb0dee16047a70f01029c996b7ab3f197de2d0779"}, ] atomicwrites = [] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] +attrs = [] babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, @@ -1744,80 +1737,12 @@ beautifulsoup4 = [ {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, ] -bleach = [ - {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, - {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, -] +bleach = [] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] +cffi = [] cfgv = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, @@ -1835,73 +1760,12 @@ commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] -coverage = [ - {file = "coverage-6.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e"}, - {file = "coverage-6.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39"}, - {file = "coverage-6.4.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d"}, - {file = "coverage-6.4.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc"}, - {file = "coverage-6.4.2-cp310-cp310-win32.whl", hash = "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386"}, - {file = "coverage-6.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0"}, - {file = "coverage-6.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039"}, - {file = "coverage-6.4.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e"}, - {file = "coverage-6.4.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083"}, - {file = "coverage-6.4.2-cp37-cp37m-win32.whl", hash = "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7"}, - {file = "coverage-6.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"}, - {file = "coverage-6.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8"}, - {file = "coverage-6.4.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933"}, - {file = "coverage-6.4.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de"}, - {file = "coverage-6.4.2-cp38-cp38-win32.whl", hash = "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783"}, - {file = "coverage-6.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f"}, - {file = "coverage-6.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29"}, - {file = "coverage-6.4.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978"}, - {file = "coverage-6.4.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c"}, - {file = "coverage-6.4.2-cp39-cp39-win32.whl", hash = "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd"}, - {file = "coverage-6.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf"}, - {file = "coverage-6.4.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97"}, - {file = "coverage-6.4.2.tar.gz", hash = "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe"}, -] +coverage = [] cycler = [ {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, ] -debugpy = [ - {file = "debugpy-1.6.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:77a47d596ce8c69673d5f0c9876a80cb5a6cbc964f3b31b2d44683c7c01b6634"}, - {file = "debugpy-1.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:726e5cc0ed5bc63e821dc371d88ddae5cba85e2ad207bf5fefc808b29421cb4c"}, - {file = "debugpy-1.6.2-cp310-cp310-win32.whl", hash = "sha256:9809bd1cdc0026fab711e280e0cb5d8f89ae5f4f74701aba5bda9a20a6afb567"}, - {file = "debugpy-1.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:40741d4bbf59baca1e97a5123514afcc036423caae5f24db23a865c0b4167c34"}, - {file = "debugpy-1.6.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:67749e972213c395647a8798cc8377646e581e1fe97d0b1b7607e6b112ae4511"}, - {file = "debugpy-1.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e3c43d650a1e5fa7110af380fb59061bcba1e7348c00237e7473c55ae499b96"}, - {file = "debugpy-1.6.2-cp37-cp37m-win32.whl", hash = "sha256:9e572c2ac3dd93f3f1a038a9226e7cc0d7326b8d345c9b9ce6fbf9cb9822e314"}, - {file = "debugpy-1.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ac5d9e625d291a041ff3eaf65bdb816eb79a5b204cf9f1ffaf9617c0eadf96fa"}, - {file = "debugpy-1.6.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:9f72435bc9a2026a35a41221beff853dd4b6b17567ba9b9d349ee9512eb71ce6"}, - {file = "debugpy-1.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaf579de5ecd02634d601d7cf5b6baae5f5bab89a55ef78e0904d766ef477729"}, - {file = "debugpy-1.6.2-cp38-cp38-win32.whl", hash = "sha256:0984086a670f46c75b5046b39a55f34e4120bee78928ac4c3c7f1c7b8be1d8be"}, - {file = "debugpy-1.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:19337bb8ff87da2535ac00ea3877ceaf40ff3c681421d1a96ab4d67dad031a16"}, - {file = "debugpy-1.6.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:163f282287ce68b00a51e9dcd7ad461ef288d740dcb3a2f22c01c62f31b62696"}, - {file = "debugpy-1.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4909bb2f8e5c8fe33d6ec5b7764100b494289252ebe94ec7838b30467435f1cb"}, - {file = "debugpy-1.6.2-cp39-cp39-win32.whl", hash = "sha256:3b4657d3cd20aa454b62a70040524d3e785efc9a8488d16cd0e6caeb7b2a3f07"}, - {file = "debugpy-1.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:79d9ac34542b830a7954ab111ad8a4c790f1f836b895d03223aea4216b739208"}, - {file = "debugpy-1.6.2-py2.py3-none-any.whl", hash = "sha256:0bfdcf261f97a603d7ef7ab6972cdf7136201fde93d19bf3f917d0d2e43a5694"}, - {file = "debugpy-1.6.2.zip", hash = "sha256:e6047272e97a11aa6898138c1c88c8cf61838deeb2a4f0a74e63bb567f8dafc6"}, -] +debugpy = [] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, @@ -1914,10 +1778,7 @@ dill = [ {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"}, {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, ] -distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, -] +distlib = [] docutils = [ {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, @@ -1926,103 +1787,31 @@ entrypoints = [ {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, ] -executing = [ - {file = "executing-0.8.3-py2.py3-none-any.whl", hash = "sha256:d1eef132db1b83649a3905ca6dd8897f71ac6f8cac79a7e58a1a09cf137546c9"}, - {file = "executing-0.8.3.tar.gz", hash = "sha256:c6554e21c6b060590a6d3be4b82fb78f8f0194d809de5ea7df1c093763311501"}, -] +executing = [] fancycompleter = [ {file = "fancycompleter-0.9.1-py3-none-any.whl", hash = "sha256:dd076bca7d9d524cc7f25ec8f35ef95388ffef9ef46def4d3d25e9b044ad7080"}, {file = "fancycompleter-0.9.1.tar.gz", hash = "sha256:09e0feb8ae242abdfd7ef2ba55069a46f011814a80fe5476be48f51b00247272"}, ] -fastjsonschema = [ - {file = "fastjsonschema-2.15.3-py3-none-any.whl", hash = "sha256:ddb0b1d8243e6e3abb822bd14e447a89f4ab7439342912d590444831fa00b6a0"}, - {file = "fastjsonschema-2.15.3.tar.gz", hash = "sha256:0a572f0836962d844c1fc435e200b2e4f4677e4e6611a2e3bdd01ba697c275ec"}, -] -filelock = [ - {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, - {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, -] +fastjsonschema = [] +filelock = [] fonttools = [] -greenlet = [ - {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, - {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"}, - {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"}, - {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"}, - {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"}, - {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, - {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, - {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, - {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, - {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"}, - {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"}, - {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"}, - {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, - {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, - {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, - {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, - {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, - {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, - {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, - {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, - {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, - {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, - {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, - {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, - {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, - {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, - {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, - {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, - {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, -] -identify = [ - {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, - {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"}, -] +greenlet = [] +identify = [] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] -imagesize = [ - {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, - {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, -] +imagesize = [] importlib-metadata = [ {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, ] -importlib-resources = [ - {file = "importlib_resources-5.8.0-py3-none-any.whl", hash = "sha256:7952325ffd516c05a8ad0858c74dff2c3343f136fe66a6002b2623dd1d43f223"}, - {file = "importlib_resources-5.8.0.tar.gz", hash = "sha256:568c9f16cb204f9decc8d6d24a572eeea27dacbb4cee9e6b03a8025736769751"}, -] +importlib-resources = [] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] -ipykernel = [ - {file = "ipykernel-6.15.1-py3-none-any.whl", hash = "sha256:d8969c5b23b0e453a23166da5a669c954db399789293fcb03fec5cb25367e43c"}, - {file = "ipykernel-6.15.1.tar.gz", hash = "sha256:37acc3254caa8a0dafcddddc8dc863a60ad1b46487b68aee361d9a15bda98112"}, -] +ipykernel = [] ipython = [ {file = "ipython-8.4.0-py3-none-any.whl", hash = "sha256:7ca74052a38fa25fe9bedf52da0be7d3fdd2fb027c3b778ea78dfe8c212937d1"}, {file = "ipython-8.4.0.tar.gz", hash = "sha256:f2db3a10254241d9b447232cec8b424847f338d9d36f9a577a6192c332a46abd"}, @@ -2039,67 +1828,17 @@ jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] -jsonschema = [ - {file = "jsonschema-4.8.0-py3-none-any.whl", hash = "sha256:58bb77251318cef5e1179e33dd6e7a008a3c6c638487ab4d943c2f370cc31a1a"}, - {file = "jsonschema-4.8.0.tar.gz", hash = "sha256:c1d410e379b210ba903bee6adf3fce6d5204cea4c2b622d63f914d2dbfef0993"}, -] +jsonschema = [] jupyter-client = [ {file = "jupyter_client-7.3.4-py3-none-any.whl", hash = "sha256:17d74b0d0a7b24f1c8c527b24fcf4607c56bee542ffe8e3418e50b21e514b621"}, {file = "jupyter_client-7.3.4.tar.gz", hash = "sha256:aa9a6c32054b290374f95f73bb0cae91455c58dfb84f65c8591912b8f65e6d56"}, ] -jupyter-core = [ - {file = "jupyter_core-4.11.1-py3-none-any.whl", hash = "sha256:715e22bb6cc7db3718fddfac1f69f1c7e899ca00e42bdfd4bf3705452b9fd84a"}, - {file = "jupyter_core-4.11.1.tar.gz", hash = "sha256:2e5f244d44894c4154d06aeae3419dd7f1b0ef4494dc5584929b398c61cfd314"}, -] +jupyter-core = [] jupyterlab-pygments = [ {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"}, {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"}, ] -kiwisolver = [ - {file = "kiwisolver-1.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fd2842a0faed9ab9aba0922c951906132d9384be89690570f0ed18cd4f20e658"}, - {file = "kiwisolver-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:caa59e2cae0e23b1e225447d7a9ddb0f982f42a6a22d497a484dfe62a06f7c0e"}, - {file = "kiwisolver-1.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d2c744aeedce22c122bb42d176b4aa6d063202a05a4abdacb3e413c214b3694"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:afe173ac2646c2636305ab820cc0380b22a00a7bca4290452e7166b4f4fa49d0"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40240da438c0ebfe2aa76dd04b844effac6679423df61adbe3437d32f23468d9"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21a3a98f0a21fc602663ca9bce2b12a4114891bdeba2dea1e9ad84db59892fca"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51078855a16b7a4984ed2067b54e35803d18bca9861cb60c60f6234b50869a56"}, - {file = "kiwisolver-1.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16635f8dddbeb1b827977d0b00d07b644b040aeb9ff8607a9fc0997afa3e567"}, - {file = "kiwisolver-1.4.3-cp310-cp310-win32.whl", hash = "sha256:2d76780d9c65c7529cedd49fa4802d713e60798d8dc3b0d5b12a0a8f38cca51c"}, - {file = "kiwisolver-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:3a297d77b3d6979693f5948df02b89431ae3645ec95865e351fb45578031bdae"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ca3eefb02ef17257fae8b8555c85e7c1efdfd777f671384b0e4ef27409b02720"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d248c46c0aa406695bda2abf99632db991f8b3a6d46018721a2892312a99f069"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb55258931448d61e2d50187de4ee66fc9d9f34908b524949b8b2b93d0c57136"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bcf0009f2012847a688f2f4f9b16203ca4c835979a02549aa0595d9f457cc8"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7cf940af5fee00a92e281eb157abe8770227a5255207818ea9a34e54a29f5b2"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dd22085446f3eca990d12a0878eeb5199dc9553b2e71716bfe7bed9915a472ab"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:d2578e5149ff49878934debfacf5c743fab49eca5ecdb983d0b218e1e554c498"}, - {file = "kiwisolver-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:5fb73cc8a34baba1dfa546ae83b9c248ef6150c238b06fc53d2773685b67ec67"}, - {file = "kiwisolver-1.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f70f3d028794e31cf9d1a822914efc935aadb2438ec4e8d4871d95eb1ce032d6"}, - {file = "kiwisolver-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71af5b43e4fa286a35110fc5bb740fdeae2b36ca79fbcf0a54237485baeee8be"}, - {file = "kiwisolver-1.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26b5a70bdab09e6a2f40babc4f8f992e3771751e144bda1938084c70d3001c09"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1858ad3cb686eccc7c6b7c5eac846a1cfd45aacb5811b2cf575e80b208f5622a"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc350cb65fe4e3f737d50f0465fa6ea0dcae0e5722b7edf5d5b0a0e3cd2c3c7"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:007799c7fa934646318fc128b033bb6e6baabe7fbad521bfb2279aac26225cd7"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:46fb56fde006b7ef5f8eaa3698299b0ea47444238b869ff3ced1426aa9fedcb5"}, - {file = "kiwisolver-1.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b9eb88593159a53a5ee0b0159daee531ff7dd9c87fa78f5d807ca059c7eb1b2b"}, - {file = "kiwisolver-1.4.3-cp38-cp38-win32.whl", hash = "sha256:3b1dcbc49923ac3c973184a82832e1f018dec643b1e054867d04a3a22255ec6a"}, - {file = "kiwisolver-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:7118ca592d25b2957ff7b662bc0fe4f4c2b5d5b27814b9b1bc9f2fb249a970e7"}, - {file = "kiwisolver-1.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:747190fcdadc377263223f8f72b038381b3b549a8a3df5baf4d067da4749b046"}, - {file = "kiwisolver-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd628e63ffdba0112e3ddf1b1e9f3db29dd8262345138e08f4938acbc6d0805a"}, - {file = "kiwisolver-1.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:22ccba48abae827a0f952a78a7b1a7ff01866131e5bbe1f826ce9bda406bf051"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:af24b21c2283ca69c416a8a42cde9764dc36c63d3389645d28c69b0e93db3cd7"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:547111ef7cf13d73546c2de97ce434935626c897bdec96a578ca100b5fcd694b"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f85adfebd7d3c3db649efdf73659e1677a2cf3fa6e2556a3f373578af14bf7"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffd7cf165ff71afb202b3f36daafbf298932bee325aac9f58e1c9cd55838bef0"}, - {file = "kiwisolver-1.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b3136eecf7e1b4a4d23e4b19d6c4e7a8e0b42d55f30444e3c529700cdacaa0d"}, - {file = "kiwisolver-1.4.3-cp39-cp39-win32.whl", hash = "sha256:46c6e5018ba31d5ee7582f323d8661498a154dea1117486a571db4c244531f24"}, - {file = "kiwisolver-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:8395064d63b26947fa2c9faeea9c3eee35e52148c5339c37987e1d96fbf009b3"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:325fa1b15098e44fe4590a6c5c09a212ca10c6ebb5d96f7447d675f6c8340e4e"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:654280c5f41831ddcc5a331c0e3ce2e480bbc3d7c93c18ecf6236313aae2d61a"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae7aa0784aeadfbd693c27993727792fbe1455b84d49970bad5886b42976b18"}, - {file = "kiwisolver-1.4.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:130c6c35eded399d3967cf8a542c20b671f5ba85bd6f210f8b939f868360e9eb"}, - {file = "kiwisolver-1.4.3.tar.gz", hash = "sha256:ab8a15c2750ae8d53e31f77a94f846d0a00772240f1c12817411fa2344351f86"}, -] +kiwisolver = [] latexcodec = [ {file = "latexcodec-2.0.1-py2.py3-none-any.whl", hash = "sha256:c277a193638dc7683c4c30f6684e3db728a06efb0dc9cf346db8bd0aa6c5d271"}, {file = "latexcodec-2.0.1.tar.gz", hash = "sha256:2aa2551c373261cefe2ad3a8953a6d6533e68238d180eb4bb91d7964adb3fe9a"}, @@ -2245,15 +1984,9 @@ mccabe = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -mistune = [ - {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, - {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, -] +mistune = [] nbclient = [] -nbconvert = [ - {file = "nbconvert-6.5.0-py3-none-any.whl", hash = "sha256:c56dd0b8978a1811a5654f74c727ff16ca87dd5a43abd435a1c49b840fcd8360"}, - {file = "nbconvert-6.5.0.tar.gz", hash = "sha256:223e46e27abe8596b8aed54301fadbba433b7ffea8196a68fd7b1ff509eee99d"}, -] +nbconvert = [] nbformat = [ {file = "nbformat-5.4.0-py3-none-any.whl", hash = "sha256:0d6072aaec95dddc39735c144ee8bbc6589c383fb462e4058abc855348152dad"}, {file = "nbformat-5.4.0.tar.gz", hash = "sha256:44ba5ca6acb80c5d5a500f1e5b83ede8cbe364d5a495c4c8cf60aaf1ba656501"}, @@ -2395,6 +2128,7 @@ pickleshare = [ {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] pillow = [] +pkgutil-resolve-name = [] platformdirs = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, @@ -2403,48 +2137,12 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] -pre-commit = [ - {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"}, - {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"}, -] +pre-commit = [] prompt-toolkit = [ {file = "prompt_toolkit-3.0.30-py3-none-any.whl", hash = "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"}, {file = "prompt_toolkit-3.0.30.tar.gz", hash = "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0"}, ] -psutil = [ - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, - {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, - {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, - {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, - {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, - {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, - {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, - {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, - {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, - {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, - {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, - {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, - {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, - {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, - {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, - {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, - {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, - {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, - {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, -] +psutil = [] ptyprocess = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -2469,14 +2167,8 @@ pycparser = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, -] -pylint = [ - {file = "pylint-2.14.4-py3-none-any.whl", hash = "sha256:89b61867db16eefb7b3c5b84afc94081edaf11544189e2b238154677529ad69f"}, - {file = "pylint-2.14.4.tar.gz", hash = "sha256:47705453aa9dce520e123a7d51843d5f0032cbfa06870f89f00927aa1f735a4a"}, -] +pygments = [] +pylint = [] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, @@ -2589,30 +2281,10 @@ requests = [ {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] rich = [] -scipy = [ - {file = "scipy-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65b77f20202599c51eb2771d11a6b899b97989159b7975e9b5259594f1d35ef4"}, - {file = "scipy-1.8.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e013aed00ed776d790be4cb32826adb72799c61e318676172495383ba4570aa4"}, - {file = "scipy-1.8.1-cp310-cp310-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:02b567e722d62bddd4ac253dafb01ce7ed8742cf8031aea030a41414b86c1125"}, - {file = "scipy-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1da52b45ce1a24a4a22db6c157c38b39885a990a566748fc904ec9f03ed8c6ba"}, - {file = "scipy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0aa8220b89b2e3748a2836fbfa116194378910f1a6e78e4675a095bcd2c762d"}, - {file = "scipy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:4e53a55f6a4f22de01ffe1d2f016e30adedb67a699a310cdcac312806807ca81"}, - {file = "scipy-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28d2cab0c6ac5aa131cc5071a3a1d8e1366dad82288d9ec2ca44df78fb50e649"}, - {file = "scipy-1.8.1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:6311e3ae9cc75f77c33076cb2794fb0606f14c8f1b1c9ff8ce6005ba2c283621"}, - {file = "scipy-1.8.1-cp38-cp38-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:3b69b90c9419884efeffaac2c38376d6ef566e6e730a231e15722b0ab58f0328"}, - {file = "scipy-1.8.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6cc6b33139eb63f30725d5f7fa175763dc2df6a8f38ddf8df971f7c345b652dc"}, - {file = "scipy-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c4e3ae8a716c8b3151e16c05edb1daf4cb4d866caa385e861556aff41300c14"}, - {file = "scipy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23b22fbeef3807966ea42d8163322366dd89da9bebdc075da7034cee3a1441ca"}, - {file = "scipy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:4b93ec6f4c3c4d041b26b5f179a6aab8f5045423117ae7a45ba9710301d7e462"}, - {file = "scipy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:70ebc84134cf0c504ce6a5f12d6db92cb2a8a53a49437a6bb4edca0bc101f11c"}, - {file = "scipy-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f3e7a8867f307e3359cc0ed2c63b61a1e33a19080f92fe377bc7d49f646f2ec1"}, - {file = "scipy-1.8.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:2ef0fbc8bcf102c1998c1f16f15befe7cffba90895d6e84861cd6c6a33fb54f6"}, - {file = "scipy-1.8.1-cp39-cp39-macosx_12_0_universal2.macosx_10_9_x86_64.whl", hash = "sha256:83606129247e7610b58d0e1e93d2c5133959e9cf93555d3c27e536892f1ba1f2"}, - {file = "scipy-1.8.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93d07494a8900d55492401917a119948ed330b8c3f1d700e0b904a578f10ead4"}, - {file = "scipy-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3b3c8924252caaffc54d4a99f1360aeec001e61267595561089f8b5900821bb"}, - {file = "scipy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70de2f11bf64ca9921fda018864c78af7147025e467ce9f4a11bc877266900a6"}, - {file = "scipy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:1166514aa3bbf04cb5941027c6e294a000bba0cf00f5cdac6c77f2dad479b434"}, - {file = "scipy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:9dd4012ac599a1e7eb63c114d1eee1bcfc6dc75a29b589ff0ad0bb3d9412034f"}, - {file = "scipy-1.8.1.tar.gz", hash = "sha256:9e3fb1b0e896f14a85aa9a28d5f755daaeeb54c897b746df7a55ccb02b340f33"}, +scipy = [] +setuptools-scm = [ + {file = "setuptools_scm-6.4.2-py3-none-any.whl", hash = "sha256:acea13255093849de7ccb11af9e1fb8bde7067783450cee9ef7a93139bddf6d4"}, + {file = "setuptools_scm-6.4.2.tar.gz", hash = "sha256:6833ac65c6ed9711a4d5d2266f8024cfa07c533a0e55f4c12f6eff280a5a9e30"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -2673,33 +2345,15 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -tomlkit = [ - {file = "tomlkit-0.11.1-py3-none-any.whl", hash = "sha256:1c5bebdf19d5051e2e1de6cf70adfc5948d47221f097fcff7a3ffc91e953eaf5"}, - {file = "tomlkit-0.11.1.tar.gz", hash = "sha256:61901f81ff4017951119cd0d1ed9b7af31c821d6845c8c477587bbdcd5e5854e"}, -] -tornado = [ - {file = "tornado-6.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72"}, - {file = "tornado-6.2-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75"}, - {file = "tornado-6.2-cp37-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_i686.whl", hash = "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b"}, - {file = "tornado-6.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca"}, - {file = "tornado-6.2-cp37-abi3-win32.whl", hash = "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23"}, - {file = "tornado-6.2-cp37-abi3-win_amd64.whl", hash = "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b"}, - {file = "tornado-6.2.tar.gz", hash = "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13"}, -] +tomlkit = [] +tornado = [] traitlets = [ {file = "traitlets-5.3.0-py3-none-any.whl", hash = "sha256:65fa18961659635933100db8ca120ef6220555286949774b9cfc106f941d1c7a"}, {file = "traitlets-5.3.0.tar.gz", hash = "sha256:0bb9f1f9f017aa8ec187d8b1b2a7a6626a2a1d877116baba52a129bfa124f8e2"}, ] typing-extensions = [] urllib3 = [] -virtualenv = [ - {file = "virtualenv-20.15.1-py2.py3-none-any.whl", hash = "sha256:b30aefac647e86af6d82bfc944c556f8f1a9c90427b2fb4e3bfbf338cb82becf"}, - {file = "virtualenv-20.15.1.tar.gz", hash = "sha256:288171134a2ff3bfb1a2f54f119e77cd1b81c29fc1265a2356f3e8d14c7d58c4"}, -] +virtualenv = [] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, @@ -2777,7 +2431,4 @@ wrapt = [ {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, ] -zipp = [ - {file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"}, - {file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"}, -] +zipp = [] From be82c99d4e6d0bc21f3fc72c3c7599ca57ab7706 Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 5 Sep 2022 13:09:09 +0200 Subject: [PATCH 146/148] Cast couplings order for numba --- src/eko/couplings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eko/couplings.py b/src/eko/couplings.py index b6c0d00ea..2d3ec307e 100644 --- a/src/eko/couplings.py +++ b/src/eko/couplings.py @@ -381,7 +381,7 @@ def __init__( raise NotImplementedError("a_s beyond N3LO is not implemented") if order[1] not in [0, 1, 2]: raise NotImplementedError("a_em beyond NLO is not implemented") - self.order = order + self.order = tuple(order) if method not in ["expanded", "exact"]: raise ValueError(f"Unknown method {method}") self.method = method From fdd02fd27542574de79af8c9d9f80083959ed60b Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 19 Sep 2022 11:10:40 +0200 Subject: [PATCH 147/148] Remove unlink in load_tar --- src/eko/output/legacy.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/eko/output/legacy.py b/src/eko/output/legacy.py index 089280dc6..b1ec9de2c 100644 --- a/src/eko/output/legacy.py +++ b/src/eko/output/legacy.py @@ -279,8 +279,6 @@ def load_tar(tarname: Union[str, os.PathLike]) -> struct.EKO: stream.seek(0) grids[pathlib.Path(fp.stem).stem] = np.load(stream) - fp.unlink() - q2grid = metadata["Q2grid"] for q2, slices in zip(q2grid, zip(*grids.values())): operator_grid[q2] = dict(zip(grids.keys(), slices)) From 47e30e518ae0aa1b34bd8d2b2d0bb7d0b7b479de Mon Sep 17 00:00:00 2001 From: Felix Hekhorn Date: Mon, 19 Sep 2022 14:21:16 +0200 Subject: [PATCH 148/148] Update docs --- doc/source/overview/tutorials/output.ipynb | 181 ++++----------------- 1 file changed, 28 insertions(+), 153 deletions(-) diff --git a/doc/source/overview/tutorials/output.ipynb b/doc/source/overview/tutorials/output.ipynb index 79bddaec3..13c10b68e 100644 --- a/doc/source/overview/tutorials/output.ipynb +++ b/doc/source/overview/tutorials/output.ipynb @@ -55,7 +55,7 @@ "id": "f26bf77f-9999-42d0-8ebf-2c1607214ea7", "metadata": {}, "source": [ - "Now that we have it, we can actually use one of the available formats to dump it." + "The computed operator is now it's own type (that no longer inherits from dictionary as in previous eko versions):" ] }, { @@ -63,39 +63,20 @@ "execution_count": 3, "id": "daae3811-e7a6-4a7c-9f55-eef8ea1564b8", "metadata": {}, - "outputs": [], - "source": [ - "evolution_operator.dump_tar(\"myeko.tar\")" - ] - }, - { - "cell_type": "markdown", - "id": "a0d115e4-43cd-4f83-bcb5-922dcbfc56d4", - "metadata": {}, - "source": [ - "Once dumped, we can always use the paired method to load it, at any later time." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "b62dc52e-ff36-4621-a772-502ff495456f", - "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "eko.output.Output" + "eko.output.struct.EKO" ] }, - "execution_count": 4, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "myeko = eko.output.Output.load_tar(\"myeko.tar\")\n", - "type(myeko)" + "type(evolution_operator)" ] }, { @@ -103,12 +84,12 @@ "id": "91b17b27-e423-43fe-87f2-a1eaff027c7a", "metadata": {}, "source": [ - "Now, let's inspect the content of the operator." + "Now, let's inspect the content of the operator: e.g. you can extract the theory and operator card" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "e5c5b8af-d478-48ca-bb61-9a0de680252a", "metadata": {}, "outputs": [ @@ -116,113 +97,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "Q2grid\n", - "eko_version\n", - "inputgrid\n", - "inputpids\n", - "interpolation_is_log\n", - "interpolation_polynomial_degree\n", - "interpolation_xgrid\n", - "q2_ref\n", - "targetgrid\n", - "targetpids\n" + "{'CKM': '0.97428 0.22530 0.003470 0.22520 0.97345 0.041000 0.00862 0.04030 0.999152', 'Comments': 'LO baseline for small-x res', 'DAMP': 0, 'EScaleVar': 1, 'FNS': 'FFNS', 'GF': 1.1663787e-05, 'HQ': 'POLE', 'IB': 0, 'IC': 0, 'ID': 0, 'MP': 0.938, 'MW': 80.398, 'MZ': 91.1876, 'MaxNfAs': 6, 'MaxNfPdf': 6, 'ModEv': 'EXA', 'ModSV': None, 'NfFF': 3, 'PTO': 0, 'Q0': 1.0, 'QED': 0, 'Qedref': 1.777, 'Qmb': 4.5, 'Qmc': 2.0, 'Qmt': 173.07, 'Qref': 91.2, 'SIN2TW': 0.23126, 'SxOrd': 'LL', 'SxRes': 0, 'TMC': 0, 'XIF': 1.0, 'XIR': 1.0, 'alphaqed': 0.007496251999999999, 'alphas': 0.11800000000000001, 'fact_to_ren_scale_ratio': 1.0, 'global_nx': 0, 'kDISbThr': 1.0, 'kDIScThr': 1.0, 'kDIStThr': 1.0, 'kbThr': 1.0, 'kcThr': 1.0, 'ktThr': 1.0, 'mb': 4.5, 'mc': 2.0, 'mt': 173.07, 'nf0': None, 'nfref': None}\n", + "{'Q0': 1.0, 'Q2grid': [100], 'backward_inversion': 'expanded', 'configs': {'backward_inversion': 'expanded', 'ev_op_iterations': 10, 'ev_op_max_order': [10, 0], 'interpolation_is_log': True, 'interpolation_polynomial_degree': 4, 'n_integration_cores': 0}, 'debug': {'skip_non_singlet': False, 'skip_singlet': False}, 'debug_skip_non_singlet': False, 'debug_skip_singlet': False, 'ev_op_iterations': 10, 'ev_op_max_order': 10, 'inputgrid': None, 'inputpids': None, 'interpolation_is_log': True, 'interpolation_polynomial_degree': 4, 'interpolation_xgrid': [0.001, 0.01, 0.1, 0.5, 1.0], 'n_integration_cores': 0, 'rotations': {'inputgrid': None, 'inputpids': None, 'targetgrid': None, 'targetpids': None, 'xgrid': [0.001, 0.01, 0.1, 0.5, 1.0]}, 'targetgrid': None, 'targetpids': None}\n" ] - }, - { - "data": { - "text/plain": [ - "'0.0.0'" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "print(*myeko.keys(), sep=\"\\n\")\n", - "myeko[\"eko_version\"]" - ] - }, - { - "cell_type": "markdown", - "id": "3d610d9f-b2cb-4ddf-ab02-a47d9c9638c3", - "metadata": {}, - "source": [ - "In the last step, we proved that an `eko.output.Output` object essentially behaves like a dictionary. Indeed, it is a dictionary." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "fcca9db4-a266-4abb-8aea-2390fc3aaa72", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "isinstance(myeko, dict)" - ] - }, - { - "cell_type": "markdown", - "id": "7b1baedd-8a95-4b5c-a1a0-03716534ecf3", - "metadata": {}, - "source": [ - "At the moment we have only seen one attribute in action: an `Output` object records the version of `eko` that has generated.\n", - "Let's have a look at some other ones." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "593038bf-d8db-46ce-8a19-fbb2ae1908b6", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "INTERNAL\n", - "log? True, degree? 4\n", - "[0.001 0.01 0.1 0.5 1. ]\n", - "\n", - "INPUT\n", - "grid: (5,) float64\n", - "pids: 14 \n", - "Q0^2: 1.0 GeV^2\n", - "TARGET\n", - "grid: (5,) float64\n", - "pids: 14 \n", - "\n", - "All grid the same? True\n" - ] - } - ], - "source": [ - "interpl = myeko[\"interpolation_is_log\"]\n", - "interpd = myeko[\"interpolation_polynomial_degree\"]\n", - "interpg = myeko[\"interpolation_xgrid\"]\n", - "print(f\"INTERNAL\\nlog? {interpl}, degree? {interpd}\\n{interpg}\\n\")\n", - "\n", - "ig = myeko[\"inputgrid\"]\n", - "ip = myeko[\"inputpids\"]\n", - "q0 = myeko[\"q2_ref\"]\n", - "print(f\"INPUT\\ngrid: {ig.shape} {ig.dtype}\\npids: {len(ip)} {type(ip[0])}\\nQ0^2: {q0} GeV^2\")\n", - "tg = myeko[\"targetgrid\"]\n", - "tp = myeko[\"targetpids\"]\n", - "print(f\"TARGET\\ngrid: {tg.shape} {tg.dtype}\\npids: {len(tp)} {type(tp[0])}\")\n", - "\n", - "print(\"\\nAll grid the same?\", (interpg == ig).all() and (interpg == tg).all())" + "# obtain theory card\n", + "print(evolution_operator.theory_card)\n", + "# or operator card\n", + "print(evolution_operator.operator_card)" ] }, { @@ -231,29 +115,28 @@ "metadata": {}, "source": [ "So an `Output` object has some internal parameters, related to the interpolation used for the calculation, and then some external attributes, related to the final operator delivered.\n", - "But actually, we have not accessed yet the actual operator." + "But actually, we have not accessed yet the actual operator - let's first find out again which final scales we computed:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "c82e6733-946f-4210-a6fa-95f995ee5be5", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "dict_keys([100.0])" + "array([100])" ] }, - "execution_count": 8, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "opgrid = myeko[\"Q2grid\"]\n", - "opgrid.keys()" + "evolution_operator.Q2grid" ] }, { @@ -261,13 +144,13 @@ "id": "075734fa-8108-44ff-9070-cb4e5baab072", "metadata": {}, "source": [ - "Even the operator grid is a dictionary, mapping $Q^2$ values to the operator evolving to that scale (from the unique starting scale $Q_0^2$).\n", - "In the present case there is a unique final scale, but in the general one there might be many." + "Remember that the unique starting scale is $Q_0^2$. In the present case there is a unique final scale, but in the general one there might be many.\n", + "Now, let's use this operator! The recommended way to load an operator is by using a context manager:" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "15262aee-1cfa-40f3-b072-b53ec5517784", "metadata": {}, "outputs": [ @@ -275,23 +158,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "dict_keys(['operators', 'errors'])\n", - "\n", - "OPERATOR\n", - "(14, 5, 14, 5) float64\n", - "\n", - "ERROR\n", - "(14, 5, 14, 5) float64\n" + "operator: (14, 5, 14, 5)\n", + "error: (14, 5, 14, 5)\n" ] } ], "source": [ - "print(opgrid[100.].keys())\n", - "op = opgrid[100.][\"operators\"]\n", - "operr = opgrid[100.][\"errors\"]\n", - "\n", - "print(f\"\\nOPERATOR\\n{op.shape} {op.dtype}\")\n", - "print(f\"\\nERROR\\n{operr.shape} {operr.dtype}\")" + "with evolution_operator.operator(100) as op:\n", + " print(f\"operator: {op.operator.shape}\")\n", + " print(f\"error: {op.error.shape}\")" ] }, { @@ -300,7 +175,7 @@ "metadata": {}, "source": [ "This is the final product we expected from the beginning: the evolution operator, delivered as a numerical array.\n", - "It is actually composed by 3 elements:\n", + "It is actually composed by two elements:\n", "\n", "- the **operator** itself, whose dimensions are `(flavor_out, x_out, flavor_in, x_in)`\n", "- the *error* on each operator element, propagated from the integration error on the numerical Mellin inversion (no other source is taken into account)" @@ -313,7 +188,7 @@ "source": [ "How to use this object is now completely up to the user, but a few helpers are included in another package: `ekobox`!\n", "\n", - "This package will be explored in a separate tutorial." + "This package will be explored in [a separate tutorial](./pdf.ipynb)." ] } ], @@ -333,7 +208,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.5" + "version": "3.10.6" } }, "nbformat": 4,