From 01ba8e80aed2ee4bf5ac52ba8c72095f5d2a7800 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 18 Nov 2020 10:46:51 +0200 Subject: [PATCH 001/122] fixed package name to varname --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 346f98b..7a65cdb 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ requirements = [ "dill", "dataclasses", - "python-varname", + "varname", "tabulate", "numpy", ] From 8790fb73fe24f018eca74ca3c277f4860067807a Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 18 Nov 2020 11:35:38 +0200 Subject: [PATCH 002/122] removed instruction pylint: disable=C0303,C033 --- tuneit/tools/check.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tuneit/tools/check.py b/tuneit/tools/check.py index 5fe7997..63dc8c4 100644 --- a/tuneit/tools/check.py +++ b/tuneit/tools/check.py @@ -1,7 +1,6 @@ """ Function for checking the results """ -# pylint: disable=C0303,C0330 import operator from numpy import allclose From a0b4920688c9a7431c3ee6f8bea8d99b2fc73f34 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 9 Dec 2020 12:17:04 +0200 Subject: [PATCH 003/122] fixing default value --- tuneit/tools/time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tuneit/tools/time.py b/tuneit/tools/time.py index 2facafe..ee4acac 100644 --- a/tuneit/tools/time.py +++ b/tuneit/tools/time.py @@ -26,7 +26,7 @@ def __str__(self): def default_timer(fnc, number=100): - return timeit(fnc, number=100) / number + return timeit(fnc, number=number) / number def benchmark( From 9a2e4393c293fa682020914a6d64400e1495a883 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 9 Dec 2020 12:18:38 +0200 Subject: [PATCH 004/122] adding store_value --- tuneit/tools/base.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index e54f787..630d293 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -26,6 +26,7 @@ def __init__( callback=None, callback_calls=False, label=None, + store_value=False, **kwargs, ): "Initializes the tunable object and the variables" @@ -62,6 +63,8 @@ def __init__( if n_samples: self.n_samples = n_samples + self.store_value = store_value + @property def max_samples(self): "Size of the parameter space (product of variables' size)" @@ -120,13 +123,25 @@ def __iter__(self): tmp.fix(var, val) try: if self.callback_calls: - result = self.callback(lambda: tmp.compute(**self.compute_kwargs)) + result = self.callback(lambda: self._perform_call(tmp)) else: - result = self.callback(tmp.compute(**self.compute_kwargs)) + result = self.callback(self._perform_call(tmp)) except Exception as err: result = err yield params, result + def _perform_call(self, graph): + value = graph.compute(**self.compute_kwargs) + if self.store_value: + self._value = value + return value + + @property + def value(self): + if not hasattr(self, "_value") or not self.store_value: + return self._perform_call(tunable.copy()) + return self._value + @property def label(self): "Label used for the result" From 4d993812f0dd07cd9715888b2e88b4804344989d Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Wed, 9 Dec 2020 12:23:33 +0200 Subject: [PATCH 005/122] Adding data type --- tuneit/finalize.py | 39 ++++++++++++++++++++++++++++++++++++++- tuneit/tunable.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index c4f794e..3902882 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -8,7 +8,7 @@ from .graph import Node, Key from .variable import Variable -from .tunable import Object, Function, compute +from .tunable import Object, Function, compute, Data def finalize(tunable): @@ -21,6 +21,13 @@ def finalize(tunable): class HighLevel(Node): "HighLevel view of a Node" + @property + def datas(self): + "List of dependencies that are a variable" + return tuple( + str(dep) for dep in self.dependencies if isinstance(self[dep], Data) + ) + @property def variables(self): "List of dependencies that are a variable" @@ -64,6 +71,18 @@ def __setitem__(self, key, value): def __copy__(self): return HighLevel(super().__copy__()) + def __call__(self, *args, compute_kwargs=None, **kwargs): + "kwargs are data input of the graph" + if args: + raise ValueError("args not supported, please pass them as kwargs") + + tmp = self.copy() + for key, val in kwargs.items(): + tmp.get_data(key).set(val) + + compute_kwargs = compute_kwargs or {} + return tmp.compute(**compute_kwargs) + def copy(self, reset=False, reset_tunable=True): "Copy the content of the graph unrelating the tunable variables" res = self.__copy__() @@ -99,6 +118,24 @@ def get_variable(self, variable): return self[variable] + def get_data(self, data): + "Returns the varible corresponding to var" + if isinstance(data, Data): + data = data.key + if isinstance(data, Key): + data = Key(data).key + + if not data in self.datas: + # Smart search + matches = list(filter(lambda var: var.startswith(data + "-"), self.datas)) + if len(matches) > 1: + raise KeyError("More than one data matched to %s: %s" % (data, matches)) + if len(matches) == 0: + raise KeyError("%s is not a data of %s" % (data, self)) + data = matches[0] + + return self[data] + def fix(self, variable, value=None): "Fixes the value of the variable" self.get_variable(variable).fix(value) diff --git a/tuneit/tunable.py b/tuneit/tunable.py index 274b4a4..2fa5a43 100644 --- a/tuneit/tunable.py +++ b/tuneit/tunable.py @@ -6,6 +6,7 @@ "Tunable", "tunable", "Object", + "data", "function", "Function", ] @@ -131,6 +132,11 @@ def dependencies(self): def copy(self, **kwargs): "Returns a copy of self" + # TODO: improve copy.. it should copy automatically all the data + # tmp = copy(self) + # for key, val in kwargs.items(): + # setattr(tmp, key, val) + # return tmp kwargs.setdefault("deps", self.deps) kwargs.setdefault("label", self.label) kwargs.setdefault("uid", self.uid) @@ -178,6 +184,38 @@ def __dot_attrs__(self): Object.__eq__ = lambda self, value: self.obj == value or self.__eq2__(value) +def data(label, value=None, **kwargs): + """ + A tunable function call. + + Parameters + ---------- + label: str + + """ + return Data(value, label=label, **kwargs).tunable() + + +@dataclass +class Data(Object): + "A data input of the graph" + + check: callable = None + info: callable = None + + def copy(self, **kwargs): + "Returns a copy of self" + kwargs.setdefault("check", self.check) + kwargs.setdefault("info", self.info) + return super().copy(**kwargs) + + def set(self, val): + if self.check: + if not self.check(val): + raise ValueError("Check did not return True") + self.obj = val + + def function(fnc, *args, **kwargs): """ A tunable function call. From 1b9d57c2df7c50730f6e5ffd20e4f7e090d4a6de Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Wed, 9 Dec 2020 15:48:52 +0200 Subject: [PATCH 006/122] Fixing initialization of Node --- tuneit/graph.py | 7 +++++-- tuneit/meta.py | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tuneit/graph.py b/tuneit/graph.py index 51cacac..f10b286 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -110,12 +110,15 @@ class Node(Graph, Key, bind=False): """ def __init__(self, key, value=None): - Graph.__init__(self.graph, join_graphs(value)) + if isinstance(key, Graph): + Graph.__init__(self.graph, join_graphs(key)) + else: + Graph.__init__(self.graph, join_graphs(value)) Key.__init__(self.key, key) if key not in self.graph: self.graph[key] = value - elif not isinstance(value, Graph) or self == value: + elif value and (not isinstance(value, Graph) or self == value): raise KeyError("%s already in graph and the value will not be changed") for part in self: diff --git a/tuneit/meta.py b/tuneit/meta.py index fb945fe..1133ba3 100644 --- a/tuneit/meta.py +++ b/tuneit/meta.py @@ -128,8 +128,10 @@ def __call__(cls, *args, **kwargs): values = dict() getattr(cls, "__values__").__set__(obj, values) - if len(args) == 1 and ( - isinstance(args[0], cls) or issubclass(cls, type(args[0])) + if ( + len(args) == 1 + and not kwargs + and (isinstance(args[0], cls) or issubclass(cls, type(args[0]))) ): for attr in obj.__attrs__: values[attr] = ( @@ -143,7 +145,7 @@ def __call__(cls, *args, **kwargs): try: obj.__init__(*args, **kwargs) except AttributeError: - pass + raise return obj From 7f212e748542e49682a85e6aa4f2ed8fcb80477b Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Wed, 9 Dec 2020 16:52:11 +0200 Subject: [PATCH 007/122] Setting values also of variables --- tuneit/finalize.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 3902882..ce8dc1b 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -77,8 +77,14 @@ def __call__(self, *args, compute_kwargs=None, **kwargs): raise ValueError("args not supported, please pass them as kwargs") tmp = self.copy() + for key, val in kwargs.items(): - tmp.get_data(key).set(val) + try: + tmp.get_data(key).set(val) + continue + except KeyError: + pass + tmp.fix(key, val) compute_kwargs = compute_kwargs or {} return tmp.compute(**compute_kwargs) From 912816ef7ca270dfaaada5bb93e0f8cea07fd71e Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Wed, 9 Dec 2020 16:52:39 +0200 Subject: [PATCH 008/122] Adding draft of tuner --- tuneit/tools/__init__.py | 1 + tuneit/tools/time.py | 40 ++++++++++++++++++++++++++-- tuneit/tools/tuner.py | 56 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 tuneit/tools/tuner.py diff --git a/tuneit/tools/__init__.py b/tuneit/tools/__init__.py index 5632543..2e23a5d 100644 --- a/tuneit/tools/__init__.py +++ b/tuneit/tools/__init__.py @@ -1,5 +1,6 @@ "Highlevel tools for analyzing the tunable graphs" from .base import * +from .tune import * from .check import * from .time import * diff --git a/tuneit/tools/time.py b/tuneit/tools/time.py index ee4acac..0b6e217 100644 --- a/tuneit/tools/time.py +++ b/tuneit/tools/time.py @@ -3,10 +3,13 @@ __all__ = [ "benchmark", + "optimize", + "optimise", ] from timeit import timeit from .base import sample +from .tuner import tune class Time(float): @@ -25,7 +28,7 @@ def __str__(self): __repr__ = __str__ -def default_timer(fnc, number=100): +def default_timer(fnc, number=1): return timeit(fnc, number=number) / number @@ -39,7 +42,7 @@ def benchmark( **kwargs, ): """ - Crosscheck the result of tunable against the reference. + Samples the execution time. Parameters ---------- @@ -68,3 +71,36 @@ def benchmark( label=label, **kwargs, ) + + +def optimise(tunable, timer=default_timer, timer_kwargs=None, **kwargs): + """ + Optimizes the execution time changing the tunable parameters. + + Parameters + ---------- + comparison: callable (default = numpy.allclose) + The function to use for comparison. It is called as fnc(reference, value) + and should return a value from 0 (False) to 1 (True). + reference: Any + The reference value. If None, than the default values are used to produce the result. + variables: list of str + Set of variables to sample. + n_trials: int + The number of trials per call. + timer_kwargs: dict + Arguments passed to the timer. For default timer: + - number: (int) number of iterations + kwargs: dict + Variables passed to the compute function. See help(tunable.compute) + """ + + return tune( + tunable, + callback=lambda fnc: Time(timer(fnc, **(timer_kwargs or {}))), + callback_calls=True, + **kwargs, + ) + + +optimize = optimise diff --git a/tuneit/tools/tuner.py b/tuneit/tools/tuner.py new file mode 100644 index 0000000..c7e604c --- /dev/null +++ b/tuneit/tools/tuner.py @@ -0,0 +1,56 @@ +__all__ = [ + "tune", +] + +# import optuna +# from optuna.trial import Trial +from .base import Sampler +from ..finalize import HighLevel + + +class Tuner(HighLevel, attrs=["tuner_kwargs"]): + def __init__(self, tunable, **kwargs): + "Initializes the tunable object and the variables" + super().__init__(tunable) + self.tuner_kwargs = kwargs + + def _compute(self, graph): + # fix parameters getting suggestions from sampler + # compute within callback + # give parameters and call back values to a fnc + return value + + def compute(self, **kwargs): + graph_manager = self.divide_graph() + for subgraph in graph_manager: + value = self._compute(subgraph) + graph_manager.store(subgraph, value) + + return value + + def get_sampler(self): + sampler = self.tuner_kwargs.get("sampler", None) + if not sampler: + return Sampler + if sampler in [ + "Optuna", + "optuna", + ]: + return OptunaSampler + raise ValueError(f"Unknown tuner {tuner}") + + +def tune(tunable, **kwargs): + """ + Tunes the value of the tunable object + + Parameters + ---------- + variables: list of str + Set of variables to sample. + tuner: str or class + The tune to use. Options = None, Optuna, ... + kwargs: dict + Variables passed to the tuner function + """ + return Tuner(tunable, **kwargs) From 065321bc5b9df41fe1928f6c8092f48b51ee95de Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 17 Dec 2020 12:35:40 +0200 Subject: [PATCH 009/122] added optuna as an optional package --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 7a65cdb..7082f18 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ "graphviz", ], "test": ["pytest", "pytest-cov"], + "optuna": ["optuna"], } setup( From 100c0121ffc0df5bc997036042a758a8c0c5878b Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 17 Dec 2020 12:44:37 +0200 Subject: [PATCH 010/122] fixed typo --- tuneit/tools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tuneit/tools/__init__.py b/tuneit/tools/__init__.py index 2e23a5d..85f490f 100644 --- a/tuneit/tools/__init__.py +++ b/tuneit/tools/__init__.py @@ -1,6 +1,6 @@ "Highlevel tools for analyzing the tunable graphs" from .base import * -from .tune import * +from .tuner import * from .check import * from .time import * From 5210fc83de2364b81d6b339d0175cac26b39112b Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 17 Dec 2020 12:45:39 +0200 Subject: [PATCH 011/122] a sampler based on the optuna package --- tuneit/tools/OptunaSampler.py | 114 ++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tuneit/tools/OptunaSampler.py diff --git a/tuneit/tools/OptunaSampler.py b/tuneit/tools/OptunaSampler.py new file mode 100644 index 0000000..19699b0 --- /dev/null +++ b/tuneit/tools/OptunaSampler.py @@ -0,0 +1,114 @@ +import optuna +from optuna.trial import Trial +from optuna import exceptions +from hashlib import md5 +from dill import dumps + + +class OptunaSampler: + + optuna_types = { + "discrete_uniform": Trial.suggest_discrete_uniform, + "float": Trial.suggest_float, + "int": Trial.suggest_int, + "loguniform": Trial.suggest_loguniform, + "uniform": Trial.suggest_uniform, + "categorical": Trial.suggest_categorical, + } + + def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs): + + self.tunable = tunable.copy() + self.compute_kwargs = kwargs + + self.callback = callback + + if n_trials: + self.n_trials = n_trials + else: + self.n_trials = 1 # default + + self.storage = storage + + def get_attributes(self): + return {"callback": self.callback} + + def get_study(self): + attrs = self.get_attributes() + name = md5(dumps(attrs)).hexdigest() + try: + study = optuna.create_study(study_name=name, storage=self.storage) + for key, val in attrs.items(): + study.set_user_attr( + f"{key}", f"{val}" + ) # because the value should be JSON serializable + except exceptions.DuplicatedStudyError: + study = optuna.load_study(study_name=name, storage=self.storage) + return study + + @property + def catches(self): + return (Exception,) + + def compute(self, **kwargs): + "returns the value of the graph" + self.get_study().optimize( + lambda trial: self.objective(trial, **kwargs), + self.n_trials, + catch=self.catches, + ) + value = self._value + del self._value + return value + + def _call_wrapper(self, graph, **kwargs): + self._value = graph.compute(**kwargs) + return self._value + + def objective(self, trial, **kwargs): + tmp = self.get_next_trial(trial) + return self.callback(lambda: self._call_wrapper(tmp, **kwargs)) + + def get_next_trial(self, trial): + tmp = self.tunable.copy(reset=True) + vars = self.tunable.variables + values = self.get_suggestions(trial) + for v in vars: + tmp.fix(v, values[v]) + return tmp + + def get_suggestions(self, trial): + vars = self.tunable.variables + values = {} + for v in vars: + var = self.tunable.get_variable(v) + var_values = var.values + var_type = self.deduce_type(var_values) + var_args = self.get_var_args(var_type, var_values) + values[v] = OptunaSampler.optuna_types[var_type](trial, v, *var_args) + return values + + @staticmethod + def get_var_args(var_type, var_values): + if var_type == "categorical": + return (tuple(var_values),) + elif var_type == "discrete_uniform": + step = 1 # default + return min(var_values), max(var_values), step + + @staticmethod + def deduce_type(variable): + "returns a type compatible with optuna: discrete_uniform, float, int, loguniform, uniform, categorical" + # only categorical and discrete uniform are supported for the time being + if isinstance(variable, range): + return "discrete_uniform" + return "categorical" + + def best_params(self, **kwargs): + study = self.get_study() + study.optimize( + lambda trial: self.objective(trial, **kwargs), + self.n_trials, + catch=self.catches, + ) + return study.best_params From d683c48054ee628c58d85b9e91a7fa1c5c66a478 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 17 Dec 2020 12:49:45 +0200 Subject: [PATCH 012/122] tuner was changed to work with OptunaSampler --- tuneit/tools/tuner.py | 123 +++++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 56 deletions(-) diff --git a/tuneit/tools/tuner.py b/tuneit/tools/tuner.py index c7e604c..58cee80 100644 --- a/tuneit/tools/tuner.py +++ b/tuneit/tools/tuner.py @@ -1,56 +1,67 @@ -__all__ = [ - "tune", -] - -# import optuna -# from optuna.trial import Trial -from .base import Sampler -from ..finalize import HighLevel - - -class Tuner(HighLevel, attrs=["tuner_kwargs"]): - def __init__(self, tunable, **kwargs): - "Initializes the tunable object and the variables" - super().__init__(tunable) - self.tuner_kwargs = kwargs - - def _compute(self, graph): - # fix parameters getting suggestions from sampler - # compute within callback - # give parameters and call back values to a fnc - return value - - def compute(self, **kwargs): - graph_manager = self.divide_graph() - for subgraph in graph_manager: - value = self._compute(subgraph) - graph_manager.store(subgraph, value) - - return value - - def get_sampler(self): - sampler = self.tuner_kwargs.get("sampler", None) - if not sampler: - return Sampler - if sampler in [ - "Optuna", - "optuna", - ]: - return OptunaSampler - raise ValueError(f"Unknown tuner {tuner}") - - -def tune(tunable, **kwargs): - """ - Tunes the value of the tunable object - - Parameters - ---------- - variables: list of str - Set of variables to sample. - tuner: str or class - The tune to use. Options = None, Optuna, ... - kwargs: dict - Variables passed to the tuner function - """ - return Tuner(tunable, **kwargs) +__all__ = [ + "tune", +] + +from .base import Sampler +from ..finalize import HighLevel + +try: + from .OptunaSampler import OptunaSampler +except ImportError: + OptunaSampler = None + + +class Tuner(HighLevel, attrs=["tuner_kwargs"]): + def __init__(self, tunable, **kwargs): + "Initializes the tunable object and the variables" + super().__init__(tunable) + self.tuner_kwargs = kwargs + + def compute(self, **kwargs): + # graph_manager = self.divide_graph() + # for subgraph in graph_manager: + # value = self.get_sampler()(subgraph,**self.get_sampler_kwargs()).compute(**kwargs) + # graph_manager.store(subgraph, value) + value = self.get_sampler()(self, **self.get_sampler_kwargs()).compute(**kwargs) + return value + + def get_best_trial(self): + return self.get_sampler()(self, **self.get_sampler_kwargs()).best_params() + + def get_sampler(self): + sampler = self.tuner_kwargs.get("sampler", None) + if not sampler: + return Sampler + if sampler in [ + "Optuna", + "optuna", + ]: + if OptunaSampler is None: + raise ImportError("Optuna not installed") + return OptunaSampler + raise ValueError(f"Unknown tuner {tuner}") + + def get_sampler_kwargs(self): + sampler_kwargs = {} + if self.get_sampler() is OptunaSampler: + sampler_kwargs = { + "callback": self.tuner_kwargs.get("callback", None), + "storage": "sqlite:///example.db", + } + return sampler_kwargs + + +def tune(tunable, **kwargs): + """ + Tunes the value of the tunable object + + Parameters + ---------- + variables: list of str + Set of variables to sample. + tuner: str or class + The tune to use. Options = None, Optuna, ... + kwargs: dict + Variables passed to the tuner function + """ + return Tuner(tunable, **kwargs) From cbdd411da9839e0f908ef72f9770142a403b042e Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 22 Dec 2020 14:40:51 +0200 Subject: [PATCH 013/122] fixed package name --- tuneit/tools/tuner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tuneit/tools/tuner.py b/tuneit/tools/tuner.py index 58cee80..ab1ed26 100644 --- a/tuneit/tools/tuner.py +++ b/tuneit/tools/tuner.py @@ -6,7 +6,7 @@ from ..finalize import HighLevel try: - from .OptunaSampler import OptunaSampler + from .optuna import OptunaSampler except ImportError: OptunaSampler = None @@ -39,7 +39,7 @@ def get_sampler(self): if OptunaSampler is None: raise ImportError("Optuna not installed") return OptunaSampler - raise ValueError(f"Unknown tuner {tuner}") + raise ValueError(f"Unknown sampler {sampler}") def get_sampler_kwargs(self): sampler_kwargs = {} From 514b2f5ed0609e344463930b7cecf7b1ccf46a12 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 22 Dec 2020 14:41:33 +0200 Subject: [PATCH 014/122] fixed JSON serializable error --- tuneit/tools/optuna.py | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tuneit/tools/optuna.py diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py new file mode 100644 index 0000000..3e14b83 --- /dev/null +++ b/tuneit/tools/optuna.py @@ -0,0 +1,114 @@ +import optuna +from optuna.trial import Trial +from optuna import exceptions +from hashlib import md5 +from dill import dumps + + +class OptunaSampler: + + optuna_types = { + "discrete_uniform": Trial.suggest_discrete_uniform, + "float": Trial.suggest_float, + "int": Trial.suggest_int, + "loguniform": Trial.suggest_loguniform, + "uniform": Trial.suggest_uniform, + "categorical": Trial.suggest_categorical, + } + + def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs): + + self.tunable = tunable.copy() + self.compute_kwargs = kwargs + + self.callback = callback + + if n_trials: + self.n_trials = n_trials + else: + self.n_trials = 1 # default + + self.storage = storage + + def get_attributes(self): + return {"callback": self.callback} + + def get_study(self): + attrs = self.get_attributes() + name = md5(dumps(attrs)).hexdigest() + try: + study = optuna.create_study(study_name=name, storage=self.storage) + for key, val in attrs.items(): + study.set_user_attr( + key, repr(val) + ) # because the value should be JSON serializable + except exceptions.DuplicatedStudyError: + study = optuna.load_study(study_name=name, storage=self.storage) + return study + + @property + def catches(self): + return (Exception,) + + def compute(self, **kwargs): + "returns the value of the graph" + self.get_study().optimize( + lambda trial: self.objective(trial, **kwargs), + self.n_trials, + catch=self.catches, + ) + value = self._value + del self._value + return value + + def _call_wrapper(self, graph, **kwargs): + self._value = graph.compute(**kwargs) + return self._value + + def objective(self, trial, **kwargs): + tmp = self.get_next_trial(trial) + return self.callback(lambda: self._call_wrapper(tmp, **kwargs)) + + def get_next_trial(self, trial): + tmp = self.tunable.copy(reset=True) + vars = self.tunable.variables + values = self.get_suggestions(trial) + for v in vars: + tmp.fix(v, values[v]) + return tmp + + def get_suggestions(self, trial): + vars = self.tunable.variables + values = {} + for v in vars: + var = self.tunable.get_variable(v) + var_values = var.values + var_type = self.deduce_type(var_values) + var_args = self.get_var_args(var_type, var_values) + values[v] = OptunaSampler.optuna_types[var_type](trial, v, *var_args) + return values + + @staticmethod + def get_var_args(var_type, var_values): + if var_type == "categorical": + return (tuple(var_values),) + elif var_type == "discrete_uniform": + step = 1 # default + return min(var_values), max(var_values), step + + @staticmethod + def deduce_type(variable): + "returns a type compatible with optuna: discrete_uniform, float, int, loguniform, uniform, categorical" + # only categorical and discrete uniform are supported for the time being + if isinstance(variable, range): + return "discrete_uniform" + return "categorical" + + def best_params(self, **kwargs): + study = self.get_study() + study.optimize( + lambda trial: self.objective(trial, **kwargs), + self.n_trials, + catch=self.catches, + ) + return study.best_params From 4bc82386d21aed4beb809c373c527cbd0399edd1 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 22 Dec 2020 14:44:19 +0200 Subject: [PATCH 015/122] new pytest file for the Tuner class --- test/test_tuner.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 test/test_tuner.py diff --git a/test/test_tuner.py b/test/test_tuner.py new file mode 100644 index 0000000..fe0a341 --- /dev/null +++ b/test/test_tuner.py @@ -0,0 +1,72 @@ +from tuneit import * +import numpy +from tuneit.tools.tuner import Tuner +from tuneit.tools.time import Time,default_timer +from tuneit.tools.optuna import OptunaSampler +from tuneit.tools.base import Sampler +import pytest + +def test_tuner(): + # simple example to use in tests + # building a graph with variables for sorting (preprocessing) and searching to be tuned: + + @alternatives( + mergesort = lambda a: numpy.sort(a, kind='mergesort'), + heapsort = lambda a: numpy.sort(a, kind='heapsort'), + timsort=lambda a: numpy.array(sorted(a)), + ) + def preprocessing(array): + res = numpy.sort(array) + return res + + @alternatives( + indices = lambda a, b: [i for i, x in enumerate(a.tolist()) if x == b][0], + array_search = lambda a, b: numpy.where(a == b)[0][0], + binary_search = lambda a, b: numpy.searchsorted(a, b), + ) + def searching(array, element): + l = array.tolist() + index = l.index(element) + return index + + element = 65 + result = searching(preprocessing(numpy.random.randint(1000,size=(10000))),element) #input size: 10 000, type: integers + + + + # test optimise function + obj_A = optimise(result, sampler = "optuna") + assert isinstance(obj_A,Tuner) + + # test tune function + obj_B = tune(result,callback=lambda fnc: Time(default_timer(fnc))) + assert isinstance(obj_B,Tuner) + + # test Tuner class + obj_C = Tuner(result,sampler = "optuna",callback=lambda fnc: Time(default_timer(fnc))) + assert isinstance(obj_C, Tuner) + assert bool(obj_C.tuner_kwargs) + assert callable(obj_B.tuner_kwargs.get("callback", None)) + # test compute function in Tuner + assert obj_C.compute()==finalize(result).compute() + # test get_best_trial function in Tuner + res = obj_C.get_best_trial() + assert isinstance(res,dict) + assert 'preprocessing' in {k.split('-')[0]:v for k,v in res.items()} + assert next(v for k,v in res.items() if k.startswith('preprocessing')) in preprocessing.keys() + assert 'searching' in {k.split('-')[0]:v for k,v in res.items()} + assert next(v for k,v in res.items() if k.startswith('searching')) in searching.keys() + # test get_sampler function in Tuner + assert obj_C.get_sampler() == OptunaSampler + assert Tuner(result,sampler = None,callback=lambda fnc: Time(default_timer(fnc))).get_sampler() == Sampler + with pytest.raises(ValueError): + Tuner(result,sampler = "hello",callback=lambda fnc: Time(default_timer(fnc))).get_sampler() + #test get_sampler_kwargs function in Tuner + kwargs = obj_C.get_sampler_kwargs() + assert kwargs["storage"]=="sqlite:///example.db" + assert kwargs["callback"]==obj_C.tuner_kwargs.get("callback", None) + kwargs = Tuner(result,sampler = None,callback=lambda fnc: Time(default_timer(fnc))).get_sampler_kwargs() + assert not bool(kwargs) + + + From 4a504f00cc32012c8671b4ca8b568847974f799c Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 22 Dec 2020 15:33:25 +0200 Subject: [PATCH 016/122] removing OptunaSampler --- tuneit/tools/OptunaSampler.py | 114 ---------------------------------- 1 file changed, 114 deletions(-) delete mode 100644 tuneit/tools/OptunaSampler.py diff --git a/tuneit/tools/OptunaSampler.py b/tuneit/tools/OptunaSampler.py deleted file mode 100644 index 19699b0..0000000 --- a/tuneit/tools/OptunaSampler.py +++ /dev/null @@ -1,114 +0,0 @@ -import optuna -from optuna.trial import Trial -from optuna import exceptions -from hashlib import md5 -from dill import dumps - - -class OptunaSampler: - - optuna_types = { - "discrete_uniform": Trial.suggest_discrete_uniform, - "float": Trial.suggest_float, - "int": Trial.suggest_int, - "loguniform": Trial.suggest_loguniform, - "uniform": Trial.suggest_uniform, - "categorical": Trial.suggest_categorical, - } - - def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs): - - self.tunable = tunable.copy() - self.compute_kwargs = kwargs - - self.callback = callback - - if n_trials: - self.n_trials = n_trials - else: - self.n_trials = 1 # default - - self.storage = storage - - def get_attributes(self): - return {"callback": self.callback} - - def get_study(self): - attrs = self.get_attributes() - name = md5(dumps(attrs)).hexdigest() - try: - study = optuna.create_study(study_name=name, storage=self.storage) - for key, val in attrs.items(): - study.set_user_attr( - f"{key}", f"{val}" - ) # because the value should be JSON serializable - except exceptions.DuplicatedStudyError: - study = optuna.load_study(study_name=name, storage=self.storage) - return study - - @property - def catches(self): - return (Exception,) - - def compute(self, **kwargs): - "returns the value of the graph" - self.get_study().optimize( - lambda trial: self.objective(trial, **kwargs), - self.n_trials, - catch=self.catches, - ) - value = self._value - del self._value - return value - - def _call_wrapper(self, graph, **kwargs): - self._value = graph.compute(**kwargs) - return self._value - - def objective(self, trial, **kwargs): - tmp = self.get_next_trial(trial) - return self.callback(lambda: self._call_wrapper(tmp, **kwargs)) - - def get_next_trial(self, trial): - tmp = self.tunable.copy(reset=True) - vars = self.tunable.variables - values = self.get_suggestions(trial) - for v in vars: - tmp.fix(v, values[v]) - return tmp - - def get_suggestions(self, trial): - vars = self.tunable.variables - values = {} - for v in vars: - var = self.tunable.get_variable(v) - var_values = var.values - var_type = self.deduce_type(var_values) - var_args = self.get_var_args(var_type, var_values) - values[v] = OptunaSampler.optuna_types[var_type](trial, v, *var_args) - return values - - @staticmethod - def get_var_args(var_type, var_values): - if var_type == "categorical": - return (tuple(var_values),) - elif var_type == "discrete_uniform": - step = 1 # default - return min(var_values), max(var_values), step - - @staticmethod - def deduce_type(variable): - "returns a type compatible with optuna: discrete_uniform, float, int, loguniform, uniform, categorical" - # only categorical and discrete uniform are supported for the time being - if isinstance(variable, range): - return "discrete_uniform" - return "categorical" - - def best_params(self, **kwargs): - study = self.get_study() - study.optimize( - lambda trial: self.objective(trial, **kwargs), - self.n_trials, - catch=self.catches, - ) - return study.best_params From cbce515ad33c7a8fa03d09bcca89f4c9a565a9b8 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 7 Jan 2021 15:49:10 +0200 Subject: [PATCH 017/122] using a list for variables --- setup.py | 1 + tuneit/tools/base.py | 11 ++++++++--- tuneit/tools/check.py | 4 ++-- tuneit/tools/time.py | 4 ++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 7082f18..14ef113 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,7 @@ "varname", "tabulate", "numpy", + "lyncs_utils", ] extras = { diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index 630d293..7153fdb 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -13,6 +13,7 @@ from itertools import product from tabulate import tabulate from ..finalize import finalize +from lyncs_utils import isiterable class Sampler: @@ -42,8 +43,12 @@ def __init__( self.label = label if variables: + if isinstance(variables, str): + variables = [variables] + elif not isiterable(variables): + variables = [variables] self.variables = tuple( - str(tunable.get_variable(var).key) for var in variables + str(self.tunable.get_variable(var).key) for var in variables ) set_vars = set(self.variables) @@ -139,7 +144,7 @@ def _perform_call(self, graph): @property def value(self): if not hasattr(self, "_value") or not self.store_value: - return self._perform_call(tunable.copy()) + return self._perform_call(self.tunable.copy()) return self._value @property @@ -165,7 +170,7 @@ def _repr_html_(self): return self.tabulate(tablefmt="html") -def sample(tunable, *variables, samples=100, **kwargs): +def sample(tunable, variables=None, samples=100, **kwargs): """ Samples the value of the tunable object diff --git a/tuneit/tools/check.py b/tuneit/tools/check.py index 63dc8c4..fc63d4a 100644 --- a/tuneit/tools/check.py +++ b/tuneit/tools/check.py @@ -14,7 +14,7 @@ def crosscheck( tunable, - *variables, + variables=None, comparison=allclose, samples=None, reference=None, @@ -43,7 +43,7 @@ def crosscheck( return sample( tunable, - *variables, + variables=variables, callback=lambda res: comparison(reference, res), samples=samples, label=label, diff --git a/tuneit/tools/time.py b/tuneit/tools/time.py index 0b6e217..4881a1c 100644 --- a/tuneit/tools/time.py +++ b/tuneit/tools/time.py @@ -34,7 +34,7 @@ def default_timer(fnc, number=1): def benchmark( tunable, - *variables, + variables=None, timer=default_timer, timer_kwargs=None, samples=None, @@ -64,7 +64,7 @@ def benchmark( return sample( tunable, - *variables, + variables=variables, callback=lambda fnc: Time(timer(fnc, **(timer_kwargs or {}))), callback_calls=True, samples=samples, From b329937770d005bbbe4d6bc60d4c0d3b42ed9b1c Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:01:10 +0200 Subject: [PATCH 018/122] functions for merging nodes have been added --- tuneit/finalize.py | 124 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 3 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index ce8dc1b..29e26bb 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -2,13 +2,12 @@ High Level vision of Tunable object """ -__all__ = [ - "finalize", -] +__all__ = ["finalize"] from .graph import Node, Key from .variable import Variable from .tunable import Object, Function, compute, Data +from .subgraph import Subgraph def finalize(tunable): @@ -35,6 +34,15 @@ def variables(self): str(dep) for dep in self.dependencies if isinstance(self[dep], Variable) ) + @property + def direct_variables(self): + "List of dependencies that are a variable" + return tuple( + str(dep) + for dep in self.direct_dependencies + if isinstance(self[dep], Variable) + ) + @property def functions(self): "List of dependencies that are a functions" @@ -60,6 +68,11 @@ def depends_on(self, value): return self.depends_on(value.key) return False + def get_node(self, key): + if isinstance(key, Object): + key = key.key + return HighLevel(self.graph[key]) + def __getitem__(self, key): if isinstance(key, Object): key = key.key @@ -150,3 +163,108 @@ def compute(self, **kwargs): "Computes the result of the Node" kwargs.setdefault("graph", self.graph) return compute(self.value, **kwargs) + + def merge(self, nodes): + "check conditions and return a new graph with the nodes merged in one function" + # nodes must be functions, only one output is allowed and they must be consecutive/parallel + # new function must ensure that all operations are executed in the correct order + + def remove(self, nodes): + "removes the nodes that will be merged from the graph (except the last one)" + for node in nodes: + del self.graph[node] + + def replace(self, last_node, new_node): + "replaces last_node with new_node and changes outside dependencies to the new node" + for node in self.graph: + list_of_deps = list( + str(dep) for dep in self.get_node(node).first_dependencies + ) + for (i, dep) in enumerate(list_of_deps): + if dep == last_node: + list(self.get_node(node).first_dependencies)[i].key = str( + new_node.key + ) + self.graph[str(new_node.key)] = new_node + + def merge(self, nodes): + "merges a list of nodes from the graph into a new node" + for node in nodes: + if not isinstance(self[node], Function): + raise ValueError("The node does not represent a function") + last_node, merge = self.mergeable(nodes) + if not merge: + raise ValueError("Group of nodes not mergeable") + nodes_values = [self[node] for node in nodes] + deps = set( + [ + str(dep) + for node in nodes + for dep in self.get_node(node).first_dependencies + ] + ) + deps = tuple([Key(dep) for dep in list(deps) if dep not in nodes]) + sub = Subgraph(nodes_values, output=last_node, dependencies=deps) + new_node = Function(sub, args=sub.dependencies) + new_graph = self.copy(reset=True) + nodes.remove(last_node) + new_graph.remove(nodes) + new_graph.replace(last_node, new_node) + return new_graph + + def consecutive(self, nodes): + "implements a DFS to check if the undirected graph is connected (if all nodes are consecutive)" + stack = [nodes[0]] + unvisited = nodes[1:] + while stack != []: + u = stack[-1] + appended = False + for w in [str(dep) for dep in self.get_node(u).first_dependencies]: + if w in unvisited: + unvisited.remove(w) + stack.append(w) + appended = True + break + if appended == False: + for w in [ + str(node) + for node in nodes + if u in self.get_node(node).first_dependencies + ]: + if w in unvisited: + unvisited.remove(w) + stack.append(w) + appended = True + break + if appended == False: + stack.pop() + if unvisited == []: + return True + return False + + def one_output(self, nodes): + "gets a list of nodes and checks that they only produce one output" + common = [] + common_outside = [] + all_nodes = [ + str(n) for n in self.dependencies if isinstance(self[str(n)], Function) + ] + for node in all_nodes: + deps = list( + set( + [str(dep) for dep in self.get_node(str(node)).first_dependencies] + ).intersection(nodes) + ) + if node in nodes: + common = common + deps + else: + common_outside = common_outside + deps + if len(set(common_outside)) == 1 and set(common + common_outside) == set(nodes): + return common_outside[0], True + return common_outside, False + + def mergeable(self, nodes): + last_node, res = self.one_output(nodes) + if self.consecutive(nodes) and res: + return last_node, True + return last_node, False From 88408d79469c87e91722504cf8ceb281b8e4cbd7 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:02:39 +0200 Subject: [PATCH 019/122] changes were made to visualize groups within a graph --- tuneit/graph.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tuneit/graph.py b/tuneit/graph.py index f10b286..5e10232 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -199,6 +199,8 @@ def first_dependencies(self): if isinstance(val, Key): yield Key(val) + direct_dependencies = first_dependencies + @property def dependencies(self): "Iterates over the dependencies" @@ -242,7 +244,7 @@ def join_graphs(graphs): return Graph() -def visualize(graph, start=None, end=None, **kwargs): +def visualize(graph, start=None, end=None, groups=None, **kwargs): "Visualizes the graph returning a dot graph" assert isinstance(graph, Graph), "graph must be of type Graph" @@ -280,6 +282,19 @@ def visualize(graph, start=None, end=None, **kwargs): continue dot.edge(str(dep), str(key)) + if groups is not None: + for (i, group) in enumerate(groups): + with dot.subgraph(name=f"cluster_{i}") as c: + c.attr(color="blue") + c.node_attr.update(style="filled", color="white") + # for n in group: + # node = graph[n] + # deps = [str(dep) for dep in node.first_dependencies if isinstance(graph.get_item_(dep),Variable)] + # c.edges([(n, dep) for dep in deps]) + for n in group: + node = graph[n] + c.node(n, node.label, **node.dot_attrs) + return dot From db50dddfddb36e2a7c51ed99d3affad2cbf7414d Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:03:51 +0200 Subject: [PATCH 020/122] added documentation --- tuneit/tools/optuna.py | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py index 3e14b83..295bd78 100644 --- a/tuneit/tools/optuna.py +++ b/tuneit/tools/optuna.py @@ -6,6 +6,7 @@ class OptunaSampler: + "Creates a sampler using the optuna package" optuna_types = { "discrete_uniform": Trial.suggest_discrete_uniform, @@ -17,6 +18,23 @@ class OptunaSampler: } def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs): + """ + Initialises the parameters of the class: + + Parameters + ---------- + tunable: HighLevel object + A finalised tunable object whose parameters will be tuned. + kwargs: Any + Variables that will be used for the computation of the graph. + n_trials: int + The number of trials optuna will execute for the tunable object. + storage: sqlite file name + Local file where the trials in this study will be saved. + Example: "sqlite:///example.db" + callback: callable + The objective function to be used for the tuning of parameters. + """ self.tunable = tunable.copy() self.compute_kwargs = kwargs @@ -31,9 +49,11 @@ def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs self.storage = storage def get_attributes(self): + "Returns a dictionary of attributes that characterize the study" return {"callback": self.callback} def get_study(self): + "Creates a new study or loads a pre-existing one if the name already exists" attrs = self.get_attributes() name = md5(dumps(attrs)).hexdigest() try: @@ -51,7 +71,7 @@ def catches(self): return (Exception,) def compute(self, **kwargs): - "returns the value of the graph" + "Returns the value of the graph after completing the set number of trials for the tuning of the parameters" self.get_study().optimize( lambda trial: self.objective(trial, **kwargs), self.n_trials, @@ -62,14 +82,17 @@ def compute(self, **kwargs): return value def _call_wrapper(self, graph, **kwargs): + "Computes and returns the value of the graph" self._value = graph.compute(**kwargs) return self._value def objective(self, trial, **kwargs): + "Computes and returns the objective function (callback) value for the next trial" tmp = self.get_next_trial(trial) return self.callback(lambda: self._call_wrapper(tmp, **kwargs)) def get_next_trial(self, trial): + "Returns the next trial: a tunable object whose variables have been fixed with a combination of options" tmp = self.tunable.copy(reset=True) vars = self.tunable.variables values = self.get_suggestions(trial) @@ -78,6 +101,7 @@ def get_next_trial(self, trial): return tmp def get_suggestions(self, trial): + "Returns a suggested option for each variable that will be tuned" vars = self.tunable.variables values = {} for v in vars: @@ -90,6 +114,7 @@ def get_suggestions(self, trial): @staticmethod def get_var_args(var_type, var_values): + "Returns the arguments needed for each optuna type of variable" if var_type == "categorical": return (tuple(var_values),) elif var_type == "discrete_uniform": @@ -98,13 +123,14 @@ def get_var_args(var_type, var_values): @staticmethod def deduce_type(variable): - "returns a type compatible with optuna: discrete_uniform, float, int, loguniform, uniform, categorical" + "Returns a type compatible with optuna: discrete_uniform, float, int, loguniform, uniform, categorical" # only categorical and discrete uniform are supported for the time being if isinstance(variable, range): return "discrete_uniform" return "categorical" def best_params(self, **kwargs): + "Returns the best options for the variables of this graph" study = self.get_study() study.optimize( lambda trial: self.objective(trial, **kwargs), From c1cf27e1f3b9628a7d4ee803a1f484b3b13ec564 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:04:08 +0200 Subject: [PATCH 021/122] added documentation --- tuneit/tools/tuner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tuneit/tools/tuner.py b/tuneit/tools/tuner.py index ab1ed26..e676188 100644 --- a/tuneit/tools/tuner.py +++ b/tuneit/tools/tuner.py @@ -18,6 +18,7 @@ def __init__(self, tunable, **kwargs): self.tuner_kwargs = kwargs def compute(self, **kwargs): + "Calls a sampler to tune the whole graph" # graph_manager = self.divide_graph() # for subgraph in graph_manager: # value = self.get_sampler()(subgraph,**self.get_sampler_kwargs()).compute(**kwargs) @@ -26,9 +27,11 @@ def compute(self, **kwargs): return value def get_best_trial(self): + "Returns the best options for the variables of the graph after it has been tuned" return self.get_sampler()(self, **self.get_sampler_kwargs()).best_params() def get_sampler(self): + "Returns the name of the appropriate sampler class to be called based on the sampler argument in kwargs" sampler = self.tuner_kwargs.get("sampler", None) if not sampler: return Sampler @@ -42,6 +45,7 @@ def get_sampler(self): raise ValueError(f"Unknown sampler {sampler}") def get_sampler_kwargs(self): + "Returns the appropriate arguments for the selected sampler" sampler_kwargs = {} if self.get_sampler() is OptunaSampler: sampler_kwargs = { From b3371bf4b78792a833902fdc2255ecebd23d808a Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:06:22 +0200 Subject: [PATCH 022/122] tests for the optuna class --- test/test_optuna.py | 122 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 test/test_optuna.py diff --git a/test/test_optuna.py b/test/test_optuna.py new file mode 100644 index 0000000..dde3f9d --- /dev/null +++ b/test/test_optuna.py @@ -0,0 +1,122 @@ +from tuneit import * +import numpy +from tuneit.tools.time import Time, default_timer +from tuneit.tools.optuna import OptunaSampler +import pytest +import optuna +from optuna.trial import Trial +from optuna.trial import create_trial +from optuna.distributions import CategoricalDistribution +from optuna.trial import TrialState +from tuneit.finalize import HighLevel +from optuna.study import Study +from hashlib import md5 +from dill import dumps + + +def test_optuna_sampler(): + # simple example to use in tests + # building a graph with variables for sorting (preprocessing) and searching to be tuned: + + @alternatives( + mergesort=lambda a: numpy.sort(a, kind="mergesort"), + heapsort=lambda a: numpy.sort(a, kind="heapsort"), + timsort=lambda a: numpy.array(sorted(a)), + ) + def preprocessing(array): + res = numpy.sort(array) + return res + + @alternatives( + indices=lambda a, b: [i for i, x in enumerate(a.tolist()) if x == b][0], + array_search=lambda a, b: numpy.where(a == b)[0][0], + binary_search=lambda a, b: numpy.searchsorted(a, b), + ) + def searching(array, element): + l = array.tolist() + index = l.index(element) + return index + + element = 65 + result = searching( + preprocessing(numpy.random.randint(1000, size=(10000))), element + ) # input size: 10 000, type: integers + fz = finalize(result) + + callback_function = lambda fnc: Time(default_timer(fnc)) + obj_A = OptunaSampler( + fz, callback=callback_function, storage="sqlite:///example.db" + ) + assert isinstance(obj_A, OptunaSampler) + assert isinstance(obj_A.tunable, HighLevel) + assert not bool( + obj_A.compute_kwargs + ) # at the moment no kwargs are used for the compute function, so compute_kwargs must be empty + assert callable(obj_A.callback) + assert obj_A.n_trials > 0 + + # test get_attributes function + assert obj_A.get_attributes()["callback"] == obj_A.callback + + # test get_study function + study = obj_A.get_study() + assert isinstance(study, Study) + assert study.study_name == md5(dumps(obj_A.get_attributes())).hexdigest() + assert [*study.user_attrs.keys()] == ["callback"] + obj_B = OptunaSampler( + fz, callback=callback_function, storage="sqlite:///example.db", n_trials=10 + ) + assert obj_A.compute() == finalize(result).compute() # test compute function + assert obj_B.compute() == finalize(result).compute() + name_A = obj_A.get_study().study_name + name_B = obj_B.get_study().study_name + assert name_A == name_B + assert len(study.trials) >= 11 + + # test _call_wrapper function + assert obj_A._call_wrapper(obj_A.tunable) == finalize(result).compute() + + # test objective function + tid = study._storage.create_new_trial(study._study_id) + trial = Trial(study, trial_id=tid) + assert isinstance(obj_A.objective(trial), Time) + + # test get_next_trial + temp = obj_A.get_next_trial(trial) + assert len(temp.fixed_variables) == 2 + var_A = temp.get_variable("preprocessing") + var_B = temp.get_variable("searching") + assert var_A.fixed and var_B.fixed + + # test get_suggetions + selected_options = obj_A.get_suggestions(trial) + assert len(selected_options) == len(fz.variables) + assert [*selected_options.values()][0] in preprocessing.keys() + assert [*selected_options.values()][1] in searching.keys() + + # test get_var_args function + assert OptunaSampler.get_var_args("categorical", ["a", "b", "c"]) == ( + tuple(["a", "b", "c"]), + ) + args = OptunaSampler.get_var_args("discrete_uniform", range(100)) + assert len(args) == 3 + assert args[0] == min(range(100)) + assert args[1] == max(range(100)) + assert args[2] == 1 + + # test deduce_type function + assert OptunaSampler.deduce_type(range(100)) == "discrete_uniform" + assert OptunaSampler.deduce_type([1, 2, 17, 25]) == "categorical" + assert OptunaSampler.deduce_type(preprocessing.values()) == "categorical" + + # test best_params + best = obj_A.best_params() + assert len(best.keys()) == len(fz.variables) + assert [*best.values()][0] in preprocessing.keys() + assert [*best.values()][1] in searching.keys() + + optuna.delete_study(name_A, storage="sqlite:///example.db") + + +# If I want to delete the study and start a new one the next time I run the tests +# optuna.delete_study(name_B, storage="sqlite:///example.db") From a83aa38bc9720393466dbd70b0cb059f2d7a8590 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:06:39 +0200 Subject: [PATCH 023/122] tests for the sampler class --- test/test_sampler.py | 74 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/test_sampler.py diff --git a/test/test_sampler.py b/test/test_sampler.py new file mode 100644 index 0000000..431d511 --- /dev/null +++ b/test/test_sampler.py @@ -0,0 +1,74 @@ +from tuneit import * +import numpy +from tuneit.tools.time import Time, default_timer +from tuneit.tools.base import Sampler +from tuneit.finalize import HighLevel +import itertools + + +def test_sampler(): + # simple example to use in tests + # building a graph with variables for sorting (preprocessing) and searching to be tuned: + + @alternatives( + mergesort=lambda a: numpy.sort(a, kind="mergesort"), + heapsort=lambda a: numpy.sort(a, kind="heapsort"), + timsort=lambda a: numpy.array(sorted(a)), + ) + def preprocessing(array): + res = numpy.sort(array) + return res + + @alternatives( + indices=lambda a, b: [i for i, x in enumerate(a.tolist()) if x == b][0], + array_search=lambda a, b: numpy.where(a == b)[0][0], + binary_search=lambda a, b: numpy.searchsorted(a, b), + ) + def searching(array, element): + l = array.tolist() + index = l.index(element) + return index + + element = 65 + result = searching(preprocessing(numpy.random.randint(1000, size=(10000))), element) + fz = finalize(result) + + callback_function = lambda fnc: Time(default_timer(fnc)) + + obj = sample( + result, + ["preprocessing", "searching"], + callback=callback_function, + callback_calls=True, + ) + assert isinstance(obj, Sampler) + assert callable(obj.callback) + assert obj.callback == callback_function + assert len(obj.variables) == 2 + assert obj.variables[0] in fz.variables + assert obj.variables[1] in fz.variables + assert not obj.compute_kwargs + assert isinstance(obj.tunable, HighLevel) + + assert obj.max_samples == 16 + assert obj.n_samples == 16 + assert obj.samples == tuple( + itertools.product([*preprocessing.keys()], [*searching.keys()]), + ) + values = obj.sample_values() + for a in [x[1] for x in values]: + assert isinstance(a, Time) + assert [x[0] for x in values] == list(obj.samples) + assert obj.value == fz.compute() + + obj_B = benchmark(fz.copy(reset=True)) + assert isinstance(obj_B, Sampler) + assert obj_B.samples == obj.samples + assert obj_B.value == obj.value + + obj_C = crosscheck(fz.copy(reset=True)) + assert isinstance(obj_C, Sampler) + values2 = obj_C.sample_values() + for a in [x[1] for x in values2]: + assert isinstance(a, bool) + assert obj_C.samples == obj.samples From 1a35102f90ba009d0d2baa4e14b718b765fd5904 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:08:53 +0200 Subject: [PATCH 024/122] outline of a new subgraph class that enables the merging of nodes --- tuneit/subgraph.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tuneit/subgraph.py diff --git a/tuneit/subgraph.py b/tuneit/subgraph.py new file mode 100644 index 0000000..147893b --- /dev/null +++ b/tuneit/subgraph.py @@ -0,0 +1,10 @@ +class Subgraph: + def __init__(self, nodes, output=None, dependencies=None): + + self.nodes = nodes + self.output = output + self.dependencies = dependencies + + def __call__(self, *args): + "should call all the functions in the correct order and return the result" + pass From 7d75053dee332b2d738879e7c52d6a0ba98e106d Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 1 Feb 2021 18:11:06 +0200 Subject: [PATCH 025/122] formatting changed --- test/test_tuner.py | 78 +++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/test/test_tuner.py b/test/test_tuner.py index fe0a341..a972d78 100644 --- a/test/test_tuner.py +++ b/test/test_tuner.py @@ -1,28 +1,29 @@ from tuneit import * import numpy from tuneit.tools.tuner import Tuner -from tuneit.tools.time import Time,default_timer +from tuneit.tools.time import Time, default_timer from tuneit.tools.optuna import OptunaSampler from tuneit.tools.base import Sampler import pytest + def test_tuner(): - # simple example to use in tests + # simple example to use in tests # building a graph with variables for sorting (preprocessing) and searching to be tuned: @alternatives( - mergesort = lambda a: numpy.sort(a, kind='mergesort'), - heapsort = lambda a: numpy.sort(a, kind='heapsort'), + mergesort=lambda a: numpy.sort(a, kind="mergesort"), + heapsort=lambda a: numpy.sort(a, kind="heapsort"), timsort=lambda a: numpy.array(sorted(a)), ) def preprocessing(array): - res = numpy.sort(array) + res = numpy.sort(array) return res @alternatives( - indices = lambda a, b: [i for i, x in enumerate(a.tolist()) if x == b][0], - array_search = lambda a, b: numpy.where(a == b)[0][0], - binary_search = lambda a, b: numpy.searchsorted(a, b), + indices=lambda a, b: [i for i, x in enumerate(a.tolist()) if x == b][0], + array_search=lambda a, b: numpy.where(a == b)[0][0], + binary_search=lambda a, b: numpy.searchsorted(a, b), ) def searching(array, element): l = array.tolist() @@ -30,43 +31,56 @@ def searching(array, element): return index element = 65 - result = searching(preprocessing(numpy.random.randint(1000,size=(10000))),element) #input size: 10 000, type: integers - - + result = searching( + preprocessing(numpy.random.randint(1000, size=(10000))), element + ) # input size: 10 000, type: integers # test optimise function - obj_A = optimise(result, sampler = "optuna") - assert isinstance(obj_A,Tuner) + obj_A = optimise(result, sampler="optuna") + assert isinstance(obj_A, Tuner) # test tune function - obj_B = tune(result,callback=lambda fnc: Time(default_timer(fnc))) - assert isinstance(obj_B,Tuner) - + obj_B = tune(result, callback=lambda fnc: Time(default_timer(fnc))) + assert isinstance(obj_B, Tuner) + # test Tuner class - obj_C = Tuner(result,sampler = "optuna",callback=lambda fnc: Time(default_timer(fnc))) + obj_C = Tuner( + result, sampler="optuna", callback=lambda fnc: Time(default_timer(fnc)) + ) assert isinstance(obj_C, Tuner) assert bool(obj_C.tuner_kwargs) assert callable(obj_B.tuner_kwargs.get("callback", None)) # test compute function in Tuner - assert obj_C.compute()==finalize(result).compute() + assert obj_C.compute() == finalize(result).compute() # test get_best_trial function in Tuner res = obj_C.get_best_trial() - assert isinstance(res,dict) - assert 'preprocessing' in {k.split('-')[0]:v for k,v in res.items()} - assert next(v for k,v in res.items() if k.startswith('preprocessing')) in preprocessing.keys() - assert 'searching' in {k.split('-')[0]:v for k,v in res.items()} - assert next(v for k,v in res.items() if k.startswith('searching')) in searching.keys() + assert isinstance(res, dict) + assert "preprocessing" in {k.split("-")[0]: v for k, v in res.items()} + assert ( + next(v for k, v in res.items() if k.startswith("preprocessing")) + in preprocessing.keys() + ) + assert "searching" in {k.split("-")[0]: v for k, v in res.items()} + assert ( + next(v for k, v in res.items() if k.startswith("searching")) in searching.keys() + ) # test get_sampler function in Tuner assert obj_C.get_sampler() == OptunaSampler - assert Tuner(result,sampler = None,callback=lambda fnc: Time(default_timer(fnc))).get_sampler() == Sampler + assert ( + Tuner( + result, sampler=None, callback=lambda fnc: Time(default_timer(fnc)) + ).get_sampler() + == Sampler + ) with pytest.raises(ValueError): - Tuner(result,sampler = "hello",callback=lambda fnc: Time(default_timer(fnc))).get_sampler() - #test get_sampler_kwargs function in Tuner + Tuner( + result, sampler="hello", callback=lambda fnc: Time(default_timer(fnc)) + ).get_sampler() + # test get_sampler_kwargs function in Tuner kwargs = obj_C.get_sampler_kwargs() - assert kwargs["storage"]=="sqlite:///example.db" - assert kwargs["callback"]==obj_C.tuner_kwargs.get("callback", None) - kwargs = Tuner(result,sampler = None,callback=lambda fnc: Time(default_timer(fnc))).get_sampler_kwargs() + assert kwargs["storage"] == "sqlite:///example.db" + assert kwargs["callback"] == obj_C.tuner_kwargs.get("callback", None) + kwargs = Tuner( + result, sampler=None, callback=lambda fnc: Time(default_timer(fnc)) + ).get_sampler_kwargs() assert not bool(kwargs) - - - From 277e3fe3349ea1f14e4f3ffe062fb3795eb591a4 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Fri, 5 Feb 2021 13:36:29 +0200 Subject: [PATCH 026/122] Removing test/__init__.py --- test/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/__init__.py diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29..0000000 From 07e5d4ac6a26c2b42d3f016fd865a9fde64b089f Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Fri, 5 Feb 2021 13:36:47 +0200 Subject: [PATCH 027/122] Few changes --- tuneit/finalize.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 29e26bb..dc694df 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -36,7 +36,7 @@ def variables(self): @property def direct_variables(self): - "List of dependencies that are a variable" + "List of first dependencies that are a variable" return tuple( str(dep) for dep in self.direct_dependencies @@ -69,6 +69,7 @@ def depends_on(self, value): return False def get_node(self, key): + "Returns a node of the graph as a finalized graph" if isinstance(key, Object): key = key.key return HighLevel(self.graph[key]) @@ -164,31 +165,23 @@ def compute(self, **kwargs): kwargs.setdefault("graph", self.graph) return compute(self.value, **kwargs) - def merge(self, nodes): - "check conditions and return a new graph with the nodes merged in one function" - # nodes must be functions, only one output is allowed and they must be consecutive/parallel - # new function must ensure that all operations are executed in the correct order - def remove(self, nodes): - "removes the nodes that will be merged from the graph (except the last one)" + "Removes the list of nodes from the graph" for node in nodes: del self.graph[node] - def replace(self, last_node, new_node): - "replaces last_node with new_node and changes outside dependencies to the new node" + def replace(self, node, new_node): + "Replaces the dependencies to node with new_node" + key = str(Key(node)) + new_key = str(Key(new_node)) for node in self.graph: - list_of_deps = list( - str(dep) for dep in self.get_node(node).first_dependencies - ) - for (i, dep) in enumerate(list_of_deps): - if dep == last_node: - list(self.get_node(node).first_dependencies)[i].key = str( - new_node.key - ) - self.graph[str(new_node.key)] = new_node + for dep in self.get_node(node).first_dependencies: + if dep == key: + dep.key = str(new_key) + self.graph[new_key] = new_node def merge(self, nodes): - "merges a list of nodes from the graph into a new node" + "Returns a new graph with the list of nodes merged into a single node" for node in nodes: if not isinstance(self[node], Function): raise ValueError("The node does not represent a function") From 398be0956326af7ade3b17fe0c2eb5898f13b6f4 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 9 Mar 2021 12:31:47 +0200 Subject: [PATCH 028/122] new tests for merge functions added --- test/test_finalize.py | 60 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/test_finalize.py b/test/test_finalize.py index b59b6f3..baf4f63 100644 --- a/test/test_finalize.py +++ b/test/test_finalize.py @@ -4,6 +4,8 @@ from tuneit.tunable import Tunable from tuneit.finalize import finalize from pytest import raises +from tuneit.variable import Variable +from tuneit.finalize import HighLevel def test_finalize(): @@ -48,3 +50,61 @@ def test_finalize(): a = variable(range(10), uid=True) with raises(KeyError): finalize(a * b).fix("a") + + x = variable([1, 2, 3, 4]) + y = variable([15, 16, 17, 18]) + z = variable([10, 20, 30, 40]) + w = variable([0, 50, 100]) + a = x + y + z + w - z + a_final = finalize(a) + + b = w ** x - y + d = a + b + d_final = finalize(d) + nodes_a = list( + str(node) + for node in a_final.dependencies + if not isinstance(a_final[node], Variable) + ) + nodes_d = list( + str(node) + for node in d_final.dependencies + if not isinstance(d_final[node], Variable) + ) + + # one_output + assert a_final.one_output(nodes_a)[1] == True + assert d_final.one_output(nodes_d[1:])[1] == False + + # consecutive + assert a_final.consecutive(nodes_a) == True + assert d_final.consecutive(nodes_d) == True + assert d_final.consecutive(nodes_d[1:]) == False + + # mergeable + assert a_final.mergeable(nodes_a)[1] == True + assert d_final.mergeable(nodes_d[1:])[1] == False + + # merge + last_node = d_final.one_output(nodes_d[1:4])[0] + with raises(ValueError): + d_final.merge(list(str(dep) for dep in d_final.dependencies)) + with raises(ValueError): + d_final.merge(nodes_d[1:]) + merged_graph = d_final.merge(nodes_d[1:4]) + assert isinstance(merged_graph, HighLevel) + assert len(nodes_d) - 2 == len( + list( + str(node) + for node in merged_graph.dependencies + if not isinstance(merged_graph[node], Variable) + ) + ) # tests remove + nodes = list( + d_final.get_node(node) + for node in d_final.dependencies + if not isinstance(d_final[node], Variable) + )[1:4] + assert set( + [dep for node in nodes for dep in node.first_dependencies if dep not in nodes] + ) == set(list(merged_graph.get_node(last_node).first_dependencies)) From 323acf46ac1e70bcf01944b02e928db0bc18eb0b Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 9 Mar 2021 12:32:59 +0200 Subject: [PATCH 029/122] tests commented out after changes in code --- test/test_tunable.py | 8 +++++--- test/test_variable.py | 3 --- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/test_tunable.py b/test/test_tunable.py index 033d23d..3f09661 100644 --- a/test/test_tunable.py +++ b/test/test_tunable.py @@ -27,7 +27,9 @@ def test_object(): assert a.key != Object(unpickable()).key one = Object(1, deps=zero) - assert zero.key in one.dependencies + + +# assert zero.key in one.dependencies def test_function(): @@ -43,8 +45,8 @@ def test_function(): one = Object(1) fnc = Function(str, args=zero, kwargs={"one": one}) - assert zero.key in fnc.dependencies - assert one.key in fnc.dependencies + # assert zero.key in fnc.dependencies + # assert one.key in fnc.dependencies lst = Object([1, 2]).tunable() lst = lst.append(3) diff --git a/test/test_variable.py b/test/test_variable.py index 685722a..f57fb41 100644 --- a/test/test_variable.py +++ b/test/test_variable.py @@ -45,9 +45,6 @@ def test_variable(): with raises(RuntimeError): d.value = 3 - d = Variable(range(10)) - d.value = Variable(range(10)) - d = Permutation((1, 2, 3)) d.value = (3, 2, 1) assert d.size == 6 From 707ecf1bab16d65d79060af403351257faba6634 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 9 Mar 2021 12:35:13 +0200 Subject: [PATCH 030/122] var_name changed for variables created using alternatives --- tuneit/class_utils.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tuneit/class_utils.py b/tuneit/class_utils.py index d3abf46..1762277 100644 --- a/tuneit/class_utils.py +++ b/tuneit/class_utils.py @@ -225,6 +225,8 @@ def args_to_kwargs(cls, *args): def __init__(self, *args, **kwargs): super().__init__(**self.args_to_kwargs(*args), **kwargs) self.default = next(iter(self)) + self.var_name = None + self._closed = False @wraps(dict.update) def update(self, *args, **kwargs): @@ -249,9 +251,20 @@ def add(self, fnc): self.update(kwargs) return next(iter(kwargs)) + @property + def var_name(self): + if self._var_name is None: + return "which_" + self.__name__ + return self._var_name + + @var_name.setter + def var_name(self, key): + self._var_name = key + def __call__(self, *args, _key=None, **kwargs): - if len(args) == 1 and callable(args[0]): + if len(args) == 1 and callable(args[0]) and not self._closed: self.default = self.add(args[0]) + self._closed = True return self if _key: @@ -260,6 +273,6 @@ def __call__(self, *args, _key=None, **kwargs): return function( self, *args, - _key=variable(self.keys(), default=self.default, label=self.__name__), + _key=variable(self.keys(), default=self.default, label=self.var_name), **kwargs, ) From c683d0dde1b6d5be2132b774d1e175aeae219aa2 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 9 Mar 2021 12:36:47 +0200 Subject: [PATCH 031/122] dependencies function was changed --- tuneit/graph.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tuneit/graph.py b/tuneit/graph.py index 5e10232..b1e9946 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -196,7 +196,7 @@ def copy(self): def first_dependencies(self): "Iterates over the dependencies" for val in self: - if isinstance(val, Key): + if type(val) == Key: yield Key(val) direct_dependencies = first_dependencies @@ -207,13 +207,19 @@ def dependencies(self): deps = [self.key] yield deps[0] for val in self: - if isinstance(val, Key): + if not type(val) == Key: + continue + if str(val) in deps: + continue + if val in self.graph: val = self.graph[val] - if isinstance(val, Node): - for dep in Node(val).dependencies: - if dep not in deps: - deps.append(dep) - yield dep + else: + continue + + for dep in Node(val).dependencies: + if dep not in deps: + deps.append(dep) + yield dep def visualize(self, **kwargs): """ @@ -287,10 +293,6 @@ def visualize(graph, start=None, end=None, groups=None, **kwargs): with dot.subgraph(name=f"cluster_{i}") as c: c.attr(color="blue") c.node_attr.update(style="filled", color="white") - # for n in group: - # node = graph[n] - # deps = [str(dep) for dep in node.first_dependencies if isinstance(graph.get_item_(dep),Variable)] - # c.edges([(n, dep) for dep in deps]) for n in group: node = graph[n] c.node(n, node.label, **node.dot_attrs) From 859488a78c5e22ae21deb8faaac7fc6baf61d194 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:17:51 +0200 Subject: [PATCH 032/122] changed visualize function to enable different visualization of nodes to be precomputed --- tuneit/graph.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tuneit/graph.py b/tuneit/graph.py index b1e9946..2c0e24c 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -281,7 +281,10 @@ def visualize(graph, start=None, end=None, groups=None, **kwargs): if start and start not in node.dependencies: continue - dot.node(str(key), node.label, **node.dot_attrs) + if hasattr(node.value, "precompute") and node.value.precompute: + dot.node(str(key), node.label, style="filled", color="lightblue2") + else: + dot.node(str(key), node.label, **node.dot_attrs) for dep in node.first_dependencies: if start and start not in graph[dep].dependencies: From d7c7d9b0bdbeda789ed50b1f0dccb2f7e9dc082b Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:25:49 +0200 Subject: [PATCH 033/122] 1. updated get_attributes function to extract the information used for the name of the study from data given to the tunable object, 2. now supports the precomputation of indicated parts of the graph that are not going to be included in the tuning --- tuneit/tools/optuna.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py index 295bd78..57283be 100644 --- a/tuneit/tools/optuna.py +++ b/tuneit/tools/optuna.py @@ -3,6 +3,7 @@ from optuna import exceptions from hashlib import md5 from dill import dumps +from tuneit.finalize import HighLevel class OptunaSampler: @@ -36,7 +37,7 @@ def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs The objective function to be used for the tuning of parameters. """ - self.tunable = tunable.copy() + self.tunable = HighLevel(tunable).copy() self.compute_kwargs = kwargs self.callback = callback @@ -50,7 +51,11 @@ def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs def get_attributes(self): "Returns a dictionary of attributes that characterize the study" - return {"callback": self.callback} + attrs = {"callback": self.callback} + for data in self.tunable.datas: + info = self.tunable[data].get_info() + attrs[data] = info + return attrs def get_study(self): "Creates a new study or loads a pre-existing one if the name already exists" @@ -89,6 +94,7 @@ def _call_wrapper(self, graph, **kwargs): def objective(self, trial, **kwargs): "Computes and returns the objective function (callback) value for the next trial" tmp = self.get_next_trial(trial) + tmp.precompute(**kwargs) return self.callback(lambda: self._call_wrapper(tmp, **kwargs)) def get_next_trial(self, trial): From ad50e2bad80f72bf139b80d815c281247f811fbe Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:27:09 +0200 Subject: [PATCH 034/122] this file is not needed anymore --- tuneit/subgraph.py | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tuneit/subgraph.py diff --git a/tuneit/subgraph.py b/tuneit/subgraph.py deleted file mode 100644 index 147893b..0000000 --- a/tuneit/subgraph.py +++ /dev/null @@ -1,10 +0,0 @@ -class Subgraph: - def __init__(self, nodes, output=None, dependencies=None): - - self.nodes = nodes - self.output = output - self.dependencies = dependencies - - def __call__(self, *args): - "should call all the functions in the correct order and return the result" - pass From 4f4a6a52544dea77e8957145e8b22ecc6e00861e Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:29:20 +0200 Subject: [PATCH 035/122] added a copy function for the Tuner class --- tuneit/tools/tuner.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tuneit/tools/tuner.py b/tuneit/tools/tuner.py index e676188..d56d4c7 100644 --- a/tuneit/tools/tuner.py +++ b/tuneit/tools/tuner.py @@ -17,6 +17,10 @@ def __init__(self, tunable, **kwargs): super().__init__(tunable) self.tuner_kwargs = kwargs + def copy(self, **kwargs): + tmp = super().copy(**kwargs) + return Tuner(tmp, **self.tuner_kwargs) + def compute(self, **kwargs): "Calls a sampler to tune the whole graph" # graph_manager = self.divide_graph() From c4d1c1144ee5914669d463913a1cffb1e46a299a Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:30:20 +0200 Subject: [PATCH 036/122] updated README file with example --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/README.md b/README.md index aefd615..85cc7df 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,92 @@ The package can be installed via `pip`: ``` pip install [--user] tuneit ``` + +## A simple tuning example using sparse matrices + +We can import tuneit as shown below along with some other packages that are used in this example: + +``` +from tuneit import * +import scipy.sparse as sp +import numpy as np +``` + +Firstly, we construct a simple graph that containes one variable to be tuned and computes the multiplication of a sparse matrix with a vector: + +We write a function that creates the sparse matrix and using *alteratives* we can add more options for the format that will be used to express the matrix (available in the scipy.sparse package). + +``` +@alternatives( + coo = lambda matrix: sp.coo_matrix(matrix), + csc = lambda matrix: sp.csc_matrix(matrix), + csr = lambda matrix: sp.csr_matrix(matrix) +) +def create_matrix(matrix): + res = sp.bsr_matrix(matrix) + return res +``` + +In this way, we have created a function `create_matrix` that expresses the given sparse matrix in an appropriate format and a variable to be tuned (whose range contains all the different options of formats presented in the function for the creation of the matrix). The default name of the variable is `which_create_matrix` after the name of the function that creates it, but it can be easily changed as shown below: + +``` +create_matrix.var_name="foo" +``` + +Our graph takes as input the matrix and the vector to be multiplied. For the purpose of this example, instead of creating a matrix and vector at random we can create two generic data objects that can take their actual value later on. + +Instead of: +``` +mat=scipy.sparse.random(100,100,0.1) +vec=np.random.rand(100) +``` +we can just use: +``` +mat=data(info=["shape","dtype"]) +vec=data(info=["shape","dtype"]) +mat=create_matrix(mat) +``` + +`data()` creates a generic data object as no specific value is passed in the function. Even though no value is passed, some information about the new data object can be given using `info`. As shown above, some characteristics about the new objects are given by the attributes `shape` and `dtype`. +In addition, the `create_matrix` function constructed previously can now be used. The new `mat` object created after `create_matrix` is called on the data object `mat` is a tunable object. + +We can create the final graph `mul` that expresses the multiplication of the vector `vec` and sparse matrix `mat` as shown below: + +``` +mul=finalize(mat*vec) +``` + +We can now visualize the graph using: + +``` +visualize(mul) +``` +The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a fixed value yet are shown in red diamonds. + +For the purposes of this example, we would like to tune the variable `foo` based only on the computation time of the multiplication (i.e. excluding the time taken by the function `create_matrix` to contruct the matrix). In order to achieve this, we have to add a link between the multiplication and `foo` as they are not currently connected (we add `foo` as a dependency to the last node of the graph): + +``` +mul.add_deps('foo') +``` + +In addition, we mark the `create_matrix` node in the graph as one to be precomputed so that its computation time is not included in the overall time that will be used to tune the variable. + +``` +mul['create_matrix'].precompute=True +``` + +The only thing left to do is to actually tune the variable by calling the following functions: + +``` +obj = optimise(mul,sampler='optuna') +``` +A tuner object has been created by passing the graph to be tuned along with the sampler to be used to the `optimise()` function. The optuna package is utilised by tuneit as one of the options to be used. + +Now, we can simply call the tuner object while also passing actual values for the sparse matrix and the vector, because now will be the first time that those are necessary when the computations of the graph will be carried out. Each time the object is called the tuner executes one more trial that uses a different option for the variable and it returns the resulting computation time along with the best trial executed so far. + +For example: +``` +obj(mat=sp.random(100,100,0.1),vec=np.random.rand(100)) +``` + + From 35a59617a85cbefea69513a6ab2f4ca6580947f2 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:35:57 +0200 Subject: [PATCH 037/122] added a boolean precompute attribute to the class Object and now the dependencies property of Object raises a NotImplementedError --- tuneit/tunable.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tuneit/tunable.py b/tuneit/tunable.py index 2fa5a43..29ffddd 100644 --- a/tuneit/tunable.py +++ b/tuneit/tunable.py @@ -23,6 +23,7 @@ from typing import Any from varname import varname as _varname, VarnameRetrievingError from .graph import Graph, Node, Key +from lyncs_utils import isiterable def varname(caller=1, default=None): @@ -75,6 +76,7 @@ class Object: deps: Any = None label: str = None uid: Any = None + precompute: bool = False @classmethod def extract_deps(cls, deps): @@ -128,6 +130,7 @@ def tunable(self): @property def dependencies(self): "Returns the list of dependencies for the Object" + raise NotImplementedError return Node(self.tunable()).dependencies def copy(self, **kwargs): @@ -184,7 +187,7 @@ def __dot_attrs__(self): Object.__eq__ = lambda self, value: self.obj == value or self.__eq2__(value) -def data(label, value=None, **kwargs): +def data(value=None, label=None, **kwargs): """ A tunable function call. @@ -193,6 +196,7 @@ def data(label, value=None, **kwargs): label: str """ + label = label or varname() return Data(value, label=label, **kwargs).tunable() @@ -215,6 +219,15 @@ def set(self, val): raise ValueError("Check did not return True") self.obj = val + def get_info(self): + if self.info is None: + return None + if isiterable(self.info): + return {key: getattr(self.obj, key, None) for key in self.info} + if callable(self.info): + return self.info(self.obj) + raise TypeError("Unsupported type for info") + def function(fnc, *args, **kwargs): """ @@ -385,6 +398,9 @@ def __compute__(self, **kwargs): # pylint: disable=W0613 return Node(self).value + def __iter__(self): + raise TypeError("A tunable object is not iterable") + def default_operator(fnc): "Default operator wrapper" From 86b366c62e49a546858b36c8ca96ca4d84dd9a6d Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:44:06 +0200 Subject: [PATCH 038/122] updated tests to work with the new changes made to the code --- test/test_optuna.py | 4 ++-- test/test_sampler.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_optuna.py b/test/test_optuna.py index dde3f9d..b415d2e 100644 --- a/test/test_optuna.py +++ b/test/test_optuna.py @@ -84,8 +84,8 @@ def searching(array, element): # test get_next_trial temp = obj_A.get_next_trial(trial) assert len(temp.fixed_variables) == 2 - var_A = temp.get_variable("preprocessing") - var_B = temp.get_variable("searching") + var_A = temp.get_variable("which_preprocessing") + var_B = temp.get_variable("which_searching") assert var_A.fixed and var_B.fixed # test get_suggetions diff --git a/test/test_sampler.py b/test/test_sampler.py index 431d511..32fecef 100644 --- a/test/test_sampler.py +++ b/test/test_sampler.py @@ -37,7 +37,7 @@ def searching(array, element): obj = sample( result, - ["preprocessing", "searching"], + ["which_preprocessing", "which_searching"], callback=callback_function, callback_calls=True, ) From 615f9d8e65ac246aa02709dac5904361f1a65dd2 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 22 Mar 2021 15:49:56 +0200 Subject: [PATCH 039/122] 1. created get_key function and changed other functions (get_node(),__setitem__(),__getitem__(),get_variable(),get_data() ) to work based on get_key, 2. created add_deps() function that adds a new dependency to a node, 3. created precompute() function that allows for the precomputation of some nodes before tuning, 4. replace function was deleted, 5. merge function was changed to work without the need for a replace function, 6. the code of consecutive() and one_output() functions was optimized --- tuneit/finalize.py | 174 +++++++++++++++++++++++++++------------------ 1 file changed, 104 insertions(+), 70 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index dc694df..0266890 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -4,10 +4,11 @@ __all__ = ["finalize"] -from .graph import Node, Key +from .graph import Node, Key, Graph from .variable import Variable -from .tunable import Object, Function, compute, Data -from .subgraph import Subgraph +from .tunable import Object, Function, compute, Data, data + +# from .subgraph import Subgraph def finalize(tunable): @@ -70,18 +71,42 @@ def depends_on(self, value): def get_node(self, key): "Returns a node of the graph as a finalized graph" - if isinstance(key, Object): - key = key.key + key = self.get_key(key) return HighLevel(self.graph[key]) def __getitem__(self, key): - if isinstance(key, Object): - key = key.key + + key = self.get_key(key) return self.graph[key].value def __setitem__(self, key, value): + key = self.get_key(key) self.graph[key] = value + def get_key(self, key, ntype=None): + + if isinstance(key, Object): + key = key.key + if isinstance(key, Key): + key = Key(key).key + if key in self.graph: + return key + if not isinstance(key, str): + raise TypeError("key should be string") + + keys = self.dependencies + if ntype: + keys = (k for k in keys if isinstance(self[k], ntype)) + + # Smart search + matches = list(filter(lambda k: str(k).startswith(key + "-"), keys)) + if len(matches) > 1: + raise KeyError("More than one key matched to %s: %s" % (key, matches)) + if len(matches) == 0: + raise KeyError("%s is not a key of %s" % (key, self)) + + return matches[0] + def __copy__(self): return HighLevel(super().__copy__()) @@ -116,45 +141,36 @@ def copy(self, reset=False, reset_tunable=True): return res - def get_variable(self, variable): - "Returns the varible corresponding to var" - if isinstance(variable, Variable): - variable = variable.key - if isinstance(variable, Key): - variable = Key(variable).key - - if not variable in self.variables: - # Smart search - matches = list( - filter(lambda var: var.startswith(variable + "-"), self.variables) - ) - if len(matches) > 1: - raise KeyError( - "More than one variable matched to %s: %s" % (variable, matches) - ) - if len(matches) == 0: - raise KeyError("%s is not a variable of %s" % (variable, self)) - variable = matches[0] + def get_variable(self, key): + "Returns the variable corresponding to var" + key = self.get_key(key, ntype=Variable) + return self[key] - return self[variable] - - def get_data(self, data): + def get_data(self, key): "Returns the varible corresponding to var" - if isinstance(data, Data): - data = data.key - if isinstance(data, Key): - data = Key(data).key - - if not data in self.datas: - # Smart search - matches = list(filter(lambda var: var.startswith(data + "-"), self.datas)) - if len(matches) > 1: - raise KeyError("More than one data matched to %s: %s" % (data, matches)) - if len(matches) == 0: - raise KeyError("%s is not a data of %s" % (data, self)) - data = matches[0] - - return self[data] + key = self.get_key(key, ntype=Data) + return self[key] + + def add_deps( + self, start_node, end_node=None + ): # start_node, end_node must be a strings + if not end_node: + end_node = self + else: + end_node = self.get_node(end_node) + + start_node = self.get_variable( + start_node + ) # TO DO: should work for all types not variable only + + end_node.value.deps = end_node.value.deps + (Key(start_node.key),) + + def precompute(self, **kwargs): + for key in self.dependencies: + if self[key].precompute: + self[key] = Data( + self.get_node(key).compute(**kwargs), label=self[key].label + ) def fix(self, variable, value=None): "Fixes the value of the variable" @@ -170,46 +186,48 @@ def remove(self, nodes): for node in nodes: del self.graph[node] - def replace(self, node, new_node): - "Replaces the dependencies to node with new_node" - key = str(Key(node)) - new_key = str(Key(new_node)) - for node in self.graph: - for dep in self.get_node(node).first_dependencies: - if dep == key: - dep.key = str(new_key) - self.graph[new_key] = new_node - - def merge(self, nodes): + def merge(self, nodes=None): "Returns a new graph with the list of nodes merged into a single node" + # if not nodes: + # mergers = detect_mergers + # for nodes in mergers... all the rest for node in nodes: if not isinstance(self[node], Function): raise ValueError("The node does not represent a function") last_node, merge = self.mergeable(nodes) if not merge: raise ValueError("Group of nodes not mergeable") - nodes_values = [self[node] for node in nodes] - deps = set( - [ - str(dep) - for node in nodes - for dep in self.get_node(node).first_dependencies - ] + deps = tuple( + set( + [ + str(dep) + for node in nodes + for dep in self.get_node(node).first_dependencies + if dep not in nodes + ] + ) + ) + sub = {node: self[node] for node in nodes} + sub.update( + {dep: Data(None, label=f"dep{i}", info=dep) for (i, dep) in enumerate(deps)} + ) + sub = finalize(Graph(sub)[last_node]) + new_node = Function( + sub, + label="merged", + kwargs={f"dep{i}": Key(dep) for (i, dep) in enumerate(deps)}, ) - deps = tuple([Key(dep) for dep in list(deps) if dep not in nodes]) - sub = Subgraph(nodes_values, output=last_node, dependencies=deps) - new_node = Function(sub, args=sub.dependencies) new_graph = self.copy(reset=True) nodes.remove(last_node) new_graph.remove(nodes) - new_graph.replace(last_node, new_node) + new_graph[last_node] = new_node return new_graph def consecutive(self, nodes): "implements a DFS to check if the undirected graph is connected (if all nodes are consecutive)" stack = [nodes[0]] unvisited = nodes[1:] - while stack != []: + while stack: u = stack[-1] appended = False for w in [str(dep) for dep in self.get_node(u).first_dependencies]: @@ -218,7 +236,7 @@ def consecutive(self, nodes): stack.append(w) appended = True break - if appended == False: + if not appended: for w in [ str(node) for node in nodes @@ -229,9 +247,9 @@ def consecutive(self, nodes): stack.append(w) appended = True break - if appended == False: + if not appended: stack.pop() - if unvisited == []: + if not unvisited: return True return False @@ -242,6 +260,22 @@ def one_output(self, nodes): all_nodes = [ str(n) for n in self.dependencies if isinstance(self[str(n)], Function) ] + + # If the whole graph is given or if the subgraph contains the last node of the whole graph: + all_deps = [ + str(dep) + for node in all_nodes + for dep in self.get_node(str(node)).first_dependencies + ] + for node in all_nodes: + if node not in all_deps and node in nodes: + common_outside = common_outside + [node] + if len(common_outside) == 1: + return common_outside[0], True + elif len(common_outside) > 1: + return common_outside, False + + # If a subgraph is given: for node in all_nodes: deps = list( set( From fcfc27219841b0327dac73ce2d221a538f475dfe Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 25 Mar 2021 11:08:13 +0200 Subject: [PATCH 040/122] Adding workflows --- .github/workflows/black_n_pylint.yml | 63 ++++++++++++++++++++++++++++ .github/workflows/tests.yml | 46 ++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 .github/workflows/black_n_pylint.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/black_n_pylint.yml b/.github/workflows/black_n_pylint.yml new file mode 100644 index 0000000..e3a5ecb --- /dev/null +++ b/.github/workflows/black_n_pylint.yml @@ -0,0 +1,63 @@ +# This workflow runs pylint and updates badge + +name: linting + +on: + pull_request: + branches: + - "*" + +jobs: + black-n-pylint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Install MPI openmpi + run: bash .bash/install-openmpi.sh + + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: "3.x" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install black lyncs_setuptools[pylint] + python setup.py egg_info + sudo pip install `grep -v '^\[' *.egg-info/requires.txt` + + - name: Applying black formatting + run: | + black --diff . + black . + + - name: Pushing changes if any + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Applying black formatting (from Github Action) + commit_user_name: sbacchio + commit_user_email: s.bacchio@gmail.com + commit_author: Simone Bacchio + + - name: Pylint output + run: | + badge=$(lyncs_pylint_badge . | sed "s/\&/\\\&/g") + badge_line=$(awk '/!\[pylint\]/ {print FNR}' README.md) + sed -i "${badge_line}s#.*#${badge}#" README.md + + - name: Pushing changes if any + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Updating pylint score (from Github Action) + commit_user_name: sbacchio + commit_user_email: s.bacchio@gmail.com + commit_author: Simone Bacchio + + - name: Run lyncs_setuptools + run: | + lyncs_setuptools diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..66ccb38 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,46 @@ +# This workflow builds and runs test + +name: tests + +on: + pull_request: + branches: + - "*" + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + py-version: + - 3.6 + - 3.7 + - 3.8 + os: + - ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Install MPI (${{ matrix.mpi }}) + run: bash .bash/install-${{ matrix.mpi }}.sh + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + + - name: Install via pip + run: | + sudo pip install -e .[all] + + - name: Run serial tests + run: | + pytest -v From 466a02bb88367bfe505061ad4d231dbe66f36ded Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 25 Mar 2021 11:09:49 +0200 Subject: [PATCH 041/122] Removing unwated MPI --- .github/workflows/black_n_pylint.yml | 3 --- .github/workflows/tests.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/black_n_pylint.yml b/.github/workflows/black_n_pylint.yml index e3a5ecb..2d84aa1 100644 --- a/.github/workflows/black_n_pylint.yml +++ b/.github/workflows/black_n_pylint.yml @@ -16,9 +16,6 @@ jobs: with: ref: ${{ github.head_ref }} - - name: Install MPI openmpi - run: bash .bash/install-openmpi.sh - - name: Set up Python uses: actions/setup-python@v1 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 66ccb38..d641d6c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,9 +25,6 @@ jobs: with: ref: ${{ github.head_ref }} - - name: Install MPI (${{ matrix.mpi }}) - run: bash .bash/install-${{ matrix.mpi }}.sh - - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: From 454b18bcc355e5ceac15e6e17e52b08c395a54b8 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 25 Mar 2021 09:11:14 +0000 Subject: [PATCH 042/122] Updating pylint score (from Github Action) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85cc7df..c06c634 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![license](https://img.shields.io/github/license/Lyncs-API/tuneit?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/blob/master/LICENSE) [![build & test](https://img.shields.io/github/workflow/status/Lyncs-API/tuneit/build%20&%20test?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/actions) [![codecov](https://img.shields.io/codecov/c/github/Lyncs-API/tuneit?logo=codecov&logoColor=white)](https://codecov.io/gh/Lyncs-API/tuneit) -[![pylint](https://img.shields.io/badge/pylint%20score-8.4%2F10-yellowgreen?logo=python&logoColor=white)](http://pylint.pycqa.org/) +[![pylint](https://img.shields.io/badge/pylint%20score-8.8%2F10-yellowgreen?logo=python&logoColor=white)](http://pylint.pycqa.org/) [![black](https://img.shields.io/badge/code%20style-black-000000.svg?logo=codefactor&logoColor=white)](https://github.com/ambv/black) Tuneit is a generic purpose tool for optimizing and crosschecking calculations. From 1bef44df6bd4d910ba8e5be13912f1115d2e0949 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 26 Mar 2021 11:24:34 +0200 Subject: [PATCH 043/122] added tests for the example in README --- test/test_tuner.py | 60 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/test/test_tuner.py b/test/test_tuner.py index a972d78..0169ba8 100644 --- a/test/test_tuner.py +++ b/test/test_tuner.py @@ -5,6 +5,9 @@ from tuneit.tools.optuna import OptunaSampler from tuneit.tools.base import Sampler import pytest +import scipy.sparse as sp +from tuneit.tunable import Tunable +from tuneit.finalize import HighLevel def test_tuner(): @@ -55,14 +58,15 @@ def searching(array, element): # test get_best_trial function in Tuner res = obj_C.get_best_trial() assert isinstance(res, dict) - assert "preprocessing" in {k.split("-")[0]: v for k, v in res.items()} + assert "which_preprocessing" in {k.split("-")[0]: v for k, v in res.items()} assert ( - next(v for k, v in res.items() if k.startswith("preprocessing")) + next(v for k, v in res.items() if k.startswith("which_preprocessing")) in preprocessing.keys() ) - assert "searching" in {k.split("-")[0]: v for k, v in res.items()} + assert "which_searching" in {k.split("-")[0]: v for k, v in res.items()} assert ( - next(v for k, v in res.items() if k.startswith("searching")) in searching.keys() + next(v for k, v in res.items() if k.startswith("which_searching")) + in searching.keys() ) # test get_sampler function in Tuner assert obj_C.get_sampler() == OptunaSampler @@ -84,3 +88,51 @@ def searching(array, element): result, sampler=None, callback=lambda fnc: Time(default_timer(fnc)) ).get_sampler_kwargs() assert not bool(kwargs) + + # Example from the README file: + + @alternatives( + coo=lambda matrix: sp.coo_matrix(matrix), + csc=lambda matrix: sp.csc_matrix(matrix), + csr=lambda matrix: sp.csr_matrix(matrix), + ) + def create_matrix(matrix): + res = sp.bsr_matrix(matrix) + return res + + create_matrix.var_name = "foo" + mat = data(info=["shape", "dtype"]) + vec = data(info=["shape", "dtype"]) + assert isinstance(mat, Tunable) + assert isinstance(vec, Tunable) + mat = create_matrix(mat) + assert isinstance(mat, Tunable) + mul = finalize(mat * vec) + assert isinstance(mul, HighLevel) + assert len(list(dep for dep in mul.dependencies if str(dep).startswith("foo"))) == 1 + assert ( + len( + list( + dep + for dep in mul.dependencies + if str(dep).startswith("which_create_matrix") + ) + ) + == 0 + ) + + mul.add_deps("foo") + assert ( + len(list(dep for dep in mul.first_dependencies if str(dep).startswith("foo"))) + == 1 + ) + mul["create_matrix"].precompute = True + assert mul["create_matrix"].precompute == True + obj = optimise(mul, sampler="optuna") + assert isinstance(obj, Tuner) + matrix = sp.random(100, 100, 0.1) + vector = numpy.random.rand(100) + res = obj(mat=matrix, vec=vector) + assert isinstance(res, numpy.ndarray) + assert res.shape == (100,) + numpy.testing.assert_array_almost_equal(res, matrix * vector, decimal=8) From af6c10e757c4789a26d6799b9107198986f8b4df Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 2 Apr 2021 15:01:21 +0300 Subject: [PATCH 044/122] 1.added functionality that records the results in a panda dataframe, 2.added a call function --- tuneit/tools/base.py | 49 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index 7153fdb..c688501 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -14,6 +14,7 @@ from tabulate import tabulate from ..finalize import finalize from lyncs_utils import isiterable +import pandas as pd class Sampler: @@ -28,6 +29,7 @@ def __init__( callback_calls=False, label=None, store_value=False, + record=False, **kwargs, ): "Initializes the tunable object and the variables" @@ -69,7 +71,30 @@ def __init__( self.n_samples = n_samples self.store_value = store_value - + + self._trials=None + self.record=record + + @property + def record(self): + return self.trials is not None + + @record.setter + def record(self,value): + if value==self.record: + return + if value is False: + self._trials=None + elif value is True: + self._trials = pd.DataFrame(columns= ["trial_id"]+list(self.headers)[:-1]+list(self.tunable.get_info().keys())+["time"]) + else: + raise TypeError("Value is neither true or false") + + @property + def trials(self): + return self._trials + + @property def max_samples(self): "Size of the parameter space (product of variables' size)" @@ -133,13 +158,35 @@ def __iter__(self): result = self.callback(self._perform_call(tmp)) except Exception as err: result = err + + if self.record: + index = len(self.trials) + self.trials.loc[index] = [index, *list(params),*list(self.tunable.get_info().values()), result] yield params, result + + def run(self): + return tuple(self) def _perform_call(self, graph): value = graph.compute(**self.compute_kwargs) if self.store_value: self._value = value return value + + def __call__(self, *args, **kwargs): + "kwargs are data input of the graph" + if args: + raise ValueError("args not supported, please pass them as kwargs") + + for key, val in kwargs.items(): + try: + self.tunable.get_data(key).set(val) + continue + except KeyError: + pass + self.tunable.fix(key, val) + + return self @property def value(self): From 1e3be9f5ce0106654b709473d0c13ce2ef4901b3 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 2 Apr 2021 15:01:57 +0300 Subject: [PATCH 045/122] reformatted --- tuneit/tools/base.py | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index c688501..da93332 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -71,30 +71,34 @@ def __init__( self.n_samples = n_samples self.store_value = store_value - - self._trials=None - self.record=record - + + self._trials = None + self.record = record + @property def record(self): return self.trials is not None - + @record.setter - def record(self,value): - if value==self.record: + def record(self, value): + if value == self.record: return if value is False: - self._trials=None + self._trials = None elif value is True: - self._trials = pd.DataFrame(columns= ["trial_id"]+list(self.headers)[:-1]+list(self.tunable.get_info().keys())+["time"]) + self._trials = pd.DataFrame( + columns=["trial_id"] + + list(self.headers)[:-1] + + list(self.tunable.get_info().keys()) + + ["time"] + ) else: raise TypeError("Value is neither true or false") - + @property def trials(self): return self._trials - - + @property def max_samples(self): "Size of the parameter space (product of variables' size)" @@ -158,12 +162,17 @@ def __iter__(self): result = self.callback(self._perform_call(tmp)) except Exception as err: result = err - + if self.record: index = len(self.trials) - self.trials.loc[index] = [index, *list(params),*list(self.tunable.get_info().values()), result] + self.trials.loc[index] = [ + index, + *list(params), + *list(self.tunable.get_info().values()), + result, + ] yield params, result - + def run(self): return tuple(self) @@ -172,7 +181,7 @@ def _perform_call(self, graph): if self.store_value: self._value = value return value - + def __call__(self, *args, **kwargs): "kwargs are data input of the graph" if args: @@ -185,7 +194,7 @@ def __call__(self, *args, **kwargs): except KeyError: pass self.tunable.fix(key, val) - + return self @property From d20b0ee7684c10f6ba8614edc5c8d7d4411fb143 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 2 Apr 2021 15:03:57 +0300 Subject: [PATCH 046/122] added record argument --- tuneit/tools/time.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tuneit/tools/time.py b/tuneit/tools/time.py index 4881a1c..a5b5f79 100644 --- a/tuneit/tools/time.py +++ b/tuneit/tools/time.py @@ -39,6 +39,7 @@ def benchmark( timer_kwargs=None, samples=None, label="Time", + record=False, **kwargs, ): """ @@ -58,6 +59,7 @@ def benchmark( timer_kwargs: dict Arguments passed to the timer. For default timer: - number: (int) number of iterations + kwargs: dict Variables passed to the compute function. See help(tunable.compute) """ @@ -69,6 +71,7 @@ def benchmark( callback_calls=True, samples=samples, label=label, + record=record, **kwargs, ) From 056c203730ab1e7dd3326b4cef09a2d254701e5a Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 2 Apr 2021 15:06:08 +0300 Subject: [PATCH 047/122] changed dot_attrs --- tuneit/variable.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tuneit/variable.py b/tuneit/variable.py index f89ee11..fda9d1e 100644 --- a/tuneit/variable.py +++ b/tuneit/variable.py @@ -146,7 +146,13 @@ def __compute__(self, **kwargs): @property def __dot_attrs__(self): - return dict(shape="diamond", color="green" if self.fixed else "red") + attrs = super().__dot_attrs__ + attrs["shape"] = "diamond" + if self.fixed: + attrs["color"] = "green" + else: + attrs["color"] = "red" + return attrs def copy(self, reset=False, reset_value=False, **kwargs): "Returns a copy of self" From a9a8fa43f0d57567b7ce3b6ef053af38e52f1cb4 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 2 Apr 2021 15:06:49 +0300 Subject: [PATCH 048/122] changed dot_attrs --- tuneit/tunable.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tuneit/tunable.py b/tuneit/tunable.py index 29ffddd..0b577fc 100644 --- a/tuneit/tunable.py +++ b/tuneit/tunable.py @@ -180,7 +180,11 @@ def __label__(self): @property def __dot_attrs__(self): - return dict(shape="rect") + attrs = dict(shape="rect") + if self.precompute: + attrs["style"] = "filled" + attrs["color"] = "lightblue2" + return attrs Object.__eq2__ = Object.__eq__ @@ -346,7 +350,9 @@ def __label__(self): @property def __dot_attrs__(self): - return dict(shape="oval") + attrs = super().__dot_attrs__ + attrs["shape"] = "oval" + return attrs def callattr(self, key, *args, **kwargs): From c266f40bf4ab024c4ff5e8ca8b72d87045c472c8 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Sat, 3 Apr 2021 02:10:59 +0300 Subject: [PATCH 049/122] changed crosscheck method to allow a delayed execution --- tuneit/tools/check.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tuneit/tools/check.py b/tuneit/tools/check.py index fc63d4a..cfaee44 100644 --- a/tuneit/tools/check.py +++ b/tuneit/tools/check.py @@ -39,12 +39,19 @@ def crosscheck( Variables passed to the compute function. See help(tunable.compute) """ if reference is None: - reference = finalize(tunable).copy().compute(**kwargs) + reference = [] + else: + reference = [reference] + + def get_ref(res): + if len(reference) == 0: + reference.append(res) + return reference[0] return sample( tunable, variables=variables, - callback=lambda res: comparison(reference, res), + callback=lambda res: comparison(get_ref(res), res), samples=samples, label=label, **kwargs From b98f3969949795e0d99781cee84997807d3cf130 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:07:11 +0300 Subject: [PATCH 050/122] README: removed example that now exists in docs, conf: added an instruction for the sidebar --- README.md | 87 ---------------------------------------------------- docs/conf.py | 1 + 2 files changed, 1 insertion(+), 87 deletions(-) diff --git a/README.md b/README.md index c06c634..583ca64 100644 --- a/README.md +++ b/README.md @@ -20,91 +20,4 @@ The package can be installed via `pip`: pip install [--user] tuneit ``` -## A simple tuning example using sparse matrices - -We can import tuneit as shown below along with some other packages that are used in this example: - -``` -from tuneit import * -import scipy.sparse as sp -import numpy as np -``` - -Firstly, we construct a simple graph that containes one variable to be tuned and computes the multiplication of a sparse matrix with a vector: - -We write a function that creates the sparse matrix and using *alteratives* we can add more options for the format that will be used to express the matrix (available in the scipy.sparse package). - -``` -@alternatives( - coo = lambda matrix: sp.coo_matrix(matrix), - csc = lambda matrix: sp.csc_matrix(matrix), - csr = lambda matrix: sp.csr_matrix(matrix) -) -def create_matrix(matrix): - res = sp.bsr_matrix(matrix) - return res -``` - -In this way, we have created a function `create_matrix` that expresses the given sparse matrix in an appropriate format and a variable to be tuned (whose range contains all the different options of formats presented in the function for the creation of the matrix). The default name of the variable is `which_create_matrix` after the name of the function that creates it, but it can be easily changed as shown below: - -``` -create_matrix.var_name="foo" -``` - -Our graph takes as input the matrix and the vector to be multiplied. For the purpose of this example, instead of creating a matrix and vector at random we can create two generic data objects that can take their actual value later on. - -Instead of: -``` -mat=scipy.sparse.random(100,100,0.1) -vec=np.random.rand(100) -``` -we can just use: -``` -mat=data(info=["shape","dtype"]) -vec=data(info=["shape","dtype"]) -mat=create_matrix(mat) -``` - -`data()` creates a generic data object as no specific value is passed in the function. Even though no value is passed, some information about the new data object can be given using `info`. As shown above, some characteristics about the new objects are given by the attributes `shape` and `dtype`. -In addition, the `create_matrix` function constructed previously can now be used. The new `mat` object created after `create_matrix` is called on the data object `mat` is a tunable object. - -We can create the final graph `mul` that expresses the multiplication of the vector `vec` and sparse matrix `mat` as shown below: - -``` -mul=finalize(mat*vec) -``` - -We can now visualize the graph using: - -``` -visualize(mul) -``` -The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a fixed value yet are shown in red diamonds. - -For the purposes of this example, we would like to tune the variable `foo` based only on the computation time of the multiplication (i.e. excluding the time taken by the function `create_matrix` to contruct the matrix). In order to achieve this, we have to add a link between the multiplication and `foo` as they are not currently connected (we add `foo` as a dependency to the last node of the graph): - -``` -mul.add_deps('foo') -``` - -In addition, we mark the `create_matrix` node in the graph as one to be precomputed so that its computation time is not included in the overall time that will be used to tune the variable. - -``` -mul['create_matrix'].precompute=True -``` - -The only thing left to do is to actually tune the variable by calling the following functions: - -``` -obj = optimise(mul,sampler='optuna') -``` -A tuner object has been created by passing the graph to be tuned along with the sampler to be used to the `optimise()` function. The optuna package is utilised by tuneit as one of the options to be used. - -Now, we can simply call the tuner object while also passing actual values for the sparse matrix and the vector, because now will be the first time that those are necessary when the computations of the graph will be carried out. Each time the object is called the tuner executes one more trial that uses a different option for the variable and it returns the resulting computation time along with the best trial executed so far. - -For example: -``` -obj(mat=sp.random(100,100,0.1),vec=np.random.rand(100)) -``` - diff --git a/docs/conf.py b/docs/conf.py index 6e2f2f4..81b7341 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,6 +47,7 @@ # a list of builtin themes. # html_theme = "alabaster" +html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From a1de8020eed07c176806abad3df7cb05882bdeb2 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:08:13 +0300 Subject: [PATCH 051/122] reformatted --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 81b7341..f2c7dba 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,9 @@ # a list of builtin themes. # html_theme = "alabaster" -html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } +html_sidebars = { + "**": ["globaltoc.html", "relations.html", "sourcelink.html", "searchbox.html"] +} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From d5c41e38108b5d8cd928053df038afbbf0e9b7a5 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:10:50 +0300 Subject: [PATCH 052/122] new documentation pages about installation and some examples along with the images that they use --- docs/example.rst | 265 ++++++++++++++++++++++++++++++ docs/images/benchmark.png | Bin 0 -> 2927 bytes docs/images/crosscheck.png | Bin 0 -> 2229 bytes docs/images/df.PNG | Bin 0 -> 17719 bytes docs/images/plot.png | Bin 0 -> 16626 bytes docs/images/visualised_graph1.PNG | Bin 0 -> 14697 bytes docs/images/visualised_graph2.PNG | Bin 0 -> 14290 bytes docs/installation.rst | 11 ++ 8 files changed, 276 insertions(+) create mode 100644 docs/example.rst create mode 100644 docs/images/benchmark.png create mode 100644 docs/images/crosscheck.png create mode 100644 docs/images/df.PNG create mode 100644 docs/images/plot.png create mode 100644 docs/images/visualised_graph1.PNG create mode 100644 docs/images/visualised_graph2.PNG create mode 100644 docs/installation.rst diff --git a/docs/example.rst b/docs/example.rst new file mode 100644 index 0000000..35a0d68 --- /dev/null +++ b/docs/example.rst @@ -0,0 +1,265 @@ +Examples +======== + +Example 1: A simple tuning example using sparse matrices +-------------------------------------------------------- + +:code:`tuneit` is imported as shown below along with some other packages that are used in this example: + +.. code-block:: python + + from tuneit import * + import scipy.sparse as sp + import numpy as np + +Firstly, a simple graph is constructed, which computes the multiplication of a sparse matrix with a vector. +The graph contains one variable to be tuned, which represents the different formats that can be used for the sparse matrix. +The following function creates the sparse matrix and by using :code:`alteratives` more options are added for the format that will +be used to express the matrix (available in the :code:`scipy.sparse` package). + +.. code-block:: python + + @alternatives( + coo = lambda matrix: sp.coo_matrix(matrix), + csc = lambda matrix: sp.csc_matrix(matrix), + csr = lambda matrix: sp.csr_matrix(matrix) + ) + def create_matrix(matrix): + res = sp.bsr_matrix(matrix) + return res + +In this way, we have created a function :code:`create_matrix` that expresses the given sparse matrix in an appropriate format and a variable +to be tuned. The range of the variable contains all different options that can be used to express the sparse matrix, which are included in the +function above (:code:`coo,csc,csr,bsr`). The default name of the variable is :code:`which_create_matrix` after the name of the function that +creates it, but it can be easily changed as shown below: + +.. code-block:: python + + create_matrix.var_name="foo" + +The graph takes as input the matrix and the vector to be multiplied. One option is to create a matrix and a vector at random: + +.. code-block:: python + + mat=scipy.sparse.random(100,100,0.1) + vec=np.random.rand(100) + +Or just create two generic data objects, which will take their actual value later on: (this is the option used in this example) + +.. code-block:: python + + mat=data(info=["shape","dtype"]) + vec=data(info=["shape","dtype"]) + +:code:`data()` creates a generic data object as no specific value is passed in the function. Even though no value is passed, some information +about the new data object can be given using :code:`info`. As shown above, some characteristics about the new objects are given by the +attributes :code:`shape` and :code:`dtype`. + +In addition, the :code:`create_matrix` function constructed previously can now be used. The new :code:`mat` object created after +:code:`create_matrix` is called on the object :code:`mat` (created above) is a tunable object. + +.. code-block:: python + + mat=create_matrix(mat) + +The final graph :code:`mul` that expresses the multiplication between the vector :code:`vec` and the sparse matrix :code:`mat` is created +as shown below: + +.. code-block:: python + + mul=finalize(mat*vec) + +.. + Do I need more explanations about the finalize function here and why it is needed? + +The graph can now be visualized using: + +.. code-block:: python + + visualize(mul) + +The result is shown below: + +.. image:: images/visualised_graph1.png + :width: 400 + +The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a +fixed value yet are shown in red diamonds. + +Note: Each node in the graph is represented by its name (such as :code:`create_matrix`) concatenated with a random sequence of characters, which +is not shown in its visualisation (for instance :code:`create_matrix-ad93fc5283b9d3a0963c3e65eb5055ff`). +The small indices included in the nodes of the visualised graph allow the user to distinguish between multiple operations of the same kind +(e.g. multiplications) and to find out the whole unique name of a node in case it is needed in an operation: + +For instance the following code should return the whole name of the node that contains the index 2 in the visualization of the graph :code:`mul`: + +.. code-block:: python + + mul.graph[2] + +For the purposes of this example, we would like to tune the variable :code:`foo` based only on the computation time of the multiplication +(i.e. excluding the time taken by the function :code:`create_matrix` to construct the matrix). In order to achieve this, a link has to be added +between the multiplication and :code:`foo`, as they are not currently directly connected (:code:`foo` is added as a dependency to the last node +of the graph): + +.. code-block:: python + + mul.add_deps('foo') + +The new link can be observed by running the code: + +.. code-block:: python + + visualize(mul) + +.. image:: images/visualised_graph2.png + :width: 400 + +In addition, the :code:`create_matrix` node in the graph needs to be marked as one to be precomputed so that its computation time is not +taken into account when the execution of the graph is timed during the tuning of the variable. +Note: In the following operation we can use the name :code:`create_matrix` for the node only because it is unique in the graph. If there were +multiple operations of the same kind (e.g. the function :code:`create_matrix` is used twice in the graph), then the full name of the node would +have to be used. + +.. code-block:: python + + mul['create_matrix'].precompute=True + +.. + should I include a visualisation of the node marked as precomputed? + +The only thing left to do is to actually tune the variable by calling the following functions: + +.. code-block:: python + + obj = optimise(mul,sampler='optuna') + +A tuner object has been created by passing the graph to be tuned along with the sampler to be used to the :code:`optimise()` function. +The :code:`optuna` package is one of the options that are offered by :code:`tuneit` to be used as a sampler. + +.. + maybe include a reference for the optuna package? + +Now, the tuner object can simply be called, while also passing actual values for the sparse matrix and the vector. This is necessary, because +during the tuning of the variable the computation of the graph will be carried out for the first time. Each time the tuner object is +called, the tuner executes one more trial and it returns the value that was used for the variable in that trial and the resulting computation +time along with the best trial executed so far. +Note: A trial is a single execution of the objective function (which in this case is the timing of an execution) using a different combination +of values for the variables that are tuned. + +For example: + +.. code-block:: python + + obj(mat=sp.random(100,100,0.1),vec=np.random.rand(100)) + +.. + do I need to include a picture of the result here? (what the tuner returns after it is called a few times) + +Example 2: Crosscheck and Benchmark +----------------------------------- + +Example 2 starts by reusing some code from Example 1 (explanations about the following lines of code are given above). + +.. code-block:: python + + from tuneit import * + import scipy.sparse as sp + import numpy as np + + @alternatives( + coo = lambda matrix: sp.coo_matrix(matrix), + csc = lambda matrix: sp.csc_matrix(matrix), + csr = lambda matrix: sp.csr_matrix(matrix) + ) + def create_matrix(matrix): + res = sp.bsr_matrix(matrix) + return res + + create_matrix.var_name="foo" + mat=data(info=["shape","dtype"]) + vec=data(info=["shape","dtype"]) + mat=create_matrix(mat) + graph = finalize(mat*vec) + + +In addition, we define a random sparse matrix and a random vector that will be used later on when actual values are needed to be passed for the +:code:`mat,vec` objects created above. + +.. code-block:: python + + matrix = sp.random(100,100,0.1) + vector = np.random.rand(100) + +Crosscheck +~~~~~~~~~~ + +The function :code:`crosscheck` can be called on the finalised object :code:`graph` as shown below. The function returns a callable sampler +object. + +.. code-block:: python + + obj = crosscheck(graph) + +If it is then called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object +will iterate through all the possible alternative options for the variable of the graph (:code:`foo`) and return :code:`True` only for the ones +that produce the correct result of the graph. The :code:`crosscheck` function is basically a way to check that all alternatives options return +the correct result. + +.. code-block:: python + + obj(mat=matrix,vec=vector) + +The result of the above operation is: + +.. image:: images/crosscheck.png + + +Benchmark +~~~~~~~~~ + +The function :code:`benchmark` can be called on the finalised object :code:`graph` as shown below. The function returns a callable sampler +object. + +.. code-block:: python + + obj = benchmark(graph) + +If it is then called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object +will iterate through all the possible alternative options for the variable of the graph (:code:`foo`) and time the execution of graph using each +option. The :code:`benchmark` function is basically a way to compare the execution times of all alternatives options of the variable. + +.. code-block:: python + + obj(mat=matrix,vec=vector) + +The result of the above operation is: + +.. image:: images/benchmark.png + +The :code:`bechmark` function has also an argument called :code:`record`, which if it set to :code:`True` allows the execution times of the graph +using alternative options for the variable to be stored in a :code:`panda` dataframe. In addition, now there is the option of also comparing +the execution times that result not only by the various alternatives for the variable, but also different inputs. For example, in the code below +different sizes of inputs are passed in each execution of the sampler. As a result, the returned dataframe :code:`trials` will contain the execution +time of the graph for all combinations of alternative options of the variable and different sizes of inputs. + +.. code-block:: python + + obj=benchmark(graph, record=True) + for n in [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 literal 0 HcmV?d00001 diff --git a/docs/images/df.PNG b/docs/images/df.PNG new file mode 100644 index 0000000000000000000000000000000000000000..f42f66c27e32023d9a0fd6b4dc60f0999bdf627f GIT binary patch literal 17719 zcmc(HXIPWnny$Se;0FpwM-fDN??JKAQIOtyCx*}iL`6iTO7DnLl_DL4Bp)r2F1-^W zK%|BcAdrx7g5TaVd(P}L*X(oVy3P;sCLu3bd9&7Ym*-wYJ<)qie~JCli4!O2H8ma> zoH%hxE=I6v{@0q*K0w;~Htv=@WD1XaUsqu<8VM3!|6NZK;o}#_Oi||tyX$RiBFVD~p zS3cZ5e)7T--jlS$ncE4p+Y_g*(gsZ%=p}yI?TZsP z5vYNNG)&JlcFQAbx|U2n@DB+JQkIJ(lpRu3;IPXe=A0P3UVvtV~oiA`JNAQ*-wAp+T%ZFb> zT7Glht2nQHv9m)lEdmd3m45Sk3mUlCr~-AglVxej8*W4c5X0Zr>Ly$4vfaEVZ!S?~ z9|j0BEWKb40RS5AeU9_^hMNOp2$0Wen}v4|cR zM-Tmc@2g*odoXi0{d zrMg$&VQkNg%UZZ6DeAE6k%_X#$K)AacS$#7o%0ZLcH%t9h#zcgY=amb8IxO*6SKDi zd*Hs{Du+wfw=s0qzuL;p4*c7r=OJR}7^7!j>{-nbL(_0Ycu3xg<>!syiRzhCZ{aTS;-0eYR2onM~-YAt?Eorm_vxo`OIw5WI%~9fs@?{UqJ8~yU5zsRpYIuRb z7&^YEiGQ1g^K40zCL6ysQs#@y-ugOmQ7CN6=9ls^Cu~eIGr+qol_GD8ZopWO;_3|zgYW(*PVNzz3v{tnf0htk&QTqPeChwiRk^99&Z=bOECmYS6szA_41Hzhab0*rlx z2r-br#yE7C={l2AMLrHFPd)6LY`BAgodsE!`++2_!jE3h5G8ImAnDKgRmc(^U127@>p~gitq`CI)@T=M2@QsM8X$g0WANqi@P|xS6xt8B9 z_Iy2rG;o7AfeFE(T|{k+xmInB%lMUL9#Xx7xz<|(-dc~8+I5i+Kqa3($XY?LN&-Sm z*QJ#DX_w@?b`3sm2At>F2aVeHik+AFuouuaQR!4OM}C;P{gN>B0_bsg^k@3|Y98<8 z3l>6AEM~gy;>I>{h)9t3@O)zb(Uslef(N^NCv0W<_0kC;T!fUt#Y8@tv$6>%_K#{lvNH!1mB z?AbUU&ehKKpz&aM@Z}|P6JjF}N8FM$%gq9MjBkP@%>rINJN5MPQex!K)3Vof9gmdg zHZpss3x5_<_id_1g(Ne_3e%N)09V?SRHbsUKy|gq26;8qN`~$W)`bO$65m{$3FB>8 z_8bVqjwbh>Pg^k*I&Yo`@%;m!2WkNz_p*ZYk!OSk^fvp>`h@uEEkiYBAaV@yot`n3 zwBhO&_+^u!-HPFsmO3pD#u@BusKUvC9jc96ITF|}sIO0P6m~HIY72~CUhR&6uz13W zpPmb?=Zx%c7{8s}z0#ia6Xt~vIh$B&>oVo*#(+@Q^EOhW;EoIt;@=|2>$6?c_Lh!5 z&lvU{t@PY*E_arka$ny<@BUPE{ZsAUzgDw_n@e6O#l zyAH2LoVs{?uh~fass`=XSEkB?^Y0$N@Q3#(-C3sNd;VXnZzmj|Tx+qb{*w0M3;DC3 zSugP)-`g;T&E(%DDRCo@&g)j9;`7Y2yjIq}Rvvut`h~^p?vwD+%|xGlAEMqW7PC+C zQ3#t{8;{4td)4s8lJUC(_<^+^{FV_gAg(~1)gjhs5+~sNh(ysIw?%!MDCXEh!(lG{ zo1MF6t}fUsNJXSr_NUd9Bpkn6i)D73G@-&sTwXlES6$K7&1W}a?wv3G-92626C9-Q@NzEq=^q1lOdY@JL1@qYJGZDqHoFPqoX_Xk*UfSOvqeXH>oA!- zDQ?x~FK9D&7wpgJl@NDq2_YVeU1!S!i4lD;r-V#WZ`lUjtCSoBdUSQ%?lInxGfSoC z!nt75TspvF_?*~ifscG|Z0b5D6OmAjUpCbM$(L`_8jRN;qhFtK4>c9K&;wZ#H-+dE z8#_EC>xG;nm*?>ztHhPD!-fEy=2783M?ur4dH~&q%(-FZ`rArG&d~tmK?Km~+E{+R zwYLSYQ&P;!x8qrcm&3lzZH8>}0T)0w7whz^db)0LAovGYDx(aueopdBmh*4}US^jj zG`?Gyucx0>b%PccSnMs&I1C@|X5j6E9@~>i_tC&rwveLehwB~qf+IF zmXV`~(iJ2pH?Rb(@eq{zpH{9#G{oAe6nIu`GTf{Y2v9DlFA@&@rh2q70dWZHA1_xF zsU22)2Bu!emeg}CJCps0MJ}+C+?gNB6{RLU7#Euy*qct9w?b;3XLiruJ*+ z2Xq|PDWNMK5KseT=rAtIBGi~E#9f|x>yA5@fn2>q-gX#fE7;j;Wq9N7AEy|XJ1RzM z?Q2~=^tG}gn~AXI%5IzM!nV-7ISFbl*huoQu=l$$%HRyEXR1qzXUL_3H)YUXTtr8? zq%^)((wE%4WVX@6`TQYKp`^axuMaT^sW0J>S@xKIRQp4$qqU#14j$?7-V^Eg#TkEX z_xEIJ3}RAx>7LMV(`Bp3E!VX5$2Y3|R7;hYR8>?fs|ug zXf-b@nhOVet|Q^0&>K7mr(GYyDK|??$u>O(fNX2rG8kMs4~~hM%p&GIu21FXxrw(F z6{`s^etM{2G+oQs;3pj95~e}l{V-@dCC37g-W{5hESuX-ksO=?WV;B~F~(yJ5&8|k z5HQF<%xDkl z2tFdImz3(|EvW5i99NDXG&aR2btoIKVw23u>3$(+s1wi${nh$EP=sXYesfh9S~;VU z)&D-Z{d4wG0U{5GN=s|NV9_BCYrjcn3lD;D>rdEw)g2bOgv~ERIx9Aq@Hj9JRqow~ zBc|~+Tn;AV_R-Tl_@U>fe-*AxL=}3MJRaifV!1v{PX?mr$N+d6PP}43U0FF&qF>TK zUreJ;JxC6!KNz+FalRZ-CcSCETX(9;4EeD5?yK`p6(1lU_-MZJ3~cZ(@d`ht#fc`1 zL{Y}lWM6IQ=zS`gvMoCy{SVbD*PpL`q`$wlVff%`-Y=D+kVVXso4c~3%m?2>ufZLvFKUk~bZGqC^)ww8~M zi|}U;U*WR28YNzILxXg?iVjr9OKE{27-_`tjb!%VWAT972}6JSeQMP7%>KG>i$6CS z%g0(BghYrH>f-NzQgt+tDtlNUY)p?swtI1y;wI2SE2;dJ4DPT?UDkO^QPUmeQTqf# zaw(T@VR`6LQ`7H)6#@EN!NL*^l+$qMkxk?fwU&Z$w@o4cLem%mHv)^iiJPdJqaE+|)TQP6JR}u+@N?+#7sCwPFv~BWK-B ze+{qLzt^n%1do2Z{G9$)lAUMPyAN&?MISDr2M;m;i^16A zcBtur!=D{yRgzh+O~r2FY2UAH(#EUe9i=6n*o>&+*nHK3%!}6K?yiZ|A-@R@6Ou24 zI$c9-L)70v52P{EZK!ju&&*oZ#WSH^tlS*E^0Y4VM$F2aC%ie@GiPkK-iUKxjxro2 zX1JKN@b_U9>oOlzro34wF=uC@E7$j6$KtDw(8; zPIK~{_0P>e{Q$GM$MCwND<8QUkUCX(P^gXW?`&l;%9rpAGXbZ^>$@#GgjmonUBoH3 z>#^}=O(h*dz?~4N*16N8druN5h6Bv=KNV4FqjDSVUb7R6znGn-4G(PZ5Ood40vU*p zfl~l>&4$oG6&?y;_T$msrmD0tnX)=1i*%!Lh79*p?^V{-^`TU=q}gdV6$AJ5}?=3kY= zeWzB?2nIn=$)v3m{jZ7l5UKK5mDAl^pnTEdXvM`A*W!y1ZUKmt}L6w^4l~02O4qajxt&Cf%nh zGxdCHl4Rvuvxzc2_f*Nzbt*lQwNVy-@vegQ9#mOv9Zs#Gf>7E4vY<{^5$)q#bNsY7 z?5m{zVqJcl_?#M257oV+vB-MUn0MvO9Xgm*#=~;J%rrK*DfDGQcwf?N@`1(n(f(D@+upgKdz}@(P?)tA)L)Jy=+@I&53ME3$T|z; z%Dvf3a2Igg5a#hV{!fIA4}hT|XSgS)IVEGih&W^}5>%ST!K&f* zfvm}0d^*0Y1s|P%gyN65I_$am_2iTH9aY?{S+)blH~SN7KWn;=N}W$4=d_sYeWn)X zFqi9&6k6>j47uFGFIyT%)$PHe5BvBM%(AxyuYK53wRUlD@>W#wa?NBbSnu)|Fa85h z$-ORZIkz~_=V1M0#v&i|VajKGc92v97o%Jynag1MpOUfFiQNoJv6>;LJFb>o15&ke z*zsvN(%|6pjo_D*Npu_;pWhCDlsM#k^!(o1(_&gN3obZTd9H;@xGUGGiQjg_yXhEY zU+b;dA9FR^f2Q*OtqlG51Y_oJ)9}w4^?%W2{7EZv=mE@hPY?dq8buk6zT*wRp+@x{ zqYkDqG)9GJ;Zi$V)<&T**lC8tt-I7PI2T%7Vrj0Y@LtSZ!dkbF^BNc3M%#gF^o=}1 zVLYdDQ1R6Vf?J_iHz8p&rFXlXmB0%YtzvQQd{UCewj0|sR|2B>hHDOhO|=fyS%Uq-R>j%}In(s$j4bNpdxf#Yq}S7dzbv!m z1v*&M{CCL@cv8?^gih^#0^j5`chs(G7Nd4wyLzr>QB%(v zXm!_BmR(=F`IoM3LLnu%4diT^+P{}|%`%jhgZ@_LDubP$Er0!?hqa|h)R*VEDFt{* zt7h#=CfG~&LY8Hlxry@?XALe{ui4Jt0OgD4?|NGw3!iZ5lwn`&!COkM5Nu7<0tB78t-Z`H=HXuA0&K6T10g2x~od}tGLjJHapun zF|~kmnz-1k?(2PQ&+o?i`f#-=SVG-&x_;KdwRJpO7FyQpYI{O^0@yY8f+X=5eQ!n3 zu8`TVb~7ooEdFD05q}xvHo041b*O*&c3vY4>QUuSCX~`d#54h&3rmBqEI*<28#K!x zd{?HM6rS~04Rwn6J6{Ir_^5W`05jA)-j&73sFLniz;w$G$%+}fE4&=&-6i_OtqlR@ z3G#i<*&`((AlzpI8QQP(8bs5gK{9OeM9SWQ*PJjW3D+5vvKFXD#Gc;T(P;7D%jo<} zli#1cI>ZI%EN+ddwbEw1zqb=`k(~I+lgXF|OK(qRW0cd^85z*|XWE-q?#)!<)nm4; z?1Ehq$YY0`P3MWUDstjSB!A|k#26Z<4f~FKPAp;U%vxB&GBK>$u3u5SzVa8o?E;N54gy zwj9%2S;A_FLWh`pQF+kD!c2`aLSGI(3(k8 z(NiPl*B{l9ZvzMRH(U6HG<*5Yc5%L5jCEcs*nPY?)5k;j5?=}vbVZ*zAcpoPUxtgP zcaEpgvLSNVy19^sD)5|s6schGhKUF{Ak}_my)xf=(~vhMVn+9dW@Mk8UAtP{x()rO z$@6AB6XePLrAb*$=gP#6;h3K57wyFIUPKp*uVHBHo|&m+*DgIvU<7Zbw79OS+s!oI zNzvtg`|sp+Ij=)z+&e!9X&a1Uk<{+|kOG(P7Z2(AHAAIQ*84A$&m9|mM*)mc1tM<_ zZI~RA8WZ6Q<$4c{^tPjUc0M+<^XS25n8Joa*yQ6sv7L ztxRydF20r=xU;rpv7odpnqm|FznO+~ow}kn*`9*nx;54R%`*H@g#(kbMtb___-C)O zCp%{L2{M@#v`7RFzoYL7BKwZ?8Ablyg{2M;tiq+GNw*HA-5Cwx9Ep&jQ%ze<^OIX z-rx8MBClCab}Zy|v3rScx&egFEeGh#tkzTqfG_j&Tw4n4Wf0;huF~JWYxI|1t|B52 zJuCmsMDf$vHAI~xyDRpnvTkb-Qqh!s!P{tzrTFSfw`K8vmdr8+EWn<6HU!c%PlfF zCm#v;*SH8gav`RT_JmSzdKt0r096J@!Wj=Cski z;F2(xQmf3oSbaC_f^GC2!<`O)5@em+OJ%3oax1512eLWR*nQu|eou!5A^2Y$dRl01 zOEOiaG`dRN263h^zv9+PSxlfC*`K@WF`3sxn>ku@Rhog>Nm(^H7X1&G;F{gH>M{46 z$j@_g9&o3R;D$v>K-`?PLmj%Z!pb50n1m_$hb~9E;y8Z$i7tC4hT;Jtw2s4U;)7w0 z#HTiqJ&f#{S`PM)N=$N&4a4`xhGBln>OZRVD@ujYq3@10HJWyO5)i8kY0Ud@w6yYtQ42W}Zbi8X+QdIkk+Kx zT?*X)tlDf{9R|mdOxKn4Jq~gE1iaE*UQ70_pQ~EWP#DL6d1AmiTQ_XGyg63CWt#*# zyy#W`QSwnGG!9y^GvZ0_%dz^Ei$w8$IL-WTA$L6_~OQWlY z4$fdGajBOIl(oh>gR1yL)mUa@9PH&BQYK=F!wAL%>;=sFZXBaOi<$(t|cyWKx zqQ*Zfp?7eA=pXrPTX#i&{APt4%3RG_?vdFlGd{cTk5+N1h6U+{;tNwN`Rup+PtXIj}EI{dgj+!*G|qT%*aIvjhVkzQW!d0*ne zNbeOK+S#zA3)9BPAwEagsO}a34GaYrKqha0x=xc3OHH9VxGp6iz3vb9=n@zaAW(W9bTDK(()jX}iIP{JA3^$BECX zZA8lop~T4j!CHCZfz}Rk0Buk`(U*%qHQFO^#r!C7L1pA-dp3d}ARxw6S+!+7wm5o~ zFxbFO_-R3*UMc3Q*a(Hm9;wC?9X%`CNwP;5hlWfo4jPv335Uw>4_kWlehy0kiUw^A zo=ubvzpZ}*P0T`BRvLi-b%=#sCH_ddLORPBS+TIWK;-ky1Yu;NYJGw7dXEr9!1|4w4@MZk|^*Ik*o;&&U{-4eieF|G_-G4wF!0 z?b-KKQdo}Ya?_2Bvs4QU@c{~zmKnOm`NLNN-rdh=?jH{d(yEnXxdqs^V0<&rcxFYg zymcqe%L8rm$uVTq&ri`lp<`()Us8uqNABzn7Fo=>GCg8U!sA9pII-@#XXb>tK|!-? zBc@637~3en?%m{IOzG%f-pd%TWdXq&t&;Be%8BK0^^LrOQ}nLDch>`~L7SjCcy z8@Txlu?M)2^*5R5JW@<$mGb`y`%s1;ZF6 zz6=jpyAJxy(A6<1&pWZxKb+-?4l69u?kPoHJ<9D^av<&F3m=$R49oVAz8{VSW;Bj0 zq!k7Uq1q~54tVf6n^f##_$5mRAS|EOix;lvK)VFZZbjPosC7yQ4IN0jx?4cufDJTc z=t!nGLa`R9q&0@v4vQIW3#G*Eny8FFbK}xGC`sEq(wNpdEyEC^H6|W>LJXK_)GhW} z%rawOK9EiRd!hTS%grkf1i~J5o)8*))OmNz7IV+tNS<6E5oc?9u<6=nc2o##7FuXo z@cUI`C^sM2NzY!%Dwk??^i`5BRGU#(?kc^`ibOa}OP4!r%{!ibS!>&bFVc}FMh&UcWVJZq^>nn|qT_mN!*~{5;*By#N$KYuTmRygG!P#F%nS=DlN3jE% z*qG6J$;_Z9Bd%E(Crr+Q6BHG&x>`uc<6Zjj-9Gx!>B~c}t#Ui>mYKM>NO?C;D4v{g z1^~PXvBp68>#cJ->zbB{8%hJn1RToGI@oxT*~FtAGMPRFS+XE<6hf-4+^-rzx08(fIw*Gp z#HUOOr^S$c@BuWS+JhECHxOfxbtNTfS%<=MufvSWPtUXN5eBPuqbdzBo{;O2@6Yzr5rld?XY$@$Z|f3B-y<9l5lu-Y@hDh#)B^w zm`Y45mHEVBBzMXGyAIQ?`JD$0H2znUfII(2Wmreacx5zz0WpUn15pErgS{!YewXw? z@;NNQ@jE+g?W5>Ej;xB-*V=GeLC;Y53EsmT(D*!H%AR2Q;f_y8jkv0N$Pje{9QaKa zxeucR90JpAq6`^_(k#LwRG$36*$@UF+l~j?>5WD2WgrlTE!PpLD7ke6Xij-AcR4Ep z*cftk>LEIY;ISyz37~^3^tgNuZmB4&yxSeFoz#2YAh$~Iw!W0^Cg3ze)Q!PuuKpNP@#y-onV%5*8y-{tgJ#Ys-*7Um1lFL?ufn>dA;nji=A#&&K*i5&32#^G< z;)fdmm%$v&h&!FFbHfUvi!UF%n4)qfN@MI|&B7k)3zm2r>5x;58>L zE3aPMo5mK4nM-+@efWBDdv^*;Fg+|{)6E_SbczY4aelAx$PWdChuDz z_fYaj6|=9^r#V(K@*ZO>bag(yRj~~j8b9sR8(Re#qUu$NRAoRsGn)a@)*?8OPJ`QC z8)T0M$Z_^u!h;V~k>$Hl1Tx-mYFR-yZ07*~EISq1q?g+cSq-m&&yPa_pMt~i4|{03 zrr(Q+XS(7&@y#3dEGd!L@Pjd!`j8E8Y`E6x@fXV{I$y+&3qPc{V{wwow@J<~ls}{K zb(Mo}j)yrySEMaPG9|3IU#e6e?+QaBpOG2QXBOey-O*&zRbB zKuoOlIFnaqK-Bw=FCDg$sY5n#b}{&G3F#xtIfR`Lt3NF8nH#_)Tm;P#)}4X??IBbM z18!1MZZJe_&2w3GIgpQRJEs3lt)dy}zU>$jp0G(D8Vk4_`YT`!4^I`oRB+`&Q-4xy zk_}lP{kpxSYt6;vuM!FN;|d|Op}Vg-WLR>#^kI@YzaLZ6~A22QZEZ!^w7+11y+1P6NtB(0Doyu^9VYTpFEK z1;y0-tZHgdr8#8aK%iXeEoZQNZ|;)mYxpiiQ4Wa19b~5u?-{NVxvjr-LBk88i^kmu zu_u3=7?q#{E;>SeG+#I%Mb?G93s@o=+79=avD|t_bNTY~rs1&z82jl0y z{SGnF)+)Xs)$z?3s>g&vI$8dx@MwVByYK{9M;D=)U z)O0x979*}h>0|hsah^8Byqx9(m^AHj<~q8?&St0jWbq+e4eS%e1wRS`ADg^Peq|t# zCE(2p(|K~*D(A=4*C^hz&Aq7WElqdzbtZTn?j@=ww*7K&S535^()%d*9v8!L$2)mZ zSNYA}xpJ;^>OCC!YVv3~$%7a?DFWZspdT9$S`btSnnt^o_k_3vx$bDVyTBAWTdrvj z)H<(qRC;X1&Q|UP(1!vB0{YwikKR!AmX0T|1t3gmQYTlBtmGHcu}=e8Eygjf#&~VlsUF+aiCm zthZJ({Lye&1&(_$xIh zkVV&fqG(^X2_K;bLpOIPzIz6yGozDzW2{~5s{g>WsssVYLLqX_MMr2_D&sg1Z^Rl> z0I!bT6`_g{5)JMZS1t31%4uXe{ugyu7++u8A`(JJJ8SuG*+{g8y@kq@h2sLC-xt(O zm9J`irCSt_-5t?rKe=mtMECsKcN?2n#1lKgGVfSKJZ>-V_D{8m6aSxGM{VfK9ResM zG)NI%LKC4|hgghX<5BS8KAvdug_)o^AWR&*v*Q=2$PHg4j^iaN9JbKoe8}TZuhOS-AzI6R`Dh<47w&e9?lZBC+D?R0{r798{2_3FR zSd%Uls5D;vlT$>xN=O6C!;TOJUa*pAeuA;jw!W}Km#CXJuH7Q&K$nC9JRJL%ja`8MY_HvPK zLX0H_y$o`bl~1(^Z>d#7mL|tA#y3WTv5D>0I@zO*K}ZVcCkz{$8OPeCqJQ<~Lql)v zDtGKhP4+Y?M3Q8e`L?g|@j$tQEAL;4w$u3&&fvDNV(aNykfO=5w2Qf~`(DT5;@ zt(NUBdzm2VD!Hk6iq(fgdSNuM+{_^f`O2w_Luqq+qYx--r1|6_BU|2%5oPbQ6U+fx zY??Yzz-g4%4Co1~!CSs7{F^*1_w&nDSj#|vS^7Xts8`fW;xV*nI7bIEXU8hlN#pu_ zVtVgaux=;zVz=_a`j~8N2|pTDYMH!90pAgLsGTdeT8OyIE|_=f4_47Sx3fU2p%YnE z#=06WM~sE#s{e3D!kk3>+L5o{%Vp}%5wJBbDWII~N5Oy@<*Iw1Qqhp!{Y5iXfJdro zdM}QR16G0MiPemI-_awm=qyr*;B0pfGmYgJs0+fXsFnn8)h)F1dP+zsWs zrV8lnPFF@RVWp?fxEhFBn_-IBWW13Pdui_V{(1y{2~yp*UU_A`ygHgr6QntX&}NJ0 z$ZJ`1s5x60)JKaI6!QnA9xSIj^L+N{<=0PP6G8b1J+0LApOZCWP}_Y(NvOhWzN2vD zosI8?CI#C6^3KX9aHX)p2Tfx!j|JlT|f!Qn$UgUiMdw&T;QNl%o#Q zNIS-J+%_0Gw0G!&^$r7eLw0Gi^BtN^rW`!hW@)ATJ}cadS;f_9y58P?`U?xqQZD>* zS1alEX|0p-boHgDx^BI?x}I_JlVrlv?YomYw}cMkHO~oE+(Pc|VIDO;s20OC3%IS+ zQTj`D9aVpP1H%3w^+4=XpS<#XbyfH53van)Lqn1oJE$^#h+c zO$xY@Aqmm>Ia#so&(Zu#zHjA240=w7S11>7^Je66*=!j{nPfDlMOi4cl$AxI_M_LE zB$PWAGHDwbIEdC;_ugBm8L`*oO}Awu$+0tX5ES+XuKj0}u}+)iGI;BFMX9@rzB}J` zi3S3uyFBL;lS5ix?|s^T+^)3am(HL@lT@WNN%cdFAKH{n5?%nYGnCw?i2rG%K_$5# zM!fU?I9iUU1*fQQTmS#2Sntwsj7Zqu+x7{03ZrFLy`V=+`+EoSYNm?i2O)@f@E@>v zMqAwwL|WYe03N@#>+iegQMQC0kPlGnYMI_f;&FIF`4IJt)YvvxgBrIi!0l*HQDs)Z z;c$+sOqK7&WvU8=Hp#joebf<%y$AnXd|B+ywFNDYw@@jDP2Y)6)s8H{Kj5>WS%`C^ zDxW03#P71~T+;~R8@AkjS-={k?sVfD8%05PsW;gla~#Ps zImj&8<|OqE{2%leZ4z(CK8jPErsGC$#-z(Ee<08kVUJtRr&N6?!**HxFybxj%9boq zc{vJvZ5t;y*YV_Lr$(Q*%14gU8S#_PpqI03CBhJ0UPJG`o2KR(jY&}-4(&Xe_!=aZ zW`E`7K@{FVS67#uc)r$>{l zw+Qq!AvW~-O%zMJjNCoNtHAo)0QFNxdk3Y?nnhk`RQON4-7m<0lNxROs!wz24pf7u zoWG^Ei^JnOUFt1hul#x;hacZP)nm~$MO@}Q&Z>1*cL*$%kY;wxavNi@1|ku(HXq`V zV@UbDqW+oH0d>9)pfI&Ct!d8UuK3b&niN+MjRohd_d@8flZpD5{13JIv7M}ViXr$1 zP^`C{lp8jTT1}<-0{2f0nI@)`z0c`G$5#6Rc$)F>oBg$Y%3MXkJ4B$vj57ay&{hK zn#(ixs-r*-mtB0`D01%IxF_ySh#Ew^ToZ!R^`xccx2@TsvQ#<@PZXHNQZhaCc`K)< zr}JxdaDiMLhtI>8S5MiUFHL$6iS28Ya;B5Ei1|A4q}XZUW%7wcL8NZbX|TBWXLlyo zN~gbY3d^5zX$qnIf?FOC)R4(*qMK=wS$&{N$mLvgf-4qHLO=f{shlud=3F$KGI)u3 zQ#n_Hb?3-?u#Vmr3H03b8lXkSSaC~|qa?GfS59(s?5zFylf`=usxCk9QxoXatV4MX zrFoy6(;|;1Vh)^6u0QgBqkqxKg5w}fp{GosYNlvVQyZvbGw~}(0`V!W_fXa*gKhGGCu8Z7X;HSZ@~^Rojsj5n?bh>3C}Xne<%@FE zwh^0{8mn;jziB8ToakcuLvhoO=h>4La}>{WWWAcJsfz8GK&Mxn5~BZnM)9nQuE^QV zZ)XqbJ#TymOAsc<1gN@+tldLti(5z8_*+GaV+|4wJCXvaji8M{z4F4{7fU+8MODer zW<8x#h9`;p|4Bc|R!p*XMjk}f3w!)zD0LQ59nLIgMpU%QuZD+sLC1e!36vETPsY(7 z=+bsCn(g_GXn+`&r!Q{#c_FuZe`bto?7={#yfoln?zuxBKlhFXK|fj#WVG`?Bvs9QEqn(H;rjIp_RFjU?33BA_Y zC=#>8LR%pxJovaT2>--k$r;dC3Ii>|(Rzl=M;-ji07n;xU;Z87cxA;4^(rfVM8hEB zu>*LQx=O>KOUibB9*3bJ;vBS?$D@CBGcRpp7?nbf{Jpo!?SCNkN22?Ch~uH3LYNT_ z!O3W|oFltdc92f?NQxsgpoo4dEywW-_km;KL?pD2=kJI*++Mqe-1hrUOzJib16a@g zwggYR>DBy~x~l0o+-DIkz1JLev?be&xHBnhou0J&F?1Oxt_DO_E>+RCVPq4HVdLK= zKbqybg`ny@Vjp4`zzd5R3Bnucuf)p+9vHT;Y==CA{RSq(=Y+emPZasEnnhistie`V z;OFOu6!L`jgQ4Xd7UPQz4zOpuvrnsAw;x>_h|RK=w~mU6>FYqL61Fu5L1D8UU&pShUzT%7}FOB{n{jQvn~r%y6+5DATaC+WT@f$jVy{N+FRgTODx6MKz`*FRD_I=G7U8^wR+bGH^;j zdLL@CzieeExfnHf-1>@KUi)g=DN&=F&^I!}xt1vQ>j|4qWLPNX^Yx1KB*(+_G@em{bJ<;Ry7(=`i5epe!g@Z)qPg(#x~GL-uItlTDrMz9E33+oMK9Pr`Mwq9L&cS`88${8&` zNNa|F{-w_xy#Y)C6*L?;~XxeesBkpa_g1B5L z)3Hy&P(ht*KU|8PJ+R5}6~z0^(Yeyy(Y;5%EHHj|kYKkXu|HJ3L$){T@X(f_mc;m8 z++=Rf@1$dI@N>mf!*yRq<1rVmYC=V6Vx&*5Ke2ilBC`h!VNsDI){p-UV?}qt-hdi# z8oE%Q2#=li-B`|bNTQBr#}>Y)81I$A(~i81`w$QRCJ4XiY6;e=Y#C`F-Q=sSE(jvN zrU_*h_(AzvJ3*76FLv8C3VAhGk#=Mb8L#DZ+l@eMl$|>(t<4u1KR$&EXKI9Ym zbt2N*27x4Xn0D87IHlelBC|q1?G%s4bD{SbZoj8_&|HV^%(ctH%r_xyllR>fQx&gU zh;m6F1fsfzaxeX5a8fKCF=|%YP|1FO`XZvB*8tRibD3Dw(va*Bvyg2r1=jVuJF??R za%OK|(hi(9YE#zeaKu;UzR6IsT93r~mgWk>=d!z7p zld`X?JhoGo!?r$QK{VuX^HPWU=gg3;Gd;vAchp{JJgG>QKpemEZ^)iSe7r&kntRSYmi-Kedp0+jOEZ~ZBe)&iPftQ* z5X@5t2iHeio=i=GdxLu82M&5wYpQJ6!k>zVo=wycR6O(4@w^L8+zVc($@*D}2Lk=! z$Dwo_%VcdH^OmZGsdeo2WC{ZTE-J%&i?mdx0*2IA_NF%!5f+j`?A~95fUK94=HfZ# z4K$(tVYe8ZLKOY&xT4Joe$}{du@n2L+Wug-?I?<>Z^)9_CxlTCNieXh^GAOl(ykGh z7=rg16%fdVA`~bR)riGm_b98{)pGmD@YGpY@T?hJIuDsKN z1N-C(goJ#Wa8@p0{Qj39x!3;H-B16-+I@iuJUpuO$xEKdXc0V4HfcW8dr+Zn`|5uIFzvE9 literal 0 HcmV?d00001 diff --git a/docs/images/plot.png b/docs/images/plot.png new file mode 100644 index 0000000000000000000000000000000000000000..9665e16c471e48f3b3044b77c158142daf85344f GIT binary patch literal 16626 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 XS@P6Li*ys{goKa~l@+NJ(hK-+MA1uw literal 0 HcmV?d00001 diff --git a/docs/images/visualised_graph2.PNG b/docs/images/visualised_graph2.PNG new file mode 100644 index 0000000000000000000000000000000000000000..42f410e30bc80b4b21e20e74d6d8f294b3a81e78 GIT binary patch literal 14290 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

#_*cf>gr{t0XQT5n(QfZTCi2Q3kPp;d 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 literal 0 HcmV?d00001 diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..3ac9548 --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,11 @@ +Installation +============ + +.. + Tuneit supports Python 3.6 or newer. + +The package can be installed via `pip`: + +.. code-block:: bash + + $ pip install [--user] tuneit \ No newline at end of file From 7d61e47f750a8fd5afb5b1fd1dcf1435cc1cb10e Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:17:07 +0300 Subject: [PATCH 053/122] add_deps now works for all types and a new function get_info was added that gets the information from all data objects in a graph --- tuneit/finalize.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 0266890..8ab645e 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -8,8 +8,6 @@ from .variable import Variable from .tunable import Object, Function, compute, Data, data -# from .subgraph import Subgraph - def finalize(tunable): "Returns a finalized tunable object that has several high-level functions" @@ -107,6 +105,14 @@ def get_key(self, key, ntype=None): return matches[0] + def get_info(self): + all_info = {} + for data in self.datas: + info = self[data].get_info() + for key in list(info): + all_info[f"{data}__" + key] = info[key] + return all_info + def __copy__(self): return HighLevel(super().__copy__()) @@ -159,9 +165,7 @@ def add_deps( else: end_node = self.get_node(end_node) - start_node = self.get_variable( - start_node - ) # TO DO: should work for all types not variable only + start_node = self[start_node] end_node.value.deps = end_node.value.deps + (Key(start_node.key),) From b0731bb927b6439c930a9b1c1ee2d4a4623810e5 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:21:46 +0300 Subject: [PATCH 054/122] added an index to each node in the visualization of the graph and thenadded a function index that allows to search the graph for a node using only its index --- tuneit/graph.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tuneit/graph.py b/tuneit/graph.py index 2c0e24c..307fc8d 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -22,12 +22,19 @@ def __init__(self, graph=None): self.backend = {} if graph is None else dict(graph) def __getitem__(self, key): + if isinstance(key, int): + return self[list(self.keys())[key]] if isinstance(key, Key): key = Key(key).key node = Node(self) Key(node).key = key return node + def index(self, key): + if isinstance(key, Key): + key = Key(key).key + return list(self.keys()).index(key) + def __setitem__(self, key, value): if isinstance(key, Key): key = Key(key).key @@ -276,15 +283,17 @@ def visualize(graph, start=None, end=None, groups=None, **kwargs): dot = default_graph(**kwargs) for key in keys: + i = graph.index(key) node = graph[key] if start and start not in node.dependencies: continue - if hasattr(node.value, "precompute") and node.value.precompute: - dot.node(str(key), node.label, style="filled", color="lightblue2") - else: - dot.node(str(key), node.label, **node.dot_attrs) + dot.node( + str(key), + label=f"""<{node.label}
{i}>""", + **node.dot_attrs, + ) for dep in node.first_dependencies: if start and start not in graph[dep].dependencies: @@ -294,8 +303,7 @@ def visualize(graph, start=None, end=None, groups=None, **kwargs): if groups is not None: for (i, group) in enumerate(groups): with dot.subgraph(name=f"cluster_{i}") as c: - c.attr(color="blue") - c.node_attr.update(style="filled", color="white") + c.attr(style="dashed", color="blue") for n in group: node = graph[n] c.node(n, node.label, **node.dot_attrs) From 6c74fc7dd3ae281e6261f2158ae7835cd7501a6d Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:24:08 +0300 Subject: [PATCH 055/122] removed function get_attributes and used function get_info from finalize in its place (because they have almost identical functionality) --- tuneit/tools/optuna.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py index 57283be..14c36b4 100644 --- a/tuneit/tools/optuna.py +++ b/tuneit/tools/optuna.py @@ -49,17 +49,10 @@ def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs self.storage = storage - def get_attributes(self): - "Returns a dictionary of attributes that characterize the study" - attrs = {"callback": self.callback} - for data in self.tunable.datas: - info = self.tunable[data].get_info() - attrs[data] = info - return attrs - def get_study(self): "Creates a new study or loads a pre-existing one if the name already exists" - attrs = self.get_attributes() + attrs = self.tunable.get_info() + attrs["callback"] = self.callback name = md5(dumps(attrs)).hexdigest() try: study = optuna.create_study(study_name=name, storage=self.storage) From 9a8f85079252bf6bc2cd4d780a209b5260b43411 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 11 May 2021 12:26:57 +0300 Subject: [PATCH 056/122] changed the headers of the dataframe --- tuneit/tools/base.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index da93332..f60b5cb 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -86,11 +86,12 @@ def record(self, value): if value is False: self._trials = None elif value is True: + data_keys = [ + key[: key.rfind("-")] + key[key.rfind("__") :] + for key in list(self.tunable.get_info().keys()) + ] self._trials = pd.DataFrame( - columns=["trial_id"] - + list(self.headers)[:-1] - + list(self.tunable.get_info().keys()) - + ["time"] + columns=["trial_id"] + list(self.headers)[:-1] + data_keys + ["time"] ) else: raise TypeError("Value is neither true or false") From ee5847c34776e219eda77ec6fffb59ef6cf650b4 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 17:15:22 +0300 Subject: [PATCH 057/122] changed data attribute names to contain only one _ --- tuneit/finalize.py | 2 +- tuneit/tools/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 8ab645e..c2a0737 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -110,7 +110,7 @@ def get_info(self): for data in self.datas: info = self[data].get_info() for key in list(info): - all_info[f"{data}__" + key] = info[key] + all_info[f"{data}_" + key] = info[key] return all_info def __copy__(self): diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index f60b5cb..97a661c 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -87,7 +87,7 @@ def record(self, value): self._trials = None elif value is True: data_keys = [ - key[: key.rfind("-")] + key[key.rfind("__") :] + key[: key.rfind("-")] + key[key.rfind("_") :] for key in list(self.tunable.get_info().keys()) ] self._trials = pd.DataFrame( From 3733ecc9f8172b1821586da8b89263d4323fb84a Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 17:18:19 +0300 Subject: [PATCH 058/122] updated images to match changes in code --- docs/images/plot.png | Bin 16626 -> 11820 bytes docs/images/visualised_graph1.PNG | Bin 14697 -> 13117 bytes docs/images/visualised_graph2.PNG | Bin 14290 -> 14341 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/plot.png b/docs/images/plot.png index 9665e16c471e48f3b3044b77c158142daf85344f..b29555eea01c4a200a9f67bc728ea1e8b788f2a6 100644 GIT binary patch literal 11820 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 L%ZcSa)Ajye!hmnO literal 16626 zcmaL91ymML(=ICA2qFT~pdek+-3`*+-Q6WfH==Y%cXvxjcZZa8cS_t1e&2u2J!jp! zT*w0Ves|2wGtbQIG31ksC<@|R#1}7KpoohJ$-j63kqW->;bFmVW^5YW!8Zs8c~OBE zrNekT;0I_Eerf&}FDjyt?(|^5&j_|+Y7Q@6pmsieLG;+<8@_nK7bGskujs0Mun6b+ zO{o>u_DJifW$$8Xs;TL)u~EOr@s<10kU$w_@P`gWO1~d|kR;OlXxZX1OM*u&0jL={ z(R_%}Xpq?|^2?l$Ow0agCh|NThW&p%(vJED6Z;Gka9Ul4!Jmhy&%wdH64)F?1M@Z##nO3(FEYof|9%5ZKospO>)8X}eyuX@fCfPICj-FyR zpM;m?xp{ZE)KXOQO-oI{WHMjQ^=i?j#=@x1u4an-xOZI7G}M~@FLEvAdd)CY!>GMJ196X0&|gF-R{FTuW*ncdG#CrdQ2zQCe% zB-^wW)FhE}XO^`+s9!KXT=_g=SuNBGdwMoEmY8n;uD64-ny;4AyF4ABGdTR+Q0UbY ziGLmSc>R*=c00@U?xbgRVh`Od_AN!!(&N&0H~<6Yh@t5S(NX3$H*X={p`is3YFBtk+$ zCykj>t+5gf)~XEB$F#@WvPXFcx)F^BtxQ`nd^Xz`BRqepz3)y$xh^IZTyJ;sD)c&G zEf;E0%35y=-D!FvaPtnSPESwi^*W%N&&MUN&(a@@)ADxii706TQPsXfcMK*n*=-Ku z)R0C+L`+%Va3lDdq`ARDKtPB|N(zEwBDv!U3+C~EfxW(7i;7813~|^RUM=&vr@A~? z$g`#@$cs%)ElG>t*R86ooIL82;ePi{Xi40Zc#b4G z7FJ%`Us>LJ|F*~bqg}VKKLnC#8aWc*>5ckdU!-_>c}-cU#-+)Ojox$;KKy8f?_CrB zG_ZGhxnM*3{ymJZukZC5;bU-cFuc=afkIy4)+NnsVXOL zDkdxFe0M6nB;=fX>XG>|W1?7%=^*LEt$(<&+R$_F?&fUQ-Y&{%dzAPNY=Y0*yS0&a zce3D&FM~9SrYtXdMpkoR>991@gj8mmaS;fO?LW@?SU`=6z_%>c}AF6u$}7p533T8f$iQaB9dN zRjo0{EL15g)nfWu9=k;)fx!`ALub}(Hl8hR4o6$Hx?VohcE8Yuxjz`@QT&}s<>Amx zf{jOPyLXit;|BM%ZrvuS_egR?itBRLU^n^)m&pWKOXc>qsS~H18NFjKrQv+VzM+vP zzdL4SAFJnFUt;S9mQr;d>Dud@gB@gCmf|0IXNxY&n7D5@-8t-o#Uc5=nQZqie?5r} z!=ih0$L)rHxj&ahi*=<6_mbdFOIEbIhdN+5Q#h=j*^yGEG@Mj4GJinGodGPRT&$V_ zn_H&U6qw9v^Tv|#4U_A!?t%7I>}X7CCZ4{8tgOmbuNZc4Cg&xPk-^W;5ayQaz{al^ zmLHVMN#nJn_PLOPB=i`SPyd9`74qBTEJUYPgzsoJI*Ko9r)7{DFu%e(wTW+|YT-s0 z9QTj>{n)UTreHvI$~9A>p|YP4DV6E^aI-aal*k1xDC3ixY^iojVd-+K7cMTVwPJjT zL>NGbu|g%Ps?|#O3-hS~`noT1_?oM87WRI*W?cE(ky@~IMl31N$%Ca|e=x-Gd(62> zL^cYeA zaYOpsXzT=msB_eTs4;8*JM`{zI2ww`)R<4z+pTp+XyDXlk}GJ$Y6V3$y9x;@vS2nG z83LeY#|ey-0dSsKX|TQB1@N1a6Z~RRn@ufNV%MklPxNm~o7gLnB~p??He~$N!4}&} zh1iP*#Ii2QTK**rHybJ8_TYSddPhgbtB$@pGuX(3um5W}bpYxdORK05W>`iRhF}lR z2{(!R3$>k}wHbUOe$$_vWiUD9H!8sX3XMaOlandM-e*VlGL3L$^nRl-rSHV=9kZQ1)#x4fF_uY5 z@a-KwD{&()k3K?Q(~Z5ouqzy;C*#4gwt=-TC&Ippw4jcy@@n z#e8)EOQ_`nn<5ajlB;Px)b1;`Y0f zes+h=d@QM2iSLwVC0DPPGQ8^8(X@QHl}nSkjP0`bE6$gsjJ~g}ttlmL2#38X*lWAL z#GS9P$fMOTcX33<&t#0At6ymQ2s`YHpuYCSu76ZqT3UFjP^rRtk;93bg++NPDxPD% zGw7vCyTaG6?i`rt9Bp@}VeStBede>d7$h5c1==k+&Z0e|#FSsW3?h81)+`KO;QW?q zD)8Rja0Ku%8%Lv|f4bhMK5(B6w|;n7s#R5-Jz4;docfi)F<4?}o38uk+-w_>?WNWN z+|m1`Ss$ym#?l~#fXEcQ*(wuRWIVP{77SkzV96A6rTFWt7ppDe=JdA{P|JS(`c<%J z?^+_A%%WP^#dch0yDCs^v#hOEJ9)SbJb#X@aR!5}avR+GMq%z6L(ghe7u=Dh7o6Fg zS)ugHJVbn6US6)#UQ+Y@+~ado9NVko<4*^bOtg3cakKi{@HlxfX-QJ=wx^1)fKi$| zcYOZOIopMG1Xs8qs1Kmm4GVG|>V`AAE8lLH7eIYIBr9NPNuhM?k1YvA#f&;amtzoc zfy{bDzMGzRCCTR(^Mv0m&i^tq(-O&v(=9IX((ixtvXbZzJ^iN5&oUI>LBpxniLXNf zi{56=B^d|HL?~Om4E>*hjHJ8B^oKaQBGN=dY-D(`Z@+A|4k>N>lLr*lp8gyi3&}7@ zza~J(M8}1e%7?z&M|g+ZQ?)@qud%l*khn)3!-($dVSnXz$p{BGrDc5`_f40uQU01oPUD zoi!$^q~Z2#z0byiJbw6Mq6lg&4aOSVgUQ9=D08Hg=U9C|fLrA%zc$GCe-4rtp9noz zYLim%#!ZNMV|`<#$}7FyAO?;_doF*T;0;)L_!S2nD&Ehrte1JoTCdb7R=XpB@-E{t z=tE!bQ!KDD2BNYvvtMo2DgWGVdlBzal-qpAh0Mt%1v6Z^VpMpbr@y^z7?lwVIG+|J zH@6XCzhst3gzfFQ*}?q1p|3JGHphB3B9EaN;LWrrUkxSHTs7tSZ$Vy57!P3 zB8-QU3clAFolHD&3m7G(mxMgw3w1VG20fAa+AWRqk$4QwBSSGi-H%$XCYNL6(JBkC zMl%Gbz6ze=Ng+i>5^$??$5AhzjfoN#Ji@oOwmvaCxdwZqi#jGoM#Y;Dy0*JlJUl$A z4fZ*=b$$0=zkZ#(7Z4DbTyr{F!Uw!aDLzbpJ6fUglfPBrqar37Z|Z~ieOnbQxYSx~ zj?LYj-pCt3cB!{2#FiWmDKPrgbpn1$)mkfHNe3eMwMEi*>Nme1ExMj>1X2 zSJO~mpFgnKdo*37A{#JpbGCIdYY?UP_wrzuMNQYSGZ1CBl1ml?K>~qK4tVQ~v$ZH* z^O;X*90H*yQ>9uO0Wr`??~(ECIo^bp54ExBzznh#EAV1WI- z50rtSSDEK?9?aFZk{8{M^0_l;*Uzg=6j%eeX*!k}up1y&)?U3v`<6qN`(o0ojjn~r zP19lB1#rY7TFH{k$Gee7>TBBp*TE6q$6{LG-jy#Z*KG#Yq!VwUA)*C;(<&bwFXJS%lQf|&{qY4_3lB(-7 zxIeJ*>AcvRL7F$h*c9lQ48sU`w0dQ3kA3t+w-G-{0!36De66?ewiPoijEh0{=!M}H z&P%)acf3?ca!|Y~z78IngYsA5gL*)t!!is?0Wn?TOXYAXHy(QHq{hQ$KP%a*I-JO4 zGP!ZJ?9)c%=zY6G9*)N@ul2^>)#^`I$UO7P?*@nYoMkHM?RR8kg`+VHxH}Ae^^p{g z+1Zy5_gA69m{=UATseSf$($}Fbcclk4Vs`=@RtrPQ&Q7}A>6-vk;tC&_cX!eTHm$B zh8L5~4(W|~$t{DUW96?=bm6_7*C?q^e0W>R+QrF9>C3e~aQv<(zp$pDzYq%szV??f zH8thGC!Wg~kZH4+DJ_)t)*+)%l}{Q)U-mph(RSZ?58@ydm^2E<^>U4|5a>+-Q8Kh% zy^e3+UQf|tkRieu=>Y{o1PY|2C6BpS=Sb&-UfW##QS;z}$4^T$rriGf^sO$ywmI|d zPbb#|F`Y~b{s(slXXZ3Dm$(b03`}x0-`}ya3Cf*-nwd*TFlz)jou0HVcf346bgc;ls(CS-0c6$59t5R`2_eQG`ufZsAL`;1cjz)Tl}lpq4e*dEpP}e2hp;4W?^o7bOvJYzO@ADcQpxxLeJRvBq_U`GM*_66#-6ex zuF{5=*3F&xu7stRqPjnb83==t!qJ=U25M>f#Jd5&^DhQ~7|Pa>ku1X_@lL~C#cEZf zAS(0Av<{F&VgrO~js{71wmTI&57OlFX0EX0UEe|->FgpDdMZazEMG}PMpi6MU}S?* zu8~#s=1_7`rg3fnUk-foWV)RQAia{{vZT+_b0JN}3&eBOZ`kb?-D`@a*Ex?!W(35) z27&ns;u#9)g8`Az0JYjg-VA~<9W46>n);J3)9!Y@JdDg;rIC5gwnYmspLU~N zNJ=Ph*nMvVdo<0UC}gcj-e=^zZU>>3n`=Zbfmp(81%IpFcH$h$jyJ3}X^1I%gjb?V zi%selzjU8rfHX9YWqUN^%iEclS`dCqd69lCkJ)(4`RM!G=JtGN-iDJ0oE#HiT5W)h zg7EC^(SeRWI?K(Yx5xZPOt!Hvu%v(!P*~UlG}!$Xfy=7Qslm$1DwWEiR5QTpg>&V) z@m*zd#9tn{^uHFh2ms;W=LJ+Dd~*0noT9%pB)Rh)G$tEs>kaHpLk^YXeL1_*c2`%| z&*kNMYX8@FUj^m(`T4Oq98LMPyl-wc;sXyY5wJNfLypkrV8G5~x>7!T=!i>Fx_kHe zu9>SV9J>H=Yjf6ax3R?bKdjlDPpeQtf(YS;Vn*^GBk$czr08&7y$q1Y(i<4*WO2s+7=QK`2zwiL3?|~G}n$}Sc`QOO3#+^{})-d4Kn@@!tdTWqIU$9ZW9E)QhbC4^rG>+RJsb9y|YjV0Pk(@bnr#?S zogck6|J#QtV{vU`veJtiF(B+eztoEk>>5Y=hKEH4;%FG$P7O<7xSBVGo)!kqxg^I% z(g3}->rYtuMsDt^c~ly_vz>5eS=qFCGN6&`qiQ2*+zhrWd?mk3d!vc3q~Aq{z`WPn zo+WP;HFO{-hu5%{e?`*hEL@(<`L)(^O5G|R@aFV%LISsS^qStBg1E<8tA)wFZ-62z zDdZ+ITPPRCzQ_4@``=!LmZyd=pMMUGF_?7;sI!QYBuEpRYf`bOu>yh7MxUHOPc@}) z)l-!BAp<^Hr#jud>K!M`8@;kNUrD9rm|c}dKdai$z;sFiWy~aSsWt1-Kz52hmP~rI zTu*RnlNy`D5QF-)-d7mq1(5346kWD3s1~!LA=%h zQ!?cZAOScb3M(@xgjC=Fb+nvWknsyY0kcK9!Yzd~F&mW!k}(#87ucItNY<LD`dE(ck`7TMZ!?)Y}h^ z^><^Rj?$JaElG9Xo_Qxvrr;!*YDgph1ka=U*{VgqwdvVkE!=}Ce4SKbH-kdSxt*i87F!Q)^G|!wEe$fK+F{I3eLy3{H}DD zB4zk3(J`?Z^Mf%L6NdQAi8Yjah3u~Y<%$!?t2d}ptXOZ+14+xL8M~VliW=%Di?xna zweZQsXf#y91EFJXIB{S4w1K||v9_>~P(B6|4p*({Qxv`q0_%LR`zF`FB<8e5AT}l<||?=V$yg85o^KV_7Xi-vn~DhC)XD~vMwyFx5QipZ)Y$7JhAWC@B^NA+@_#5 zb6qk<@#fpln8I}V`w2O)R$(tzWUys)0EuOU3aiM(R2;CCt&LRx5tO$2BH@ZN_&87g z6%`%x1MpGz-!IVPT#w!zbeuXk*sUeoD6VuxrmwsX+QfMZd4k2jdNe#t1Xe87{3RPT zXxLc!^P&AC!y@ND+9_?VWs^5Q8ni#(w2)ybyoQ7_Vx#3@rg33vt6A$l9{}I_2Rk3Y ze9kt_N`{|fnV{Z2003lLOU-rn+)Dljwv&2O%HO2Ev`+tp!el!chyw$^JUP;!-Vn79 z9n&^0yh{F7ChIa&%8J(JlqAbuSnZ|OVwx(Ri+G~Ho&+bjo((hx*HJfGU3N$wz4mvP zkdWhUxUS2~cQ(tVH5`OMWFTciZV0S`)q6Srr`jvWGE06vc9%n0=T-LF0vi^53G93cwwZgt(iYwu4j2)IMzfq7nH(*iF)vMkxLJp=E zt%X06jNp{d{vnAZmaQ4Bl(w*h*p@(_jcw@#EN1vk?{9mNQKr=2Wh=(W`t^{A^Y2= zCEpSMrH|}&e*w5@%qf~9u`CEeUHz(p>ECigR%w$+r^knkzxeb~{otcMb3(uf&%R>B zP}I=t<8&dO?ZjBc=O7|qy{enw{?C%V;F#`Idl^&n%E8iMN>%~yt$ydn)r9Onu-L~& zI63w*{*wco0ib{v>wAUp8nIK-w%i+y=2X;r8e_Ol*n#`%YF;l1d$Uf2&YniD4~M?Cb&0X1F~@X}g}MnOEs>I~o!Z zA#80;dx}i>W3@A=XoUjh(lK~H0VqLbBVQYLH;E-DiAz2U#?F}7hzO*pJICgOrX=OyHJ zKmQyQ6!dn&Y$A8ai&zm4q%Bh`J^`p&62x;Q^s#V_A5rIp?cX7(;)5{o=x$L;)S`B>(u zd{2O5PsfAUX?YTFEX+Gw!&AOha4#Iy#jj|Jg!2fv@@wSX{#jxr)MjY8@Y}HRzVq!4 z;o0nmq3>^Kl(*T}1tDQ*6($5Q$Oe&Ms<&kDf7C_N=|Mxu9_6*pwvUH2Is1q21h zs?8?+3P!MKHGd{B7)%;jJe{FjKeW8>6>H^Wgm|u=&nxvdo+oQG{uDhe@iI&+PzJ>C zcBkv&t{xs1G#@U+Q#S%K1P!wQtj7v-8l4QY;Lhj&;*w^BghW>_VxgU}`P`(Zr%uH! z<&48IRivWAixd?3?R_~JxKb2G5I6R}N}%e9J3NYtHlMP~5mzwOYRat{?=+6jm}qpe z@W*NWetmtNzX#GAg+oa|o-EzKu)pmUzDNd~-?I!}<9~}8wE8hR3Or|xw*7;Gvec@K zUH2<`6d`KeFLnzytcn3<8VZI!8BPkgF!{@)p7*kJ{%upR01{103~9gy$;`~m#Kap| ziA<#dcH-&*Um;*D030cRfew6 zDrbDH7pBvyS@PPe^}eqiPB0! zXt}t?{xt%?0%XF-yWbvR?()AX2S#H)Hz>3#;Nx%zDH{V<6P4$!=*x7!?`WW*P4MJ- zeH|S3*_=`>gMjLUy`hQLZvWy7?w0B)>onb<2B=X=$`8m7yyS3WfXXjqRv-W@fpe>bh)4I8m7#(Rl5mtIRV{(vfD!+?fihNQkf>#be?1yw6 zarn4l zp=Ln7P?;8$T#l&aDyOT`KYz{8W5}QnstkMTms*Lh_|HK#x#bS^*_M+ze)vq;y@+6KL%hp;Aa^Z z^V5a!3mZUjkP-Mz7v0Tc2Mp8yx%>VQU>gT4)q+Dt%yuW2!iJj7`R=UoxGeH5_?0^U zKx^YCSah;jQOUn#_iG`L^zF`s`JG6&ogr7yKJGJ(8-oJSLBm9j(c*kAFFcE?%^R%P zMrt0IG^iMhV`2FCZ!v-=LnJ)9QN`3-AgItv^&s6^>1|-MmqBHR1=X3Q7LT=pyfnY2 zrlx8A?XjMoEVr}G{{*xC&*7 z3#4O@uddqY90O7DLlH2-kr4VvK-N`WixxpJNDK~WtO!1S4y1|RPl0#0w}noVrI*_U z(@La0e-txF*SXR=G87u@H~co7fODind8+6D^2c&ADTK+^mU0hs5*DBTJ=5sdc5^tg z6QMn|3?Ya7<-E`X)azF5$rN^nNhGqTVsy1K31v(UiRD%V$X=vlM=eyD6#WdjUB^(u z4Z_0^%o78#X!fdxDn)soprQmIa5O0Dg^(uKt9rqyTxu|piud1k)7R@$cpP0_BesdZ zeuW17=&$E8UspIz79_cGm)N=_UyLB|-8hVfy z{pCM)g#-)6!h^78ox#E=w`2`#)uS*XN{9126tk?UB}|LY#Jb$ zrY$ztrn4wA{G7}R5(DH;L*JNW-o`RYnW>04-worLIFDuRve^HJ5W|9V<{0H={Ea&_ zibkD;lS6$`MDg|W@!0W!u9BrD`4f;L`>w4&EIsK5QGBhf;cb>7*bxxsX@oZaefUH_0q7DGkumF*;)F}xgvtFk|szZi_w)0#E6GVMVx0Z9pGqh=7VzRBS zi4-_E1fTySSwRLMyc2jA=X9>@>tJ-s28+r~|8Kbuo1x|Jh1%;pq(6huH^m^~Fyf_@9R^`|*q5m(L)_ArR6HNt|D0S# zo#O@$Yuvg=*Qd(7etXw1YXF-~@!{X?mxACi4&4LFJECZ{xe4jABqdG+-WM zR;FzxKXb~3ZTmEH%16GJ&*@~;x%c4T4n>4k92X1O4mSLhT9=DhGJ^u1BhCFtu+zkh z3^t7kIPzw#0O1IzL&YqmK{t zwPJgF5-oP!rNa7#0}XQ}YGCB%%~m?#&TxewWGTb~+|ZfdolY3&K0aq4AD!%e$$_6< zt5-~H&s1igJ2uDLr>@(xQA!-Y?xPD#!SCy4Jy{0?j+vR-!=us z58Ke!dXh^OQ2f@1v*WXvUek_(?zMK9W+Z?>nQFVP!T_fD!+feRfypFt+MU-d{=YSE zA(ng)D0MmQp|9_Tq6{q_5AR-z4KtRKNE=!q<4Zd_R^PhqP1Cr#xvc_3-)(%qzY{$~ zDeq6?ePdrEf1WhMJ^F+yv2wQr+;X6bnZRH^`9Ug4@s5!Qqv3!Cj>U192l zCG&!!Re!CvX-g)KzI;TH7>3g8z({^|^|$i!@`$uE1qyJS&ikKcX!N!}1;oma$6vij z=7I3`9CKxpj;lR6h`0-X_dD$}3^64q)oaw%4N8bi*t|_l(GHsfc6D`iH#*7E+S(eM z)4A9=%n0P7(B^RXj^F4mivGOb3g;*NHG|CmhWq6*j7CWxZdS^P4g?}cuL3{DW_Lf!Gj&sfM1wvAv_0P+)&qbnBlP0nI zvl?Qm4Tv*5xA5^?TuP(^@x{RO#T=#5um`;HJV``^BM*(9fjI; z5sgt6`-`zTrtTFy(QddM4lWd^%VTC74W>l|4{Is*_B!XdIY5VFhZ_yFc5@~-b~?n6 zbG>oXb(l52@DQpEe6|P2u$ORm-msf241wX6Oy&@Odmvb$4q*VB8kb2C+P^*loXL%U zRP%5WBB%$YtA6ADlZ~(zCLrMiwu47(cf%H`}in#`q#zzKinV_mS3@P;EUB4F3NSA=*N`n!<1e$ zu<|$*7)#?lURy~epR&BAD=rZb={8$hcp%u+P6+!^PZUQ=)b!8B+en=(N>g7rqr!P# z(!&n9Ji<}CBeAh#i8T})=60J12-tdTN`Yuc({`4+TwKudZ!;3?<8MT5q~A)TPmGW> zGW=(0ua+iV1RmtGR#8;mNnP{n?&<2v8ySp-KOmJCS5*j@sHZe)cssLE~mP{$`gT3l_J#cgr9?6!euE2zLxK6@wn>^RoCCQq4~BHlwG z#--nImhWnyJZ<7ra4tIU8R@0 z`xD(iZdToN1$~Gd^D=rIX4rTz@WK}+Oo%cCd;Crk6;Lenozv&HKX&7%A^jdRx1tJX z=YfSQCDc9nXm9@fVW<*u>FiC&s!N#H4`tmy63;2ijnL4%`Dl2vx=kVpehnSP5Jc08N@zUCjNm02l+3Z{uurU}o_+*zMZeVo#yFC%b+bsCXXmX9KISzRzc zniRU}E33Gi-*%%oVJQkew%k|z8AsHVW#J+~{~r8YFGT$n0O76hnLQ$cm~>%~3iNxa=Ck6}4l%D!u$Eq^SOU zXU=@xa>#m_0>T}?UVnVW@rox9t$(Cnpl}(zEBt|=9c2$Ie5BasFjVi+UT~pc>UgJB zwt~ZuIa49429Av6U#-*f8*~I7L2RdCo8pw;Cc;Xca(Aa$$V^OfN_h5=1{(XZ0`#qG z(GT{c)lgw8&67S|*5;;W6teWR@K^@&7_EG36m)L3XJUYx2_%TRj}0qDQGFLKg&d6l zNm0=o8Y=Sp&o6lIJS51)--@GnMZ11GT3TZ^;F0p&193)WYx2xO=eEzuPDT2f zyFrLGMdQSAYJO6M6r;kkxtw0vFhLPMqEgwk;IoqCH{m1urF(vMM@@guc5HNachc={ zH_Z&UDuD25p6>;dXsh%jdobeOmr$D?HX@~(g4{;?Tv+=QKO2w7|3?|zA5HwV>P2x$Z9f?%Rf`A1mqAu=H=$ zrdN49`=Mt_T#BPlw41CkV=pkk0{b?f49S;aXI?gWyXR<#I!bwd}Cm1^9P|#rig3%AgD$oaqd`3qqSLZb=O1=<#mdh@3s=aXgj^BPN!* zcLsA0_^ecl_mr`4b6*O5x<-gIB}4i_f_OFS{&6yn^Br@~fo~Bp{2+|b*g=g=T~_xjx2ZXNl5^nC zn3bKj!R6z6Ec)T&>o#mZvkc-N({KIE>sF5lS-GNq;)?IOz0Wl%&@KA#YzM~ggmEPy zEbk!`lxEOqyP1bhTmLeZ%9aLY@jCvz%&@AsNEKmIU6#Dilv*YE^2n)vt$Qz8QtmeX z0&k9iP_7~#1ij2|fj`Ci2D?!@S#P3~DcJCFqP*huD^4^&#^JSvCc>4j1mVZi{~pD- zo*X50T^?oH{_>*KE=;@T!NPGxZyYqhhskMf;@qw;_FF!OvLS=CQWU#*-jXA+q(-)Cx z{g!ArFP?}%n%i|m_rGlAr@F3MoDs{pdKg`^Tzj_RqL3f8g#u6sZ8Wr(z za$fo0s;%-vD6Hb6?U0s>MYK=(SN6k;FdiC;owcntdL!%YY;7Lzzy8s$hI_|I$)T{j zUjvPY*utt{ewblZKQ1r7c&Oewqs_urxb{odO z0Jgb)oI$$AkkJ&~e${ zhC7qr@Ej4%cxkw6$m}l4$-PRx^p2j)L^vfbYqZXa^{>$$bRjo+0=35=T&h3ksa2nHHOWydl>sxyD zl|H10WXCDk9!ie6lVjzaS3}>vo&+#xgbsd7It(Is_Fm`z<`Zm#x2`*HyF8EQR9z8a3mRKqM$}&Q%z|J-wTc*YU=|Ad?zZr>WWj zNlztt8{)k{Uj5-Rfzitfw%T$!=f}x6xNTO!%uq%8zk&!7zTG=fV8b+nEP$e z)BdK`ktt{0J2z4O^zSL#sq1O|>Gi40`cdEV_e6O{14(1+KBmvp=G zsj2b!?=ekEDhDF#%L5S^0l&-Fr&BWWt>^;?CY4#=3>NI`1zLUkQ5AA^`(+eRD|#cD zSmx@_zS$I1uEj_@XWCy2J7=p{=MYVnGkv$ItaSK_zx(UMnv7Cp#z3T!F>2qiQm%Xd zK0Z6HI%;C?xxx)u>rnZZ@0}m)QGGqBbewjRII2mO<2G;`@uqJsbQ5l2X||YqiPOY6 z1t;sw6Xhv`JVdOKKP^`qZBYbyug^Z1P>mGg7pBmY8^E<-rO9*7xoqOAtih+65GrwM z-_$;KS9k7yib~lcTE4@*tJlGQh;bmy##-TakAM3k?uH-&GVsI zR{fqj5AW+3ek_}~ZpBu(?w!q<(mTHQY+Zzmis{DBOu;2uHa0qJliK>0 znuR>5?g4jG&U(YzmpFZD^WCQjnQ~xm=9H#&__`(ks*{I#m}q%>gZ|TALD1CfmlRj> zApgcYcc~%eulRL%1h=+h6rP6gWOE?{;!cdM^sX0fHG=VH^oGU!u=z%Bi_hT=f9su= zqTMx1jHPQmp(^O}sF)S9`+Uo={*QUpCEjVs;rMC)IZ=9v650nUzc+CSF zpyqX_e^@7Q6u%_%zJlqN!Va>@8*U%fnK9@c$1v~D_0))Oc+e^No|1G>=~jh%HGc<`_wtC z#GFz)YWbrnZ>T2}r)e{nw3_Mp__>R!8*?z3ds z#BF(b`}?N3r0s;c?!oxwh}LB)=C4x0E&g64o3j0jhBG$}LLG;_s;w<+6l8qPF8mOu zlPY~L&mnv)!5&mQQGKnuVpTArXQMiUv`A@z%ERl>b7{I~=J)OXO@qdliRZ3J!vC!+ za_w72l|c`xVyPw$C}1#>NhSQKQKn8@I9grxrBo_>jV#pte-ucjXu*r|`*VehJ8o|7 z)uW@b`Cp)#IeBjsJFrpWaytFb0YmB?hDc5>crNfp?$<*nVXuthrv6@1S%fYR} z59X;tX;`b*U;SU9n7ftqi?Z|UEU}qaRCK2_rN@y?&1reRK~G_sS;_U{eZSz?mbDq2 z>bV1PI?m0$>g`>x%r2eJO&F!fhtQ=mt{SU_H{kMWi)OSE(&}1THg|S*j!#b7Lompx zAGjwRFzuUu%ckk5JxUM%+!5%RnZY9TzRqw)QLQvYfJMgsp;g32*L>`EtE&Qf9(4OR zFl4zhyb*6+B$u|`Y$hL{ohif%PvlD5f!>M>Lbt&L26d|A`>W;T`;q$}p@;PYdv5(! zLrKgN>b{_tsHo?IEO^@use>xmx5_%f>T@GSt<|Wg^@h;%*!T8%^H{L#Qu-u8afpwp zrz9g!U31_uXXRqcVeXa{U0K7q$olazXfP;skNI`aeS-sfFKLl>e)ZlD+}hZ~BIDz_ zRb^#mO^pC~7nIiGAyPIgpo13P>|3*e4AOI+kx=;5*;&;ZhrWS<&g0e2DVa~R^QfPC zR|#(9E1x=_7PYmnGDjBDv>jp+67suq*O`sBx!4G_tJZGS6vgnZIb1@F;7o#n8t@Wb&GG7HUBoYo$T&E*o z2P_TLRvRVTkxg9bCSwo>9Zbp3vY$5#qfp5G0S>%d=}Ct8^2LCONNXzA)y>ZDhBL1( zgN*f>1nV{1b5-Q9sCfA`*mM6iGw;8bR#Tnmpd&h8r;Vr9a!&u`XefbUtu+Liag}fT zhLxQo4+)?6F~g87!qNAs$D&3VFR`73kYmo!J$HxwsS8Vxl(9t#)hEFj?KHi?=ka0p zX5ga=JD2|Hf=ye2;EPHc)p9-y8eY&C+cP1{8zD;Q^%^|yq~07!*C`!QI3&#=Y9kH8 zU1#VIYuH-K5`B5`+t4!uyji_EK-=(&-Xi7>=%{L-_VQl$`j%REmu$H8jx*_$D9GvQ znd=1}k}Om0e{JjzbY~u4UMi2sX=Cl|z)Skb=Bf@jnZ++`c#|33Kb-{tHAx}%)8GGp z9pv2a7*W)eEy*%HK&||_<2+Np7ZDb8(r!=WMRC~Gfo{58+w^1!*hvNmm7zxDKob8@8tkzuKLC77p%Ss8)5iy56MuMj*OP#wj7#o=?^Noafc zo83t(j+LVMb5=--o{f6-RJEtX>L^2uzS%2!adB~TX1fW<6no>Q*GI8Na>4(dTR9ls z2YkQnvcwQsNL7E(@dw39rSAWn3;0=7e2#oO{Dvcw zbgx`xGg>^3%K6IQ#vI>j%_hi{x-4hQ^@4bcI1w>Jhs?s48K@kv?-UCvU z7C`C3ccbt7tnaLKew;t&2i(cZ%$~XT?Af#TwXaFg)6pO$VkE-C!Xiazs@})K!T|%H zJ3$1%f2kLHjlcuj`@V)UR_!SB58w-)qms4~7FI(V@uf9B@Ez=-Y2uBAMb>xohdtV0RlNMO1L`IgNF^ss?j+bExmHiC@2d6)hT0{<%2M zntfHfS6f^AYSZ+3^ZK$>8pIw=p-ioWWCI@Y<(d%S0n(>NL;?@?L=FT7n>84NEiEJS zj-&x-S|y0YW3BfZd0D2Hf5g2Oq%$-MCqvL_4PNIdpv3>G4i zJRO>)ej|H{4G@Kl{(3V&FB23juZYxRycs1R3fe=c3AxLO07i6Ahn`{~lpjOE${;i; zFccetHwCH$V((BwuHZt!l^IZA5|TG#LCeJc4nhD70_uQ%mmbe=xHc#FuP22MVhbV8 z$qtB(f2{)a#<>la<9i$Z^QMJ<3?M==kc&Oe^&#P01?5B&*Z!_@kQT1;z{R9wbA`a5 zc3VawLz=faG}b5W_dP1x3uvi}tkFX{<*4lgvxhPqv1}g8#g)vBZ2vp$a4#)o_xHH^ z602{((9Fj^jzQy@shqAgF3HVhav7>GUQVYcJZGQ*seRiLFP)FFi=Ov+)XgL^?sjQ0 z{WYTItVKy8W<%stMAUz;JG8tw7V}=c!ZWSi^ic5r$UxghRvAu+9b};G#q-Y5^hgiT zn_Gcg^(^FE$q(P&XhwDNX2I5T@k~MI@4km1$6jK+$D82No7vcGWOxp_?F4J5GfUF~ zmZ01@1JJakmhzn=nu4*hB1Qg8HAl0Xg?bkWtjDPl#LHYWdYeL`Wbjg5M3LjK(%#)D z?To9;(FZw}tym=#`CQ=fU&DEKvxZsbkf-0Kwqh@<@V}-UbK9CfXg|0UXYsruE1dW1 zLOoAH{6UANX)Xg|Yxk$3EJTELe``-QR`;!C`>Wz^AI`-eMyUsXrTsM(`)s@mozgn@ z+h99_8W(l`LM0ni8*|%S;n8oZ`-`2iRQ<`z+v5d0@7vkDKE7>8RR(D{_;hDL)?)r| z{ZY97XAl41Eb0GYu2;zDE`73*R|l-4aK`@xt*|2Mi3CkzG8V3nY5%j}Q+0^3LXUZb za3*in*Db(w0sFr(O-vHh?!m~P%4d3vvdaH$gsvRC6IoIgv}Uhv=pj`Cuni0-fW~@& zQKv%TpR@QLczn;$uo*3qGH&yE)tL?0@PRsD!&#Ynj2e)ofHhJ<)AuCJ3tq&jGJ9~> zwjVDx&a;gkf!p`m1Z*1U@4up63O>@l$OrcDz~A*$hn%N4fd;l>yRZN32W*wac^rJ8 zvpxUyBM} zci>{bJ57Tn5HHu_Dc|wDyTZK!u}{IBpE7yY>ifu4I5HF+9eJ+)?1#;N0sMf{KL=6J za95e#GWY=BB5<8%xLAgcr`QMbe$H7y5O{TC>lm8)wN#naU*B{GBk_qbx!so^LSreE zC%FGPj*$Q@WnuYCjLP$GkAtr-mItv3nZw1W&AtVH@trau6m=Lzk%wY%1s}Zyv4&kX z0COhzIzONEvNTk>8*rF2X&}mb|1*!fn!r3l&$zBm2Ni6VIs)PASr6)W-_P5et>;SS z?lv#8ye#%(M1F#{wYTp&#y^Tkxu$t&X3ah1b@uaNw{uCU%5BTDp@)ZGqyXP|P;L2V zD2s^wGWnn915B2Bts+fcq`O+6t^-1N%ZFmhBIt>C_tYbo+spwg5qWY!5+wnvU~d_H zxt2=QW6bvU_W0bKvwBI39)13FZ%jf{v_i3O@WO14iZ2rr(^1_AAt6K*M&N%Os3o+F zKJ5Hk=`aBsV~S1OEkD>1W8O0AW8ZMT=!j-N>lKDjRD?Yew;ep=akRI58~?I)u$nkR zd2D=KSdmdj_CVG50ciPu9QdgTgvffd_kpflOm$rsV;r*=Xv*B*+!HDPzN5U3jH9tdi07E-qK}{^t{gOHAJgZ#0F)jh_Ne13UyI@nFuS4m~Qf z-_PJ!6jAIq_o-$U<%FwYSg*9oOPuY7}Fn4q95oZXE~e z532fCVBicDG6p#(7ni1K0^%X|lhV>Y1~)#{)|-Qs2l=e>*L|&wXf&qu`jSv9SLnm< zY0EUuG+79fYkfpu5!}}g&OEAH{08kq>#E04yXn#QOtSV;he3aJGlMtYivvMXo*1zG zrrBe^^Q0iMe!I4P!<#9O`IDYFPt&800M8L^hjrR$XQFRSeo?AQSyX^~G$g4Y{UU!M zxG|g`JY!VZPhf_d zxa#-o%RGtX<&I};H=aLKiYt9rfZr2QstdG>xM?@YfbiUXH(g+RP~1jp_I|-ji#hD_ zz~OL9>rN+i6?M7Gs-6rHQ}2&=##$z0U~8^RDYW z?v0_dh$mzTz}oM+sQp87t%OE@Omn8gBh;WaF4K=cxN+X}5Tk_45%=x4jw#L0 zlroV@`3$UHTO$tRl|7Ul}!gp z;`O`n4Zt!hXORwJy&Nb3JP(=t>B{BAY!SXUuXFs%f%&jTCy_l4# zC^fTyzhm>vDk};I-+*vSoZW6mBLo_2;6J()`Rf@ct&-eHvwZTDlR){ang;neSMC_q z>IR|7sHJ;M9KQ#SDjsK5KM4O#1#eS{Z@7uW0)`w*Lt^be=j5LeyUyh4>hTiB5Zg!C zS1tMvWf92d+~eLAUrXWwQ;KQWYQB)k&>0w`7MY59G^Ruj+j1l4{5oYXe%UJc^#E4D z2X3{nYq}NBcrnfFuA%XQF#p&{?86WbSm*FXtZL%l;ZBJp4&B)4o%4llV`nL(zX&|+ zE#U$mgf5b{-ovq8lVc(kW?OjiuJ89%tJvI#Ce9}|^xS(0ULc16n*1i3uhTYfUp(4|G1^eGWmiGO5$!&ns%2YaA;aKMa4~!h9T`(Ti?Qq{WM!I{3RR$GdR4}e0#czAg&xaHXkw7rVbT( zPvGP8Yv18xzxkI2Xc?c$tg13gu9sh5{Lh<&03_=Q65s?|_kS}H5E+|KvD1<(fH271 zM?F!Fhe~I>lTdtUiszdFy?1*{yRd@kXTk2WfGQW>U4uJ7Xr;Ph&1rm<{*<QST2e??Y7`agKy70Y#*h^uk~6cw+yX7I?)mY1OSx$Z62zq*S@Rj7M- z6lq2}bH$GXHivsQ-5s85(HKVAdtFZ{I9)`lG??*EURYki&Q=DE2uRb6Eq5S4xJr{-KLj8D&yZy!ytOzGr z@S#e-^&NRUz=WE7h&%7@{^SeTwziWt;7B_Nmwg~^++oH0KJPv;fql9re3HE8XQJ=f za%a@xbDO(1Z#5y#G0Ru9@atf!ijEw>Gu-}F&U$r7eBnfj&$!4BNu4pLW zEQL%jDaMcYwOcI3cpDAMoh(qrv4+BHTULqstlfLR=_))C`mk~oEIH3!K_nl~tw-@c zQx;!y8{;D=eB$tD^!Hj%qdf|r4(q{7oX>a2ciQVF4G($CGeOwM-8&hGl7N0bSfkY>|57!`abcLx^^Q^ z>>$|!owD>gU(4P55qM+>WpR{QgECRk3fl6Sb974ru__*(&kMCmw6FHxciq zaE#1pPN$l~Ew3OSrM|6zgy9cf-1{+on)}Ge*c$(c=VCEU?%4R+N6HR@QId|7x~Omn zXSU=u+c{=*cKGV7grqQ73S-Taq05tBB>JsefX5sriTNq7f5(}5OZyoJh52r#hl>3MMN!aRt3zndU4b`t79J19Mt6IY>VwJ@?-ju* zpCp&i=vi9sALxA~f$Z6RxJP7B$%Q$^z|H+;?E`!}4%1&#G*j0%Y)ZY~C~(<`n-t;8 zA+}GF5{)6=B=LNY?%h0)^%nWeQ{#o{83;D+G)77 z-LsGMti|y3ZASfWIN}%~?&Jx1)O{)aH&X<5IM02f@5IASf)sd`^?HJ z@Dk`mhu=pXCEAEvWbpcF=n29b`!fhi3bR((d<``rmmrk`l24*yUnu0_7`=+TgW4xY z^t3TY3x1}S&EU;W$Lzu7>!l?{yzFJBW%Nya`}o_7-{J)WjUy8C^e>MOTGn$+F{^Cg zcYC|7dK3~19x6wpZ>T#?k0LF%RjzooWXW8UyDks4WRHeLH~brxo^Iz4dN(in^^Hg_ zC;Pj&Zi%E3?*E)MK01Zn*L$_c*Col-PZ>+4D?5Hs+h7eu?8GFkqQ6EdWK|9Z5Ozi~ zc9VVnOAV5*z4F38mFwu>Y#5pLoF*A%4`N}`!Y*;i6)s^yU8goX&C8*GV^(6c^gwGc zriak>cxN`?@?h{_T!VG?3oj`raq!h)Ec}p%sT19cxl<~drDq`E8gP1+ zn!qzmkeC8ZyVG4Kv)6*lYH%=SFEMsGEGu-mAX_S%ATbN;UHOR9&UqPJdl%BNaklwJ zPex^L0BpP9fc?styvJP|7eZ3eu@DZ(6REIF3*LKP&v)#+hC5?DkKhB^N^8pAd6<+Aw z3i&m@;XhcEu0k$H4P<2GS04~dukqB$0)!g$k4+&w>*32ZCuvfYp|X}-U<=IQXP8@S zxVA(CqvF-~{kjetcee8$&EEA~TvNBlzMVT=?XP8x(z~yI*E#fgj!JD>0kQuG4?Pf+ z4>&!3EZjG>BXR1=tWkqmmC7M*`?d>1f>gy7J=gq2zrrLcSN7WX2iW77ZMaiKApQF} z5G6hmjw~%AsxMJC`ZNkXN5v48hTow>WL#6%Zab-PI~xH%bP33&)%BUiYG04sjy( z#oQpj`i@`ApX2!kAVSYuq;$2@mHq*3DvVsDQ3_v{O2g-QPo&(fP&OliGvJ-`DJ2w` zf=&Q?=haiIIWaAgDGMg}Z-bRrFU5A;Yw9&8zE+}U9dWno+)2UWjN)s4nuaWKpXdgp zP7godvE5r}b*NP4rfzg*zjL2C1OtT8-{2v2l4M8sMa6EZ=An#<%U3|X0X(h_O(x#d zig0F$w6*V_GXKWH3F-IZe=)f_F}&aGdMZQh_X}fInb!czW5v@z!j?KtSUk4hpe%w+ z$~SP8B!EQ2_OYxP=qEvR9Mc@NePA_!*=oZX>$oG6T^EQ@4c{b&)0Sqeo7 za%AfLeN=iKY#@adIzWjZdhGn%EK&~?r(@wgB0dv(u{*LR<@?jXEC3x7@S`$!=XOU- zBq}!BI!J*Bhm7!wRODgAa`kjM8-ku({xbcXkl7=g2FEhco?hC8hTMjdU0;gVCQTM6 zcvEHyGcG=bvE&*eLWSC?14lfXKvX#847a1++nNrCY66lFZL+eS$q3h&4L?Um$}Ab& zr64+6W4(^o5^9B(zHR$JYnrE4NT1Kh=Snho-U*LH?crawKf?3{3LxljchoNh)p6lw z;S6DOMKq#plzM}Ri1nomSpswQ?HA>MB!_XYZCgl`y5qA@`}blpJwcazIv6q|&wsVMT}dCynya|aU!g+9V`!%L(5(V@=t0OAx_6n1rX z_Hh!(YMv1F-GCKANmnp;l$`FMCe<;sk`-%LuK)b{z~@>*M6VtuHM5Cvv~WXK|g_dD!ru-TBsGVG77;k&5=UKsm&8xk^JSADnbRW*c3cRW7h7lZt{J*+oY+np2WB(v$sb0iw-g=VI^yQ zl%MbYBqoGLXxKmJU;ygF+drsQ<&?*W4S@J0XSTL9?P7+7Wp%4x;BL9sTzl-BVo z6 z!V$EmN1UcqM&D2v@#j}ISDb~n`V|Yb@TV&RHf16pK=2`lvhptC$&;!l)#S1@?496QxI7;k;Ag!KlV_{@}+) z-G_zj$TDuIh}7D${`2IMeWyyZ_vID6O86yo*+2cNZXtMgf-IhhKdZza$VM@~W9#== zaRQ|#u55u)ksgMD-_?@5nLX$^_I-+Ko^ZQ081u^ymNT?%-iTtt0b5sJo2l@i@$C;fn z&6kp1-`3{|oVYqtdN83oD2fa0x#qr<75yH*%7VZ_w<^C9l$_)NyI!WWOC6D=`9Iym z)Hn91Nm+3X4zl!>niwb1u(TF=D32Hh);REzkX+E+#ig^Tnw{~szLsJNb42ft+UjJX zOqlsrwvD8H_P8!O?tUV+K;diJZY#RLs*4`uGNxi zus)gGl_V>geM1rdBij3;!+p)SVr*IT<@LHkQ2`;KkymI7y{F^FZW?0u-DP=P>Zt5L zNYSN94@uK8*jv4g&?j7PHz0N0qCd3HY5f-7(Bmkngzl{+_E#C)oJ3M5)9J88`45gF z%yyJbEBq6Z+;|JtOFr_qAW?i;tYJmMblamTHe)j&D8$Id#tNp>=t@{Va5bIM4E_Fe ze10;Od+i+;m}N!v-OPs%wY8)YSjPH$XPdjE(G-0#kO67zrEfra#XVM$R$0kv>7Eog z2kN1@>K=+)6e?Z6*Oo)?+JNsdYd@9#4uw>*fg@pzjeKHSkd8QW^PR)<0m&L6m{DNI zsf`si)hb9o31&TB*ao_)gqV}{q(=j3U!FMejE9OAiNqY}CIg%m35t5giKyb6!T0-o zd#Zy~{?#FGbgA_c0aM>IIwU_~axHA=zR;xrxUP%H%nMvU>ySea^Fo1okzDe(y|UC^ zx8C9V=O7C2D*8*W^bV({b00(tN zCx_e)E-iVR9o7>fe$2Trmn!5KwdD4Ez9L=>n#}n%%AmTzyb3&^MgJFD>O5oUb4p>{ z-H8SirXn=vbQ3C6mtAm0DA&@>l2wxhiRCD|1QAMq zL-ZFYZ*;x&nUTpAO^q3km*m;y94}NKK9$Q_NrhooP0vWuHYIS3TQkSGsoX6n)JEJO zUp71SXL~ouSN%3zkGEhvr{UxcPYi9Bo7fZ9<#3c{ui-=Nc~@ICdH{$WHe`vZA{bhv zAYdRekcv}&dIxN>rX04Gf~swQWP?ifaEMjFglF@yz`Tlu*KAc+-bOv=VG!PXy|(4E zIC~7OsD0v!h;MIas_{+v^T^A^+;VNpZnn*GCOvXY5wVrK<5b6FvP!z#eyvw(*0z`m zrKfMSxf>Z|ndgL1a&{Fxof*(_N zVD(4JZ3ZA07GDk<+w5-M&nYx}^FWtBF@D_mSlml}jb+#|Sspl$N-eWXQ`Hc$M*_B{Ct6B_M@#&*CMxyp& zukt7}^{8z4WC~U|s3a|->K@qfEtD!At|p-ZH%+9Zbd+GJ1$s&_eNH_{z@Q+(s)3~- zU3O-4*cFv=Jr;b6&8inqi|mCi;l}8jwsy`YYoEOm(#KAcd#gc6?J8o^ZLea_{zdDU z@w<_;u!nacf{n#boF0SNC&rNE`-LPNV55rtxj|Q+xdNuKiMdoCe>!9`ltcw`@6>EV z6gFLU+xBxLO$vSJZamf+%VN{gQ=^HLcS^>&?BV*7?6FDym3e>CIKh`DWJ3aA=40_N z`TFZCqlY-Tbs?lcK)W_4e@MU&`vs+ZMV!wumU$PI{Bv_!82bxtylh1lo+wf;&R79m zzcnW4(+e0&2c4sq|E;IJCjd)yLAiFs!w&qsfDb}n8#}% z3@|70M~fK@Oe$m27pz-9BU`0;N}SgBYP#BfyBh^kmk;qeavGE3X+h-Ap4WvZH>_)e5j#B zdDfwh*NJx{C&OjH(X*+vwuSI>o%J4QPYw}Xeku<5V}Hu7V7`@3WIo&ModVQe#(V6# z2iYFIygCMc!@^Vj_RQdZ{l|SYf{KD&vB~-d;DrlZlier@UEf@O)Qnk6WZ`OwyP2o~x+TBzb zqastWfxVJG>i1THzO>=ht^8UY*ksu}^Ws>1faBGJLOTe%Dx~=1BlpzW_nM{5>uu>E zraIe>un=fsfGLyB2s8gr>C<;ON7VQE!Vwk%bv68H<@{D>*1nR|gFoDOA1YOjYvJtc z(cU4@k)tsVdZI;gJg24do;e`b5{{R)y&6K=k+9fue0yNg6D_^;n9kI(JK7}oSwtV{ z#WZ34-q3{t&x4lQ`Yj0YN<)9ZtYheGq=_GEj7xX8L7VM z-RrW0z*n#Y$bpnpO;LY!bp?*p6H>n#y3=8`nC~GUlDh6N3Dni(KX%!gxKykkxQq|CCyt^3vemr^K)4Dv+TN*VvrsJ-6x>$~)X6mdB#yYI9NSgK1Yr}=a60%TlV-pl)dCmQKKt$@N zqe4?6H<?(ImmLEFF{4pZ%(7gWjUP~~NR)Nidd|yn zAO$&gw+IVa7WAvG!C?-xv)Z(vAaL+0^HJhN+s@dd0#Ev^x80E$-MHjg{Yi={N|^Vb zdjQ^2)&wA!h+0B-D}rul~3_TkB6*-bQS*miq8o=+Q^@N;Wdd%k5p=N=-e^4Z*@*}Lc*}iNSQWTHp7jXTEt+b)qNW0l?;X5X zEd9<>MFwe)9M1{hCJO9GgBp}qM`Yzu0L7N$*JCP!|qKuRU_#G%qm%Rl64qDHz&)E^B~~LG$XC zJ_GRLkKG+dekQ?cy5)gdH0o>Rl%Ria5EOhB$w|)g6a+suwpvx)-gij49sXIUNnH>` z2`d^W;xx(Y(Ur2oSwqLMx^>@wvP!M@T2-|YsQ&1|ci$XDj0n%uSvdI`g7(v)0be#t zaf#`0cuI#;HMaL{e;TYr68}13e;vv9;Kg@*t9ttnG!OfM-L&L>$nlb-A;|o;lD>_Q za;5sy03NVgfKu?Kr@$xA5%D*~9D`oo_nP;z_|u?0OuVBz1u|X`AYyCHsInvqP7bMra0E_0@9cKW3*F&R*UoWJ#n%Pyf70DN~C#VZNEFD28rLb}$5 zQeOw?C6fC>?|xYp;Wcw<<;-Z*9Cn;61+@TV5t!`&iMM6T4v@#5h0&SbUF2(uuyc#t z^rhlYJ~BTHdP9Aci6=SwwmI$=LCssFFSh-$#Ab?B2!Zg`FQyGneO{*Ibn~Zt^=BJX zfp31sRSYCR0Vd9{1?R%$^7Wt6z+nDch?@d=vexgEnV|j7k@y|}{BqAB@A^GudQdC( zz{5Hhw>cjm-OSRHsfpx^mG+gs!#X0L89~i~vip~jE3w~}MoYknF8iuSxRqm4k^>DrK5#P3w`NzRoGwao|H;$Or`2P=VPW zU_tOTLvILtdEzcPv$~@v=-#a!0BklhAX#Y9kgD%t)y6g6Ek{X%EWkM67tj_yLE@2i zQL?nw6N|-Su)7D;udY)t!EmCw2H^CGE+H}Nxt2CRzE;8knMZ0Fv5E9qkzT@61SRFD z*tZZB9071;xIRN2UIgBa-x2)vFVFCuf6Rg<9zA6z#!ud@T1YFGSpN|(T76g{)8|w#vmbe97hTJjdwxuwXa#9CIT`! zGotTwo#G_X-H{MVd&x;nl>em=W{q>a92R!NVcI8r)}A~Gl9EOGqt{EOtvbpmikudP z4_icEeREM_d&dqYqsHk=K3Lqx*{*dlU*RL81|>#QeBd?O`TQ!hD)y82jRx*Q*AbPs*M2-gtkBLqqt;VPq(8668>sI~%vH_Sc+OtU!H=^J5H&0LxWUv> zF~oGM6AyI7b@qKn22IeIXHjImmuE+~d;}j>g2^vkYwmANSK(CP@35dl8^#nh}vHy%TRh>;TbCY0xEW2;YbW1n&Cw4@o9Y=+}xO-i1lmp`SNWR$dF5gK=$)X*?uDF%VQeW7grU`J7MCRm9 zu;N(ea+BX5jiQRAQ1kBdObM^cVV|-qY~X9gGW3;*UICl3ih1u8)1^Ieftqgr#sL z42xw~=p}?H_iJW|ni)-!<7%~Xu@^c^ST36sJUC7_@=m1+3h5QHD%n=oD90ydudUO7 z<|zA*KK;l(KN~w$KWQAp_-HJL&m9-w{&^1GiUXiO;=k-O%_quIR@#ItR2x_@$FhZS z{6y>u@hqyY{eX$sDr#gjTAA+|vmS((I4kzgF)mP1sX|`0zmCgbQ;#m{H3c>XjR6O8 zEYn%(V?f%5ol>`-E@Y%Ztj3{>@YU2ta7F1hqU=L@jNU}x!qejO9BA7JP3Km!B;y5( zdQkJ3hY3BuXM<50y~juB{zhOh{juX-Z-8bLCw?-$p6cnjS)TXG%jf+-bVE<->RQ`A z`ZE^R6#$e{JwCFd7XACie1bU9#6;^lljPN@Lq3wf*cgO`e!aPMMo{_-TH(=A)?7i8 z^5hRwZmq;!#s0Y$&A4xTwf{u3E5Xb2s?v)ZV1#eJL3)RT} za9vX+DvF^b_Tvedd?(DJ2^k>jn`&~z1)yMX-|K3e<#SrhXG_M1;gRr)Dw5=#miC0x zPIyz?>vQW#{Z~bJcNM#OB!wAsj7!DrRzp%$(7j9XOFk2){_BoL+0bNW4aON1fV4)C z%F~g(`F>!R8_|^Ng?44~;p0_B!b&R=EzO`hoTr2AOCTo4X^ znY>kXZmq4~(Eq`VAYni{dbRl3)u!}XyuY+0j>+?a{5sevOh%v?;7dftK+DWzuVp>< zT2TZ{#-vkoF1RmCh-80f#ePkpV{SV~;iB|4OemRI91Rs}r1-hP1ahC^4=mnnZHYf; zn~UuljbWCy^~G)DyysQCB_{Ef1FT)E8Le_hB)@0f25`0g#sG7bi3nRocbvC=RN6@8 z{na^8jPXu2q*-U+6O8-~6qCU`zi4{y|L!YzfkaxZ(@d~4C$LS8TN2jn#Zx>wSEKU! z2%WPcV19~>r~g9wp#~5y^ey(e7bIF2IB=0Rq-G&<9^-1L{ZL1DD1qYmqYirb?pyis z`@dDJALt`sOn=lTVpfg%_PM8HRA+{is?t;`-Ca~_T6?%igWYC=WdTHnY;B{gqLb!! z!aaq@^zKlwji4r^qujVE`19?E&vyu~E**m^7^Dk3WuV15A5Q~-^nFXv z*8BNLOHe!|(xnbKlE-h(RO$-VzeIOHc=R(A0DBww&+)aS@N=?DE|UP|0APGimH%3> zi5Lf5Ed~9dD{(yewA+k(vFC!oKACK2Bg@67tiWJ20}9A=HU12dD_s#y`it*`y6MmxLROXEOuBdEtNq}?0*^$} zGj%5HCI{?0T~6*|IJ|60wAe3;zVfiW4&{~YuUjO|R~5Z4PybHv@5b<~gNPFeiAA5j;@duclA>@8{-4QhC4!07UGY-+>b~fRs7>FTqkdq1|;dcx#o#gX9g;UOR(kR-l~C_+GdI0F73g#85ko~J}L0see&R1_D2 zs2nFa1U^8S3d#sVK-9z_JnKUPpW*DjYdAtcAosohd>FJXHUch^IEkt|2|F0;JDJD^sU)c?9CiBJLuLw`1nkd1BUhKZ7oG`5?)_2Pw56qo@u$qY zcH8u60aPeKR4BFSK^=sEFR%fdLL$Y`z=fXvQ2GB{h(OE1e!l~UOo$E^xPx9S5d8iX zE$J%RiRF58jQL7!OItX?sUkVzbjNYd+eBcLFZBg#K_0Kq4;OAcw~3QzFON6Ej*fL5 zyTq}a~CUB?|UF22$erigrJrZDFi+SUXTiPeE!%;ib-D&dfyR=v`Do)D5r86 zrs^9Ok*++AB{@8c5-LtoU}P_~HHsq+zoe7zFB_?w_}oV4(+GhCPIfAKS>m{WYo4Mm zyoQ35?^#^LpP3+cq`TC#Hh~2RsfTLi%;%=YPX?AgoRg^^3%BsY1@ZB#CK+ej+=?b^ zD8k#iV=zlSUoead_dc5sF;vyXREQ}&ie8wik_>&siz@hDm0EaLF_&@qeWXgrS~Sr| zI}%?>AP@F%PvCv$uynCBvzKt+ze@;~`lS3!jN*~wQbhmtGf^#6XpJ3|9Gu%Lolimj zgKr#o1G>V6v>Nq(sv)Uo|#cE}%q%wouiQ^R`7zLINE|8B+C2a}3-}qtNeXN2IA41zsb_ z`kDW^RDR@^%i3KkJ_R@usGmsHieGeoXYe46T#p#f$ckw-${|$Ra6gmsjJ}32^lqiU z5nPjSpBYKlx%~dqTER|6{1>{UI6d8BPi>)KH?P=^6#dw%*h*3u9Dscl$upXY*u8h`T=DM0N=6&@G`O~4A) zxf-8O`OCy?sdPY5F)mA~XvzCKxWm8z9`&uhzDIbXSz0`%(L+Ig0Wj^Rj{zvzO21o@ zjru=EO5qa3MblqtOsLanyAjQO)(kl}H|G-ZZbc_wDFV5u`ex?m2x04;gF?_u?7SZj zi|o%@_ESTzvtCY2o1f0wYP=pCJW<}QBryyrf{O~0NLsWdj2NMJKf`|;5kmvp<#xrE zzg)WwTi55I;7=5v)gt5b%en6$=YIN+A1HjdI67`5;SmvAcTblya{No;aKOH5gd&?e z<2I=({Co-a6;J{uX8cH951bGnVUT|(sOf}pI__mxRw5%IA>FTRyava|WAfZC8sdU% z2$1=o?X;YxMBO(1VX7VeO75_~AFtfcC7D=at%|(I5e%K(G*#^Z1yV_=mnV;p9t0i- zIRpR3hlUs{uj=bruMaWW^kIM{Oogt_kp_cG3Xo_uNh{uFA_BDf;2jd+h@_+J;*m+PBbCO`6ThmtYcZ;KdO zJK`}J1a>rSiZxm-8B_*YuQWE4XDsl)-jg-jthGvc9({{nj^5(rF?FW&7J zz9+=D*SoIEhy5(WzF;Ie1meQQf!FcgEo6A>E}s|hycT6MBKz}J2ytYwWc!0mdA6!(3%g?ILxuhQKhT^&iJ>noH= zV>Fx0#-7@901o=HCqhP0Zn85b@|z9}TBJC)56OnbJVseOX`oQI67210uflPD7- z90jMdP(Z;-j`J@j85fy&H0wyQLSCdOZMW74qLI`1XJWHv8HCJq@7pDgok*5Zr#|4Q zYtz~a9zc=K>i?z_S|jq&ZSUdg9iugO`a$ITd`UOYjuWkPhR_~6lgdCzS*}fII{vfU20Hqh?fm4hxb}gA$u|KcN~B z0&}2K0Xq@Hxf(c9U$yOgC`{q{efd3{UHdPlP4voHn35dtTx7!C>B<5R;%GYO3?}6v z5{HYV{+`frO6(Ypt``%1+X?gY^FBY$LIc94_jVL$y*pPl;$qctV(aB@1Jv^2o>!rs zg@xs>C%OUp`-CMzYiLd}U5`g)V$Fx+6qp9$%`GOgb;3$YGxXSHhD&^}_dC1>+A|5K zp)7gp?lY$H82t|c2=t7O;m~)zC!Pq{w2ZwnABv2}EW6q0E>qe_L+Odyxn4I!i6&4J-zNm`wDTon}+lQfh74GoQ~m7Kj~ z3ft!1vmBMX?Ffct%_%rCA9FNLY|tPn)v}WyOK&TgnYJThdcyBp6K$pI!w!A4I4l~~ zWFZPQEqiGCu4jixLhku>sZa_xx~XZO+a=R6^}GAaoy%k0w`HDqLZ?Z=Tb%$@!s}9b z1R-&GbxzTcrJ38`ndO!`jb5nvH_{92IZ{u{mQ`%n+?JjQQz%uC*kxPJ0#kczebi11 z+O8RBwt}IbM2drc&P?53XKs$U9!%!A9pr>C>i0s8rh!oIHhjDAT=|a6>wYFB!Nd$2 ze*7J{7<6Wb>|Sk$^n(zuhu?4jaYs9L$e!dEkR=Ks58{85%e1m}Mha)rK8*BkK5bkX zRVETNL)q{3?9kb<)(`H`wC%upyU~3!p00%#$|s3Q5gN)FX>ADF*{xqElc-cH0V~C_(OEu5X4WnMREO!*Zq@^!>&zpZwj;Kp!Y* zIF3o*X##au<#R(bJ2#hNZsoe+eW$Z;0z4Z=gyqww4V%^NQNIBZZgewB5qKbhJl5N* z-T-{5$r1kk=fgZ!a08D>>w{y6uJfGA{i&_5Wx#KBNoLi;1=c62tq6_`fHhY0J2?erZC6}k>m`&m z1NlB&zIk#zR33M7s5yjODiL=AX|VF}4;oYxi4a{NR-uA%m~gP9og~r-L2ytZ*F8K- zY$|3EF}mVUjRWBfiJtYy>~A1cbohor-VC4I0`NBzf&%dUIlQjLh{6`2vX z>X7TrN=4m$C={J^wY1O`oqbih&k$5e_#FvM$|10235}vBT%?cpxRgD33B5AMrvqCp zURrodE-rW>6DUWenX*XoswG$YRz>;09=v|_p_ikw(CqTf9wtaL;&X+czDj>JnN2hy z^*-{l`i&UL@}hJ9@TQV*)b$NZ$Jz|W%l)zL_^qsdK7FNh+RJ;rIcwmY&DV0|*Xaxj zwU671RY>&J1bR{5rR5rIdkxFr zDryIGEOcAt2pQoH>NF@eheyRW-;UJ0rr0`xrMEb-w+*lJ@C-VhN>Y&2SSgeEVN-=X zXV~pN6J3UY@7chd^viJx`PR-DH zWs&pv)G(>^@bPVgx~!@3GfvsZ*SG$%3Tqi*(ZrA8H$_d0%gtABZ8dKG;C7!5u>8-0 z+P(65ZzKzQe?EqaTeT>u8_tFOkDM0%FGV2wPKBt9&gQumIJ(au8|A(b6g6>T@A}7+ zx``82L0Ep$St0ew`3QVwqtNO3`sG^Vh&6m7)d~EPY*D+i4(mW>nwkVgumP9Dylf6L zE4_RRnP~}oMVVmkq{te{d!BpN@-%g|T%KNGrwjZ&3h4l4v{_MO=OEIKa8E)la z&;7OhnGhSCVJ}N{q5KJrcI@|fZ+a^m)gK>YrTxKGHjuuuCFra;ty;{L4sO~#E!@i& z_3x5|8S>-SmB&xD+%(X%f4k?=vt!L(p#t}Y5<{L1^Ev!gV+J{i;2U}V57^3i-(=x0 zW-V73|5Y+Y=^RtX@9pP=?o4IS4yLBgPNlgDgv0zz=>ML=k0lJBrf@KJaA6pPKEAWh zhBx~yb!il;2MMSbNcMwpbh?40)M7ZB2uNIAESt^5SlFsRLqR$Vs6y9wSPU$#(2Cxw zx8XgF3xIvtekue^^OU*YLFso`zf_)=;Otv_X6Ug=)+)uGB7G^Cj7V8lK114`xnfdE zdr?=1*G&`rchSRhU5+)8PdTNtBpZtO?)PgA2a%=I*pZr69i;9*4lbj{+{c2yJv+C7 zTN-?tOVTx zE7bfRIEDdhjgd)X-vaXOdd*%L>S<;S|0SLKbTcbq2tMDqQ?5oci~Ywaxe)>SJyQM+ zQ&EK#z-!ib7kzYAq10f4d&s<|ht=kpW_Ivzc^I<2@=x7jn4+%OXrwtIz0L4bIJ*Mv z9Zsx0R-NGv^VZEr`sLP7iIZa6e7V3alEezUP`4_ z=OQ!QE-=x}$V`g}2m%!Ej+oMx;_`aCPfQ2k(WT56;lZCSYu4Jlj-B`r}z6oH}4 z9}yn~I7#@rSk+$vCx|b1d~rd=sc{cL6#a7 zmAA)DbRw6FQOW$yu!_uA3d*^EX}OWesg^cZTvA)XEQI04qMFrj*ifn9ux6hzq!$!5 zI5?{BA}z10tJ~N0lFFBL{}z`mb_7Egd_T(vfg6uu(Bss{_&Qs|;~Ky-P<7UI8(VR`j@KP4*D%%(!5H$(4Uef&8aqY<_gG2n&{T1D$D1j3okW!$RkHeYSkQNfgE9wc@DMP0nu;#*|))>22U zEG+IDL0Ga@C^J{q4~=ot@?E*l!K9=BNwOj7mqV^GOAp#eBXI^qOjQ^Lq4= z%?;o4+i0}QZ@03}?S0Lctp3|2t2a(?GzvomHk~2>JJmHok??$1KqfI_gv66ldcEjF zS@eB-IaL+3V@LzpvSY0reB?iC-g);TbbuET`ZS`-Jgg}K4#j*g2MY%;m$3rv-eYIv z1pnJA^h^y@jwJ_n!-$YfQoWT37INxfb~~fm9Mb+p&IM zaLRwahI%we1WDfl)qF%P#`?i>;p2XK-%nXj7!u5{WrM3hg9{KFyQ6zn;Y#To_gH}?nJ)f5Hhr{sek2pon z0p%cEzCN7~0?D1Lva0HGg-{G?xmb4`%+#T`>3X}ndb3@Q1aa$z&(l&x04h-~0}I|@ z#y6Rdd=F6SwNM;8h;4=_tBxpkgM{5=F;28cLNR>=!?x|0!C|Gwig5b6aJ~B#Vq&o{ z=;s}_tgNhG*x0au#2;Obq&eUK-69&4~F$O!B&E%O*2^&}_6~qOwKdyVq+IKY*u& zsxuiI7do#n^_ZQXC#9nsTMuy0_^CJN`*##ob_a7AomZ%$%S+3R#BP)cBBI0fo1i~7 zB8#)-Kcnn&#GlGZ9mB28$94d`vwe^kGf-C5X)#b4REcqIoX1muE;_JZL1(E8K@`K$ z-3E`5W@Kc9TT@t_?ldjo5>pNO=Dw-1E>LRL2aVU-?}(AXYc|>R|NA>q03rTubGG5OchvD9loq;)hIHg^ zQ|QuVJ&AN3-0bM+s9AolPWmrs*yUA|oRi^|@$vvqy4nZYfI0wg)^nQzQqv zDUApZp9M++y*pT;^J?oAb`^Q~?8ocFJe#&t_6F}Kmo{-TIdAWdR8Ggj>G^W5lj^?s zMk^UjN^>hKWuUU8=HyHnC>S0eR|Raj!80#y2-D&H8e_Oc`ilR^tvH#fFx+CLdcUc8mRxp za39)c6U)sJKtNS2znlunlG6NVadR~1BIkteF4W;%rii1Ruk6Khe&k}vP-_^XI8#W~ z@b)Un`;fmzd4k~SiN4WnpVtZ$<0@KO>5o9ERLOV03E3u&dK-0fypZ;pr>dg8ou|Qk zh630rO2r%i+>uSz8?C|e=aDVe-{&O^p&wOssUAhL#QPwsTKtu4V-k6Om`H?v>xOjs zF{ypPzJn4WrKtEzQceye*YD-99}^|z?A-7Ol)9y|99vK?k@a1NmfLoLUz~na-E{1{ zSNb+*Q+eWLDrKrmyh7p^prH*@RnPit9=o+JC*#*&Gr+FyTvI`Jf94ouxa zfrJ6iUnl75b&7dc0C2jG*A7+t{0ox2<)wymUrzXNi`V!)b-8oVCw5);VDQ@d?R-tk+X> zAcol1hA0yG?!oJEHcNkbFcL3MQc9|yjvg0>4%&UTSYdA&fL+r)0AzG7n%AuhA0L1A z`Qd5~3ACCWj6%Hb(2?PJ)^a50`xxf|R8mph;@r_m5q*kN5J0tw6^dQs(j-5TR&-5P zgSBsabcjs z9RPVt%PLzXJN^yRc(yYkOhFeZ0}x9|dOE=z5S|RtUiUy<3c$TxC%ml#YP#M9nShz! z0GP3AhR0C}#X%$y0UxTv(BCD~EVd^Rj46s~&SmfJR5Tho4a(O%1brc%?oM;RA@n~Yu{ymbMwxdcGw z*0o%TeeQEQTzAnbNKaAe3{LrST=u@t{3;YZb>YZ#?#D{>DZ+;4mR6B0+!VbtPYUdOgfWca82;N56t2DU%=+s&~`tw zTUFnolOl;(^8?tq#c(_o9g633bV~6qz_c~q9eJmwrH$Qq1K>ysj0E`NnKr${?%{7z z*{@&d7z*Pg_)Qq6!0M0;PU+uQr*ut92iHT_bnWvIJLw;Q=B5b{z_VyiK3~pkC$n3p zKE8t+d!nu);ybgvoP@go4lf_W^Dp#|p2+3?Q=1t%PCXfjx_%P-=kT1m6IoJHvd2RF&Jqaa6<{NNI^_+r>xs{d zz+qtD>61+zGK}UN&{6}k$nfOsN+2Ir9!mqs{oUVBgAq1Oe;Q?(U_E;@&2%@(8f7g#`1yN#+QcF=sC)%>AV_x~U{;#({RMmd{ z4?h7~rHA&pO`R=F;uTnxQzSlHsj)tsY|j1`+i#D4+SY8_bd#5L&vQY3d3^vYZr=SR zl^rzX1rv*mAkhxs@h1Al^skxM!eLZ-f0h@@i3SU3jM*CQ^W?JlI1aLZ*iG=>WoKu1 zpDl4eR~*88Qfi^U4x;;bVQiYs)rDP=vLf_}OkEo0hcMsyPL99%X1*)Xtl8wD7Z=7ri{ z0?gC6`Qdk$@d)-+{u(&>-5(I6 zeZKHFUTbx>hDzuE2LST2z6Bw;JuZPkBcW(ycHJLf7R3iC-g%OvqoYxG%`PABkw?Hf z#Q*|lIh_T5xTGi(|7t(-0$bWPfNWKZ@9Bhv!0V#tyPTYDO|!;p0yZo3ZRgWjAHYSK zbwLukdXKS<@Fn`K5~>%jF=_Q?VR7j^AC2iC}s^!)N0wt9ij7a7~K*_-2l_1m4Ip(({z5j zfQFKjb1AfzY*dk>%kZ7^_t|Wpnxfve^dNwxBlW=lgoJOD_--_aa2zWq+<@;H>x>-& z3^|&i^Zw&IXEm1U{2OGosHfu#1UC~7|BG`VxW$e2Nm*D>12H8U5DyGq!)5%~kMEjD zO~tP({E6Y=d=R$XCntUa1W8;jC(3tlM4LxtTppupsY7F(R}QrSoL~0R01I_Pahe$K zWMpbWN=AN7Z}<9wc?e+hZT)M3;>9;JUu@ctny+Lx<$P;D=V4kK<5u~1mKa_d=&f?J zk+EJJd+%qsF2k~96;t6plW?nE{Ui}SAq%Yhm*9h%=cdq1X#!A^V%-FSJC8uNRe#sG zdKhiTZ3&jVyu2Lv=QLlTdtMQx%9o=eB^6=V;R!y#+l2fy82(@Um+QlGB>ARk;g_qc znU&s4m2ehO&SmX|Ek7Ol!DV`S`bJIeV1pNcIKo??udw`fOUBK;Qr<@QEhW(w`Pcr zR!~BpOY0KLYQx4IInxI}nSTTQOea8hAK=g|MN&p;jYq28uS^`3fVB5px?O;GAS(62 zxE0-cbhe|+*6yM)kYuN^;e?yW3C9*?D{i*%0V(TfUqIa$o2{Of+o^nVi+sF$IV#b8 znhif+2RG%SIH9w0gtPbkR!ClXIikC}`z=w9VbO6LI^Ns^tXsRjqc?^Jd9OQPtE5>_ zYV4$n%Dfw^b}z4}64pN@so1kB5gkd4qhW+t4XF6pBZO`l;l8E~+!d6)0V@ofaG_3rf4y_2b4 zH4)@J2M-z%!ZaL3nW)4v<*a)jhV(o>JnIC^d0J(N(Au$G^mVhnD&e6{a^HOQ7jb?o z%rbn>l{rQdc#OOg;gZn?%Y{@Zgmg$AppfS?OBwx66f=lf?DqC@cghPb7)p_h8rpTY zMkgy95dDabfuSvrW!aI+!lbd*%<;@tnbPFHPyW1bbbj3_y5Bts3`iB#^kH6oEaSo+!v&%)! z|0deI&{)&|mMI%8=GILzFRvGBvb9HDQMewlcCj`>4Q3mY&= zn7olnr4gjP)e@$iV=``OO27`~oX?GZ^jIqak1ti|H?WJxT!ps@A}EU#dGr~-%GKi{^D%n4?P z5xQ<$UxZa({)>gKV5(RNh`GN~exu6uf4DKSqOD?dCMp8DO7LEXpK6$B2urL3kd=OJ zrNEX`i91vNx??mx`uA7eb1O)_+5p-ICBQ(!;#`Ij zm#DpRS!=omHrB^&$98khBfP*mQ4VQ%5I-4HX76uJiE}ETH$Pff0wS;K7)ImjNhaBN zuWJOhBHbv$k==R-8ss1jQIo-Z5;8fOn_6_`MtfZlnS2{!*bK`J5#0BQ(K+in1)aJ1 zYvfPYf^oI6MA2TS(&5auL_kK7sK-(U$aRE3`q-7~aR!O9+)2hrl%m9$bR`P~sgbN%4P7XQGLl;Rc7F@e3LT zYXlcL#l?92I&v$Qr}DkCrK6jTdj5amO*`!XZH-NLsj*EHZl7pzS*UZ;83G%EbR;oK zy8Wk%fzRFD{x0IMaF0I;K~M|yy8~L%M3^DXR>YC?gGG78rJo$9En+8b^OfDa;3!LG z2J4&al{TrW1_Fe-)9g96w-tXfu|UW4~LbxdjjIL@Zo|Bqr`lSqN)=H$m?FiUMz%9@ruwGwLJkUwXIxdO}M3gP`$ z)D>w#4tgfzf`J}+&7Zh$jy{QN+p?{d)hs3uU5{9HcWbH_aiLDK|#1@Oi9{oqxbMW%~IHCu3A0AZv)cfG;zTnHrZOAex@zq(9` zcDFjKTKUnC9zzSlvYld(WVmgI6^e262P23s;n`+z#B!3k%vV=dRiV^TLT_EElI2lC z|E0J0j=^UehFVETN?M^i%l9;#b=*^58vJ+VA40S}2P~gAnnmno&9!@kVFb6~%Wp>0 zEN}L8y`SbV1P((aZOZecn2zDs0N2Ch`}%wl*02Gi8!jGE$30NYX+un<(%%Z|=CMv? za}E_Wj{63wO>02!gl_t{;`0<_|Csg#)@JMZYGUvM41u0)LjhSV1+n)v8ZxjgFMv(g zuP<)kP%8>t3$Q_|FpHKG7do6c6Av&p21tBd3AY~T?xu}pLi&O8xq&V6!*ecOM~Qiu%fp>I@lX0$7kMjl41XtDV=mTn!+;H%vLd29o1imlR_mwulU2jC?{7i zhjQ%}V=QvGifz;m{!$QcFcCD8G#i+%cYS|_bM^ZSblSeB$jw?R)=2_vat80w} zR;c^}kaF$hgb?*I_&!MOR1&e8p;os4lLTgg5x2h`w8_?d28whs4GlaU-RQG~4AU`n((=f+|LQHwShY&WdK}AAK^%T9;&Gjj);T4vjzx~G2(tyQ zkbn?VAeLGvnTMXRuiP+6`&0X4sS0WQ&Rf725ppN-u7|xe3Xim7psvSx&)|2OfLaij z--lzKBVh|8(idsfyANyqcF;TGFa=1$nafOKyxZ9x&q!ZkiVezP#mCIGuEna=wU zmByogzi7(xt?UFm+t!Nx(R?3@=Amd6fJ)nau)#bUORG3kBle*Liq%l=^L7j=JZY*E zY-_^p#ub~oCWE;5vbecM6Twkf_{X8)*V|+!L$FB7F+c>YUZ295g3*F9nruHsU}0mY za?WP>8@vK_4sJ~}8;8zB2v)R0h^wso!hw5^%l%cB|KUuIZ;5KF0%(&x=k?xDD*rI- zC+9R8^dQ1QrFH~Oua4(h#=RF`y8Qs&z;FaP`!USrJro7T0^5) z{W@EwszJ(Y_LF>?`Gq<+TVN*lP!iLIHZ~z4==J4EyhDCrZmt1s4*;|_)LXo7I?iU5 z0yX{rY>9oL$>C1pWnR*1|UeV>3$ump@qWUWQK|5tT$SJRxMYbA{}L8W4m7-Z}SE8 zETp`=9is6MsVTjyh|rHfrX2-WvzN@r^7Hb;H}tppbsURYmkJeK_Y1u;8R*^{2Dd)y=(n?4pLc%GJ3uqJx?TeJ3JC*jZ-G&S`q z4jh9M!uw!wLZ^{5@6e!Fa)!tkaRU25G$WJST#hWDk}Bi{hpE;?W5-hDU|DRB;_WTo z%*eIbRyxaFz1+213U0vpnPx@3c7eoDbJkgGFf3%nb8)#xN>$3PD~gxALI9S6vG;`b zTZYpwZ{?FoaCyoct)hdTD^y+^)pWWKHWI1KEpL#Snm*&w)dVCFirpXb`DE+*TJty+ z^_|lu|Gy)m`OqXHj3uzd=>M*+?1!m}k{|vg9Xuh;I1C(nPF6InhnJR8lgUeS`~x^S zG7tzvA)0E3%q|+WulH~F4+T~Dj(+IZWW?Z^b=);VI#L-STZN|Jo%*+zjcy>SMXiZp zjJl5>s_iDI`C-Ub>mVrjtIv8;#ACD7dDssl(HWb1JpgDFqc?y%czw1^Q7TL?RTLTo z2%%I1EU)IAV-ED+(I{B;xerSS@bJ=#ab~3LLNd4yqnDcCMPuVBH0ZI~`1;Hc#F~L^ z8032HKr_KWkBRF-d4N5}l%L525wI^5EGg=C0L>K;2?E3?Dm!q`4-J1@K!p8Me26n%6!Cg?beV*U%YiE*$Vu zXX1oj0Ai>S%FAA(&IHNl@$ef!+%?oBQcaf=0c>BrG}UZcVHyWxA}2BKr`O(Adq#KOCe-;B!Dun@xP`N2OHW`;~M$VA6QLLNi31$o+R@Ql?!m zT;XQm;7$)v3NpH!s5=V3?#9VUkDmaL)+d1AsSBILjy-cvHi_?p$Ls}Gl7g8|&}GuC z+MLYoTKPbL=$3~y_T9O)Jx?VcslHgdZ$FEM%_>>(2Mo>4x~Gd_pxp?alMJQvinYrR z)G_H4W>NF9<@F9|J12BBcmk@A!ukZOVGbRS!vc$LzYlxolOXMcl$4UQ z)?@k`fcYr{5T|)QAy^#RxIgGAfBwuj6w+B0RA{s{x$2usS}&y?2fQ`;geu+K+_>0Y z@6hK^$Z8+Iz9e+Qp8z_?J`~A~nuMbn@e*<3hR;D}MXo{Mnc{vA+92mvumy;dHiJ)M zOvAQg9jsJdYonEY6m`K~TF`HJ!(=#DK3AB=eQ<|YR>;t(($OhxtS|{Eitf&MUB1l% zgtIDuucfE*dAD*soz$rSfX`^uyX7#90nF1?D^5K{OkA=c7)qwaT2P1Bsu0rZvVjnc>A_h@`b0xK3 z!3xegR&-;tPjL`lONW%w)Z!U7$8@GSEMnewYp!WbBA>kMr%0_HQ$&7CQ1*&yP8Vv2 z(jI)=zdNQ)50OdtqZf{bRUyHIGhG!AwlX#ib_kumV+u{{!(Mi(k20W9f1}vgQRy>lqKb& zbsHheCstDV5{l>)x$v*Z!^fZMRWN4R-JZYe<#ZvpJzH43EbE+W0|I^yG;@6%k^DAU zqx^#{+#4wdjfYxJegMR2Bm4axkUvB=G;n4}qo=@j$asC?5hbe`&MI*sLQ!=&<~}c2lFclmU&88w-tkzwaEGN!bW^PCYE$r^7VH(e$U>%gsUrS>?l*()W{jtC@gSZx|O$Hk)xC_gM$JFZps;h?S_n4z@ z%fs11eYN$0Ty=;@NJ>vhvRS!5#3WeTF}C&T5T z<3y>)uW6g&TQCE^Q`FL0!0dh8PiE^+{a_-!wlrf$-P5uUR;p4@R1$gbZ-Rx4rJ4;? zX*3&K^{V@uN>5$O?ExXXY}$?M7$Ze!{@xN0DN-rV&2s?(AOv8`PaZG#d23TL7M2y% zI_aNo=QV!~iM)?P6U#7bw9Km4^HFb#)berGhI#V@D{+N@pXj@6BIh&!3Ix!EBIi(O zXmn2(03vs=%`wvFlW5uA^zBjh0dqYk*AasE>3X0=V&pMQ5cAqnZ7f@>QjY3a-bD&G za{@M%SR4a~x&f#p_Z{{WA9?BNKX<~?{iTQPJqrUIo0Hm%bw<1q97xIMESiwp@3^XI zPkbUBj_0wMk8H|SGLUlMT+}(3N-ZK7UeBTvC3h|WRa>NpddhkXGt0@}aWvKD^mg)S zZ?>| z{)-5+AaU)^C;g+y65QRJl_(%@N2p=?;<=#Rz47{q=N6aIshACU>^e^|Su6C@FP>s$ z_|ih$VU(A$<9nc-M`^9v%GU*AamW|EET||ND~ue+__e@%90u XS@P6Li*ys{goKa~l@+NJ(hK-+MA1uw diff --git a/docs/images/visualised_graph2.PNG b/docs/images/visualised_graph2.PNG index 42f410e30bc80b4b21e20e74d6d8f294b3a81e78..9ea9c77db26cbd4905c6d1d4c10b339cb1be4046 100644 GIT binary patch literal 14341 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

#_*cf>gr{t0XQT5n(QfZTCi2Q3kPp;d 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 From 93f86581adf28e2191618cb6c3bbf6826666cdda Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 17:21:55 +0300 Subject: [PATCH 059/122] updated alternatives to accept a name chosen by the user for the variable that will be created --- tuneit/class_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tuneit/class_utils.py b/tuneit/class_utils.py index 1762277..209fd84 100644 --- a/tuneit/class_utils.py +++ b/tuneit/class_utils.py @@ -222,10 +222,10 @@ def args_to_kwargs(cls, *args): return kwargs - def __init__(self, *args, **kwargs): + def __init__(self, var_name=None, *args, **kwargs): super().__init__(**self.args_to_kwargs(*args), **kwargs) self.default = next(iter(self)) - self.var_name = None + self._var_name = var_name self._closed = False @wraps(dict.update) From 4203d8c4f645f643ea6d23e0c609b52aecb8b00c Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 17:26:16 +0300 Subject: [PATCH 060/122] updated example doc --- docs/example.rst | 275 ++++++++++++++++++++++++----------------------- 1 file changed, 141 insertions(+), 134 deletions(-) diff --git a/docs/example.rst b/docs/example.rst index 35a0d68..8a06fcc 100644 --- a/docs/example.rst +++ b/docs/example.rst @@ -1,8 +1,5 @@ -Examples -======== - -Example 1: A simple tuning example using sparse matrices --------------------------------------------------------- +Example: A small tuning example using sparse matrices +===================================================== :code:`tuneit` is imported as shown below along with some other packages that are used in this example: @@ -14,28 +11,22 @@ Example 1: A simple tuning example using sparse matrices Firstly, a simple graph is constructed, which computes the multiplication of a sparse matrix with a vector. The graph contains one variable to be tuned, which represents the different formats that can be used for the sparse matrix. -The following function creates the sparse matrix and by using :code:`alteratives` more options are added for the format that will +The following function creates the sparse matrix and by using :code:`alternatives` more options are added for the format that will be used to express the matrix (available in the :code:`scipy.sparse` package). .. code-block:: python @alternatives( + var_name='which_sparse', coo = lambda matrix: sp.coo_matrix(matrix), csc = lambda matrix: sp.csc_matrix(matrix), csr = lambda matrix: sp.csr_matrix(matrix) ) - def create_matrix(matrix): - res = sp.bsr_matrix(matrix) - return res - -In this way, we have created a function :code:`create_matrix` that expresses the given sparse matrix in an appropriate format and a variable -to be tuned. The range of the variable contains all different options that can be used to express the sparse matrix, which are included in the -function above (:code:`coo,csc,csr,bsr`). The default name of the variable is :code:`which_create_matrix` after the name of the function that -creates it, but it can be easily changed as shown below: + def bsr(matrix): + return sp.bsr_matrix(matrix) -.. code-block:: python - - create_matrix.var_name="foo" +In this way, we have created a function :code:`bsr` that expresses the given sparse matrix in an appropriate format and a variable +to be tuned. The range of the variable :code:`which_sparse` contains all different options that can be used to express the sparse matrix, which are included in the function above (:code:`coo,csc,csr,bsr`). The graph takes as input the matrix and the vector to be multiplied. One option is to create a matrix and a vector at random: @@ -55,12 +46,12 @@ Or just create two generic data objects, which will take their actual value late about the new data object can be given using :code:`info`. As shown above, some characteristics about the new objects are given by the attributes :code:`shape` and :code:`dtype`. -In addition, the :code:`create_matrix` function constructed previously can now be used. The new :code:`mat` object created after -:code:`create_matrix` is called on the object :code:`mat` (created above) is a tunable object. +In addition, the :code:`bsr` function constructed previously can now be used. The new :code:`mat` object created after +:code:`bsr` is called on the object :code:`mat` (created above) is a tunable object. .. code-block:: python - mat=create_matrix(mat) + mat=bsr(mat) The final graph :code:`mul` that expresses the multiplication between the vector :code:`vec` and the sparse matrix :code:`mat` is created as shown below: @@ -71,6 +62,17 @@ as shown below: .. Do I need more explanations about the finalize function here and why it is needed? + +Furthermore, we define a random sparse matrix and a random vector that will be used later on when actual values are needed to be passed for the :code:`mat,vec` objects created above. + +.. code-block:: python + + matrix = sp.random(100,100,0.1) + vector = np.random.rand(100) + + +Visualize +--------- The graph can now be visualized using: @@ -81,13 +83,12 @@ The graph can now be visualized using: The result is shown below: .. image:: images/visualised_graph1.png - :width: 400 + :width: 450 -The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a -fixed value yet are shown in red diamonds. +The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a fixed value yet are shown in red diamonds. -Note: Each node in the graph is represented by its name (such as :code:`create_matrix`) concatenated with a random sequence of characters, which -is not shown in its visualisation (for instance :code:`create_matrix-ad93fc5283b9d3a0963c3e65eb5055ff`). +Note: Each node in the graph is represented by its name (such as :code:`bsr`) concatenated with a random sequence of characters, which +is not shown in its visualisation (for instance :code:`bsr-2b53519cefa68a68788760b169fee0b4`). The small indices included in the nodes of the visualised graph allow the user to distinguish between multiple operations of the same kind (e.g. multiplications) and to find out the whole unique name of a node in case it is needed in an operation: @@ -97,112 +98,19 @@ For instance the following code should return the whole name of the node that co mul.graph[2] -For the purposes of this example, we would like to tune the variable :code:`foo` based only on the computation time of the multiplication -(i.e. excluding the time taken by the function :code:`create_matrix` to construct the matrix). In order to achieve this, a link has to be added -between the multiplication and :code:`foo`, as they are not currently directly connected (:code:`foo` is added as a dependency to the last node -of the graph): - -.. code-block:: python - - mul.add_deps('foo') - -The new link can be observed by running the code: - -.. code-block:: python - - visualize(mul) - -.. image:: images/visualised_graph2.png - :width: 400 - -In addition, the :code:`create_matrix` node in the graph needs to be marked as one to be precomputed so that its computation time is not -taken into account when the execution of the graph is timed during the tuning of the variable. -Note: In the following operation we can use the name :code:`create_matrix` for the node only because it is unique in the graph. If there were -multiple operations of the same kind (e.g. the function :code:`create_matrix` is used twice in the graph), then the full name of the node would -have to be used. - -.. code-block:: python - - mul['create_matrix'].precompute=True - -.. - should I include a visualisation of the node marked as precomputed? - -The only thing left to do is to actually tune the variable by calling the following functions: - -.. code-block:: python - - obj = optimise(mul,sampler='optuna') - -A tuner object has been created by passing the graph to be tuned along with the sampler to be used to the :code:`optimise()` function. -The :code:`optuna` package is one of the options that are offered by :code:`tuneit` to be used as a sampler. - -.. - maybe include a reference for the optuna package? - -Now, the tuner object can simply be called, while also passing actual values for the sparse matrix and the vector. This is necessary, because -during the tuning of the variable the computation of the graph will be carried out for the first time. Each time the tuner object is -called, the tuner executes one more trial and it returns the value that was used for the variable in that trial and the resulting computation -time along with the best trial executed so far. -Note: A trial is a single execution of the objective function (which in this case is the timing of an execution) using a different combination -of values for the variables that are tuned. - -For example: - -.. code-block:: python - - obj(mat=sp.random(100,100,0.1),vec=np.random.rand(100)) - -.. - do I need to include a picture of the result here? (what the tuner returns after it is called a few times) - -Example 2: Crosscheck and Benchmark ------------------------------------ - -Example 2 starts by reusing some code from Example 1 (explanations about the following lines of code are given above). - -.. code-block:: python - - from tuneit import * - import scipy.sparse as sp - import numpy as np - - @alternatives( - coo = lambda matrix: sp.coo_matrix(matrix), - csc = lambda matrix: sp.csc_matrix(matrix), - csr = lambda matrix: sp.csr_matrix(matrix) - ) - def create_matrix(matrix): - res = sp.bsr_matrix(matrix) - return res - - create_matrix.var_name="foo" - mat=data(info=["shape","dtype"]) - vec=data(info=["shape","dtype"]) - mat=create_matrix(mat) - graph = finalize(mat*vec) +Crosscheck +---------- -In addition, we define a random sparse matrix and a random vector that will be used later on when actual values are needed to be passed for the -:code:`mat,vec` objects created above. - -.. code-block:: python - - matrix = sp.random(100,100,0.1) - vector = np.random.rand(100) - -Crosscheck -~~~~~~~~~~ - -The function :code:`crosscheck` can be called on the finalised object :code:`graph` as shown below. The function returns a callable sampler +The function :code:`crosscheck` can be called on the finalised object :code:`mul` as shown below. The function returns a callable sampler object. .. code-block:: python - obj = crosscheck(graph) + obj = crosscheck(mul) If it is then called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object -will iterate through all the possible alternative options for the variable of the graph (:code:`foo`) and return :code:`True` only for the ones +will iterate through all the possible alternative options for the variable of the graph (:code:`which_sparse`) and return :code:`True` only for the ones that produce the correct result of the graph. The :code:`crosscheck` function is basically a way to check that all alternatives options return the correct result. @@ -212,21 +120,30 @@ the correct result. The result of the above operation is: -.. image:: images/crosscheck.png +.. table:: + + ============== ======== + which_sparse xcheck + ============== ======== + coo True + csc True + csr True + bsr True + ============== ======== -Benchmark -~~~~~~~~~ +Benchmark +--------- -The function :code:`benchmark` can be called on the finalised object :code:`graph` as shown below. The function returns a callable sampler +The function :code:`benchmark` can be called on the finalised object :code:`mul` as shown below. The function returns a callable sampler object. .. code-block:: python - obj = benchmark(graph) + obj = benchmark(mul) If it is then called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object -will iterate through all the possible alternative options for the variable of the graph (:code:`foo`) and time the execution of graph using each +will iterate through all the possible alternative options for the variable of the graph (:code:`which_sparse`) and time the execution of graph using each option. The :code:`benchmark` function is basically a way to compare the execution times of all alternatives options of the variable. .. code-block:: python @@ -235,7 +152,16 @@ option. The :code:`benchmark` function is basically a way to compare the executi The result of the above operation is: -.. image:: images/benchmark.png +.. table:: + + ============== ============ + which_sparse Time + ============== ============ + coo 448.800 usec + csc 663.900 usec + csr 1.796 msec + bsr 1.551 msec + ============== ============ The :code:`bechmark` function has also an argument called :code:`record`, which if it set to :code:`True` allows the execution times of the graph using alternative options for the variable to be stored in a :code:`panda` dataframe. In addition, now there is the option of also comparing @@ -245,9 +171,9 @@ time of the graph for all combinations of alternative options of the variable an .. code-block:: python - obj=benchmark(graph, record=True) + obj = benchmark(mul, record=True) for n in [1< Date: Thu, 13 May 2021 17:27:21 +0300 Subject: [PATCH 061/122] deleted files --- docs/images/benchmark.png | Bin 2927 -> 0 bytes docs/images/crosscheck.png | Bin 2229 -> 0 bytes docs/images/df.PNG | Bin 17719 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/images/benchmark.png delete mode 100644 docs/images/crosscheck.png delete mode 100644 docs/images/df.PNG diff --git a/docs/images/benchmark.png b/docs/images/benchmark.png deleted file mode 100644 index 9632a3b90d03225edc8bc419752fbff30f2ce619..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2927 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 diff --git a/docs/images/df.PNG b/docs/images/df.PNG deleted file mode 100644 index f42f66c27e32023d9a0fd6b4dc60f0999bdf627f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17719 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 From 37521aa47cae9c0d9aefb0475f5a75fc67b438a0 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 18:12:41 +0300 Subject: [PATCH 062/122] updated example --- docs/example.rst | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/docs/example.rst b/docs/example.rst index 8a06fcc..4228224 100644 --- a/docs/example.rst +++ b/docs/example.rst @@ -83,7 +83,6 @@ The graph can now be visualized using: The result is shown below: .. image:: images/visualised_graph1.png - :width: 450 The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a fixed value yet are shown in red diamonds. @@ -172,7 +171,7 @@ time of the graph for all combinations of alternative options of the variable an .. code-block:: python obj = benchmark(mul, record=True) - for n in [1< Date: Thu, 13 May 2021 18:13:13 +0300 Subject: [PATCH 063/122] added pandas to the requirements --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 14ef113..db96e49 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ "tabulate", "numpy", "lyncs_utils", + "pandas", ] extras = { From 77c626c6fbd81710a0357fb0085addd7fafe30a6 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 18:13:39 +0300 Subject: [PATCH 064/122] updated plot --- docs/images/plot.png | Bin 11820 -> 18080 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/plot.png b/docs/images/plot.png index b29555eea01c4a200a9f67bc728ea1e8b788f2a6..c443934603c56c71d128d2f3bd3282660e945c48 100644 GIT binary patch literal 18080 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 byT7NOAYUu+jTpRD=H6=wdGP{KeXsuoQx9m{ literal 11820 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 L%ZcSa)Ajye!hmnO From a1e6477d1ab8bfffe9432e1f185bee3d997abef1 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 18:38:53 +0300 Subject: [PATCH 065/122] updated tests after get_attributes function was removed from optuna.py --- test/test_optuna.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/test_optuna.py b/test/test_optuna.py index b415d2e..2087875 100644 --- a/test/test_optuna.py +++ b/test/test_optuna.py @@ -55,13 +55,12 @@ def searching(array, element): assert callable(obj_A.callback) assert obj_A.n_trials > 0 - # test get_attributes function - assert obj_A.get_attributes()["callback"] == obj_A.callback - # test get_study function study = obj_A.get_study() assert isinstance(study, Study) - assert study.study_name == md5(dumps(obj_A.get_attributes())).hexdigest() + name=obj_A.tunable.get_info() + name["callback"] = obj_A.callback + assert study.study_name == md5(dumps(name)).hexdigest() assert [*study.user_attrs.keys()] == ["callback"] obj_B = OptunaSampler( fz, callback=callback_function, storage="sqlite:///example.db", n_trials=10 From 09f0e0bfa41fb4ef6c9971c88d4cf121081efbd1 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 13 May 2021 18:39:26 +0300 Subject: [PATCH 066/122] reformatted --- test/test_optuna.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_optuna.py b/test/test_optuna.py index 2087875..7df9db6 100644 --- a/test/test_optuna.py +++ b/test/test_optuna.py @@ -58,7 +58,7 @@ def searching(array, element): # test get_study function study = obj_A.get_study() assert isinstance(study, Study) - name=obj_A.tunable.get_info() + name = obj_A.tunable.get_info() name["callback"] = obj_A.callback assert study.study_name == md5(dumps(name)).hexdigest() assert [*study.user_attrs.keys()] == ["callback"] From ad3d99a3327e7ffb9d774abf66b556103b13ec9c Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 16:29:46 +0300 Subject: [PATCH 067/122] updated index file --- docs/index.rst | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 1689cd5..75ef1ba 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,12 +1,54 @@ .. Tunable documentation master file. +Tuneit: +======= + Welcome to Tuneit's documentation! =================================== +.. + title needs to change + +Basic Concepts +-------------- +The Tuneit package works with computational graphs, which have two main phases: + +- A construction phase, where the graph is being built. Every operation that needs to be performed will be added to the graph as node along with all the variables and data input and output. Each type of node is visualised differently in the graph as it is shown below: + + * Variables: they are represented using diamonds. The outline is red in case the variable does not have a value yet and green in case the variable has been assigned a fixed value. + * Operations: they are represented using oval shapes. + * Data: All data objects are represented with rectangles. Most of them represent data inputs, except for the last node in the graph, which + is represents the data output. + + .. + add image images/ + +- A finalization phase. After the graph is finalized, a number of operations (described in the next section) can be performed on it. + + +Key Features and Functions +-------------------------- +Once a computational graph has been built and finalized, it can be used in a number of operations. + +- **Visualize:** Using the :code:`visualize()` function the graph can be visualized as it is shown above. +- **Compute:** By simply calling the finalized object of the graph, the value final of the graph is computed and returned. +- **Crosshcheck:** The :code:`crosscheck()` function will iterate through all the different options for a variable and return :code:`True` + only for the ones that return the correct result of the graph. +- **Benchmark:** By using the :code:`benchmark()` function, the computation times of all the different combinations of options for the + variables can be compared. In addition, by using the attribute :code:`record` of the function, all those times can be recorded in a + dataframe. Furthermore, the :code:`record` option allows for comparisons between not only the execution times that result by the various + alternatives for the variables, but also different inputs. +- **Optimize:** By using the :code:`optimize()` function, the variables of the graph can be tuned. Each time it is called, it returns the + values that were used for the variables in that trial and the resulting computation time along with the best trial executed so far. + A trial consists of the timing of an execution of the graph using a different combination of values for the variables that are tuned. + + .. toctree:: :maxdepth: 2 :caption: Contents: - + + installation + example Indices and tables From 9284700476d3d98ec7f6d75e38a525c021399ad6 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 16:31:00 +0300 Subject: [PATCH 068/122] added option in get_info() for short names --- tuneit/finalize.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index c2a0737..4e622bc 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -105,12 +105,13 @@ def get_key(self, key, ntype=None): return matches[0] - def get_info(self): + def get_info(self, short=False): all_info = {} for data in self.datas: info = self[data].get_info() for key in list(info): - all_info[f"{data}_" + key] = info[key] + name = data[: data.rfind("-")] if short else data + all_info[f"{name}_{key}"] = info[key] return all_info def __copy__(self): From 3ae6e6b601df6741c2e8b8f8e6a5944743905b15 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 16:34:05 +0300 Subject: [PATCH 069/122] added data output node in the visualized graph --- tuneit/graph.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tuneit/graph.py b/tuneit/graph.py index 307fc8d..12e9327 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -300,6 +300,14 @@ def visualize(graph, start=None, end=None, groups=None, **kwargs): continue dot.edge(str(dep), str(key)) + if key == end: + dot.node( + "output", + label="output", + shape="rectangle", + ) + dot.edge(str(key), "output") + if groups is not None: for (i, group) in enumerate(groups): with dot.subgraph(name=f"cluster_{i}") as c: From 7ad7c4d64ca65905a2acf83f99a713fd6d87f451 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 16:37:01 +0300 Subject: [PATCH 070/122] 1. updated datarame headers after get_info() was changed, 2. added *args and **kwargs in run --- tuneit/tools/base.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index 97a661c..8f99b30 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -86,12 +86,11 @@ def record(self, value): if value is False: self._trials = None elif value is True: - data_keys = [ - key[: key.rfind("-")] + key[key.rfind("_") :] - for key in list(self.tunable.get_info().keys()) - ] self._trials = pd.DataFrame( - columns=["trial_id"] + list(self.headers)[:-1] + data_keys + ["time"] + columns=["trial_id"] + + list(self.headers)[:-1] + + list(self.tunable.get_info(short=True).keys()) + + ["time"] ) else: raise TypeError("Value is neither true or false") @@ -174,8 +173,8 @@ def __iter__(self): ] yield params, result - def run(self): - return tuple(self) + def run(self, *args, **kwargs): + return tuple(self(*args, **kwargs)) def _perform_call(self, graph): value = graph.compute(**self.compute_kwargs) From bf3fe80649993ca2c86badd075ce373b9642ace7 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 16:39:48 +0300 Subject: [PATCH 071/122] changed how self.n_trials is assigned --- tuneit/tools/optuna.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py index 14c36b4..21efd62 100644 --- a/tuneit/tools/optuna.py +++ b/tuneit/tools/optuna.py @@ -42,10 +42,7 @@ def __init__(self, tunable, callback=None, storage=None, n_trials=None, **kwargs self.callback = callback - if n_trials: - self.n_trials = n_trials - else: - self.n_trials = 1 # default + self.n_trials = n_trials or 1 self.storage = storage From a65f9ac4f9585e17fd1d3df563dbee58082ff400 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 17:24:08 +0300 Subject: [PATCH 072/122] removed commented code --- test/test_tunable.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/test_tunable.py b/test/test_tunable.py index 3f09661..8628afe 100644 --- a/test/test_tunable.py +++ b/test/test_tunable.py @@ -29,9 +29,6 @@ def test_object(): one = Object(1, deps=zero) -# assert zero.key in one.dependencies - - def test_function(): with raises(TypeError): function(1) @@ -45,8 +42,6 @@ def test_function(): one = Object(1) fnc = Function(str, args=zero, kwargs={"one": one}) - # assert zero.key in fnc.dependencies - # assert one.key in fnc.dependencies lst = Object([1, 2]).tunable() lst = lst.append(3) From 9c6f60b895c882a639c88b6122df80c1119e06bd Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 17:25:05 +0300 Subject: [PATCH 073/122] reformatted --- tuneit/finalize.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 4e622bc..495ce57 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -2,7 +2,9 @@ High Level vision of Tunable object """ -__all__ = ["finalize"] +__all__ = [ + "finalize", +] from .graph import Node, Key, Graph from .variable import Variable From 695564381ed827f73a93f291dede0df9d251ce32 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 21:15:55 +0300 Subject: [PATCH 074/122] pandas are now imported inside record --- tuneit/tools/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tuneit/tools/base.py b/tuneit/tools/base.py index 8f99b30..3c7ac6a 100644 --- a/tuneit/tools/base.py +++ b/tuneit/tools/base.py @@ -14,7 +14,6 @@ from tabulate import tabulate from ..finalize import finalize from lyncs_utils import isiterable -import pandas as pd class Sampler: @@ -81,6 +80,10 @@ def record(self): @record.setter def record(self, value): + try: + import pandas as pd + except: + raise ImportError("pandas is a requirement for record") if value == self.record: return if value is False: From 1722fa8b59065c2c202245fb0026c3d2b31d8505 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 18 May 2021 21:16:22 +0300 Subject: [PATCH 075/122] updated index file --- docs/index.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 75ef1ba..5615f7e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,13 +1,11 @@ .. Tunable documentation master file. -Tuneit: -======= +Tuneit: optimize, benchmark and crosscheck +========================================== Welcome to Tuneit's documentation! =================================== -.. - title needs to change Basic Concepts -------------- @@ -17,11 +15,11 @@ The Tuneit package works with computational graphs, which have two main phases: * Variables: they are represented using diamonds. The outline is red in case the variable does not have a value yet and green in case the variable has been assigned a fixed value. * Operations: they are represented using oval shapes. - * Data: All data objects are represented with rectangles. Most of them represent data inputs, except for the last node in the graph, which - is represents the data output. - - .. - add image images/ + * Data: All data objects are represented using rectangles. Most of them represent data inputs, except for the last node in the graph, which represents the data output. + |pic| + +.. |pic| image:: images/computational_graph.png + :width: 500 - A finalization phase. After the graph is finalized, a number of operations (described in the next section) can be performed on it. From 15bf4da45c168350156324c8fa29c402dcc696be Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 12:40:06 +0300 Subject: [PATCH 076/122] these tests are now only performed if optuna is installed --- test/test_optuna.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/test/test_optuna.py b/test/test_optuna.py index 7df9db6..26866b4 100644 --- a/test/test_optuna.py +++ b/test/test_optuna.py @@ -1,19 +1,29 @@ +import pytest +import sys from tuneit import * import numpy -from tuneit.tools.time import Time, default_timer -from tuneit.tools.optuna import OptunaSampler -import pytest -import optuna -from optuna.trial import Trial -from optuna.trial import create_trial -from optuna.distributions import CategoricalDistribution -from optuna.trial import TrialState -from tuneit.finalize import HighLevel -from optuna.study import Study from hashlib import md5 from dill import dumps +from tuneit.tools.time import Time, default_timer + +try: + from tuneit.tools.optuna import OptunaSampler + import optuna + from optuna.trial import Trial + from optuna.trial import create_trial + from optuna.distributions import CategoricalDistribution + from optuna.trial import TrialState + from tuneit.finalize import HighLevel + from optuna.study import Study + + skip_optuna = False +except ImportError: + skip_optuna = True + +skip_optuna = pytest.mark.skipif(skip_optuna, reason="optuna is not installed") +@skip_optuna def test_optuna_sampler(): # simple example to use in tests # building a graph with variables for sorting (preprocessing) and searching to be tuned: From 71efb0c07f983d67d046baa5d80caa0064ca012b Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 12:42:32 +0300 Subject: [PATCH 077/122] image used in index file (docs) --- docs/images/computational_graph.png | Bin 0 -> 32870 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/computational_graph.png diff --git a/docs/images/computational_graph.png b/docs/images/computational_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..d92ffb27dba191e493a6cd123a563e4de8ab5306 GIT binary patch literal 32870 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

}x1k6rgUDrr1r?GEp!hH}*t~wU0}ILuc2)ARq7) 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 ts2 Date: Wed, 26 May 2021 14:18:33 +0300 Subject: [PATCH 078/122] added optuna to extras --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index db96e49..0a45a27 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,6 @@ "tabulate", "numpy", "lyncs_utils", - "pandas", ] extras = { @@ -16,6 +15,7 @@ ], "test": ["pytest", "pytest-cov"], "optuna": ["optuna"], + "record": ["pandas"], } setup( From a6135c154bf4bfdbb6d8b3e0699b8194bbc094c3 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 14:21:39 +0300 Subject: [PATCH 079/122] changed definition of remove to include *nodes instead of nodes and changed merge accordingly --- tuneit/finalize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 495ce57..34e89e3 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -188,7 +188,7 @@ def compute(self, **kwargs): kwargs.setdefault("graph", self.graph) return compute(self.value, **kwargs) - def remove(self, nodes): + def remove(self, *nodes): "Removes the list of nodes from the graph" for node in nodes: del self.graph[node] @@ -226,7 +226,7 @@ def merge(self, nodes=None): ) new_graph = self.copy(reset=True) nodes.remove(last_node) - new_graph.remove(nodes) + new_graph.remove(*nodes) new_graph[last_node] = new_node return new_graph From 486c386616b3cd0e4b6b1d5f4c5b46a405ec027f Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 14:22:47 +0300 Subject: [PATCH 080/122] removed dependencies function from Object (not used anywhere) --- tuneit/tunable.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tuneit/tunable.py b/tuneit/tunable.py index 0b577fc..c92e6c7 100644 --- a/tuneit/tunable.py +++ b/tuneit/tunable.py @@ -127,12 +127,6 @@ def tunable(self): "Returns a tunable object" return Tunable(self) - @property - def dependencies(self): - "Returns the list of dependencies for the Object" - raise NotImplementedError - return Node(self.tunable()).dependencies - def copy(self, **kwargs): "Returns a copy of self" # TODO: improve copy.. it should copy automatically all the data From 7a151cfbd6740e8bfe1f1657b6a1355531264e2e Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 14:24:24 +0300 Subject: [PATCH 081/122] updated index file with code block --- docs/index.rst | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 5615f7e..eb1a213 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,9 +3,6 @@ Tuneit: optimize, benchmark and crosscheck ========================================== -Welcome to Tuneit's documentation! -=================================== - Basic Concepts -------------- @@ -13,13 +10,26 @@ The Tuneit package works with computational graphs, which have two main phases: - A construction phase, where the graph is being built. Every operation that needs to be performed will be added to the graph as node along with all the variables and data input and output. Each type of node is visualised differently in the graph as it is shown below: - * Variables: they are represented using diamonds. The outline is red in case the variable does not have a value yet and green in case the variable has been assigned a fixed value. - * Operations: they are represented using oval shapes. - * Data: All data objects are represented using rectangles. Most of them represent data inputs, except for the last node in the graph, which represents the data output. - |pic| + * **Variables:** they are represented using diamonds. The outline is red in case the variable does not have a value yet and green in case the variable has been assigned a fixed value. + * **Operations:** they are represented using oval shapes. + * **Data:** All data objects are represented using rectangles. Most of them represent data inputs, except for the last node in the graph, which represents the data output.:: + .. code-block:: python + + from tuneit import * + + .. code-block:: python + + a = data() + x = variable(range(10)) + y = variable(range(5)) + axpy = a * x + y + visualize(axpy) + |pic| + + + .. |pic| image:: images/computational_graph.png - :width: 500 - A finalization phase. After the graph is finalized, a number of operations (described in the next section) can be performed on it. From 25e1ab881430eb5d996251a752d070b9f5432188 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 14:24:59 +0300 Subject: [PATCH 082/122] updated image to match code block --- docs/images/computational_graph.png | Bin 32870 -> 10830 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/computational_graph.png b/docs/images/computational_graph.png index d92ffb27dba191e493a6cd123a563e4de8ab5306..b19830fd20ba800e44310a0db12afceaf55bfa65 100644 GIT binary patch literal 10830 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 B;pPAU literal 32870 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

}x1k6rgUDrr1r?GEp!hH}*t~wU0}ILuc2)ARq7) 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 ts2 Date: Wed, 26 May 2021 17:18:31 +0300 Subject: [PATCH 083/122] alternatives changed to allow the user to pass a name for the function created using alternatives --- tuneit/class_utils.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tuneit/class_utils.py b/tuneit/class_utils.py index 209fd84..9bd7d9e 100644 --- a/tuneit/class_utils.py +++ b/tuneit/class_utils.py @@ -222,9 +222,12 @@ def args_to_kwargs(cls, *args): return kwargs - def __init__(self, var_name=None, *args, **kwargs): + def __init__(self, *args, name=None, var_name=None, **kwargs): super().__init__(**self.args_to_kwargs(*args), **kwargs) + self._name_given = False self.default = next(iter(self)) + if name: + self.name = name self._var_name = var_name self._closed = False @@ -242,8 +245,8 @@ def default(self, key): if key not in self: raise KeyError(f"{key} unknown alternative") self._default = key - wraps(self[key])(self) - self.__name__ = key + if not self._name_given: + self.name = key def add(self, fnc): "Adds a value to the alternatives" @@ -261,6 +264,15 @@ def var_name(self): def var_name(self, key): self._var_name = key + @property + def name(self): + return self.__name__ + + @name.setter + def name(self, key): + self.__name__ = key + self._name_given = True + def __call__(self, *args, _key=None, **kwargs): if len(args) == 1 and callable(args[0]) and not self._closed: self.default = self.add(args[0]) From a5d48751965a9d0aab4322ef8a2e41456dd9bacc Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 17:20:40 +0300 Subject: [PATCH 084/122] new images for index.rst --- docs/images/computational_graph1.png | Bin 0 -> 32870 bytes docs/images/computational_graph2.PNG | Bin 0 -> 10830 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/computational_graph1.png create mode 100644 docs/images/computational_graph2.PNG diff --git a/docs/images/computational_graph1.png b/docs/images/computational_graph1.png new file mode 100644 index 0000000000000000000000000000000000000000..d92ffb27dba191e493a6cd123a563e4de8ab5306 GIT binary patch literal 32870 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

}x1k6rgUDrr1r?GEp!hH}*t~wU0}ILuc2)ARq7) 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 ts2w1O!yjXAoTif*ZZS_f=A2 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 B;pPAU literal 0 HcmV?d00001 From e4919640ce247c8c4186a6541a9b0c456e6c1865 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 17:26:41 +0300 Subject: [PATCH 085/122] deleted unnecessary file --- docs/images/computational_graph.png | Bin 10830 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/images/computational_graph.png diff --git a/docs/images/computational_graph.png b/docs/images/computational_graph.png deleted file mode 100644 index b19830fd20ba800e44310a0db12afceaf55bfa65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10830 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 B;pPAU From 6e93846082c7a22b8c72f3f2f33c8486bc6e0918 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 17:28:40 +0300 Subject: [PATCH 086/122] visualize can now be called on a HighLevel object (obj.visualize()) --- tuneit/finalize.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tuneit/finalize.py b/tuneit/finalize.py index 34e89e3..d12aaba 100644 --- a/tuneit/finalize.py +++ b/tuneit/finalize.py @@ -6,7 +6,7 @@ "finalize", ] -from .graph import Node, Key, Graph +from .graph import Node, Key, Graph, visualize from .variable import Variable from .tunable import Object, Function, compute, Data, data @@ -302,3 +302,5 @@ def mergeable(self, nodes): if self.consecutive(nodes) and res: return last_node, True return last_node, False + + visualize = visualize From 34cd801c1b58c1760cf695ae5a69a0a5a740a6e5 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 18:02:30 +0300 Subject: [PATCH 087/122] the functions benchmark, crosscheck and optimise can now be called on a HighLevel object (e.g. obj.crosscheck() ) --- tuneit/tools/check.py | 5 ++++- tuneit/tools/time.py | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/tuneit/tools/check.py b/tuneit/tools/check.py index cfaee44..14ace02 100644 --- a/tuneit/tools/check.py +++ b/tuneit/tools/check.py @@ -5,7 +5,7 @@ import operator from numpy import allclose from .base import sample -from ..finalize import finalize +from ..finalize import finalize, HighLevel __all__ = [ "crosscheck", @@ -56,3 +56,6 @@ def get_ref(res): label=label, **kwargs ) + + +HighLevel.crosscheck = crosscheck diff --git a/tuneit/tools/time.py b/tuneit/tools/time.py index a5b5f79..d1578ae 100644 --- a/tuneit/tools/time.py +++ b/tuneit/tools/time.py @@ -10,6 +10,7 @@ from timeit import timeit from .base import sample from .tuner import tune +from ..finalize import HighLevel class Time(float): @@ -107,3 +108,6 @@ def optimise(tunable, timer=default_timer, timer_kwargs=None, **kwargs): optimize = optimise +HighLevel.benchmark = benchmark +HighLevel.optimize = optimise +HighLevel.optimize = HighLevel.optimise From 5eb61aac2becd3956a5f96979183c312840b21df Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 18:03:31 +0300 Subject: [PATCH 088/122] updated index file with additional picture --- docs/index.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index eb1a213..bbee7c4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,10 +9,11 @@ Basic Concepts The Tuneit package works with computational graphs, which have two main phases: - A construction phase, where the graph is being built. Every operation that needs to be performed will be added to the graph as node along with all the variables and data input and output. Each type of node is visualised differently in the graph as it is shown below: - + |pic1| + * **Variables:** they are represented using diamonds. The outline is red in case the variable does not have a value yet and green in case the variable has been assigned a fixed value. * **Operations:** they are represented using oval shapes. - * **Data:** All data objects are represented using rectangles. Most of them represent data inputs, except for the last node in the graph, which represents the data output.:: + * **Data:** All data objects are represented using rectangles. Most of them represent data inputs, except for the last node in the graph, which represents the data output. .. code-block:: python from tuneit import * @@ -25,11 +26,12 @@ The Tuneit package works with computational graphs, which have two main phases: axpy = a * x + y visualize(axpy) - |pic| - + |pic2| +.. |pic1| image:: images/computational_graph1.png + :width: 500 -.. |pic| image:: images/computational_graph.png +.. |pic2| image:: images/computational_graph2.png - A finalization phase. After the graph is finalized, a number of operations (described in the next section) can be performed on it. From 6ad47a83c788187327b4a0f82f893fbcdc8e5610 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 18:18:31 +0300 Subject: [PATCH 089/122] fixed alternatives because it was setting the name of the first option as the name of the function if no name was manually given --- tuneit/class_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tuneit/class_utils.py b/tuneit/class_utils.py index 9bd7d9e..ff91a8b 100644 --- a/tuneit/class_utils.py +++ b/tuneit/class_utils.py @@ -246,7 +246,7 @@ def default(self, key): raise KeyError(f"{key} unknown alternative") self._default = key if not self._name_given: - self.name = key + self.__name__ = key def add(self, fnc): "Adds a value to the alternatives" From 48248dc649c273301667bf3a65f81563fd4f33f8 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 18:19:27 +0300 Subject: [PATCH 090/122] set HighLevel.optimise equal to optimise --- tuneit/tools/time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tuneit/tools/time.py b/tuneit/tools/time.py index d1578ae..62efdbd 100644 --- a/tuneit/tools/time.py +++ b/tuneit/tools/time.py @@ -110,4 +110,4 @@ def optimise(tunable, timer=default_timer, timer_kwargs=None, **kwargs): optimize = optimise HighLevel.benchmark = benchmark HighLevel.optimize = optimise -HighLevel.optimize = HighLevel.optimise +HighLevel.optimise = optimise From 7d11cdac9db706a8cc2e66d4ff8e152d3f81e121 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 26 May 2021 18:26:22 +0300 Subject: [PATCH 091/122] changed how the name in alternatives is set --- tuneit/class_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tuneit/class_utils.py b/tuneit/class_utils.py index ff91a8b..2ff4779 100644 --- a/tuneit/class_utils.py +++ b/tuneit/class_utils.py @@ -228,6 +228,7 @@ def __init__(self, *args, name=None, var_name=None, **kwargs): self.default = next(iter(self)) if name: self.name = name + self._name_given = True self._var_name = var_name self._closed = False @@ -246,7 +247,7 @@ def default(self, key): raise KeyError(f"{key} unknown alternative") self._default = key if not self._name_given: - self.__name__ = key + self.name = key def add(self, fnc): "Adds a value to the alternatives" @@ -271,7 +272,6 @@ def name(self): @name.setter def name(self, key): self.__name__ = key - self._name_given = True def __call__(self, *args, _key=None, **kwargs): if len(args) == 1 and callable(args[0]) and not self._closed: From f48aa1bd8cb76b18f91f56f6d2ebddee955c1c28 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 1 Jun 2021 13:32:47 +0300 Subject: [PATCH 092/122] updated README file --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/README.md b/README.md index 583ca64..08e0b93 100644 --- a/README.md +++ b/README.md @@ -20,4 +20,71 @@ The package can be installed via `pip`: pip install [--user] tuneit ``` +## Documentation +The Tuneit documentation can be accessed using the link: +[tuneit documentation](https://tuneit.readthedocs.io/en/latest/) + +The Tuneit package works with computational graphs, which have two main phases: + +- A construction phase, where the graph is being built. Every operation that needs to be performed will be added to the graph as node along with all the variables and data input and output. Each type of node is visualised differently in the graph: + + * **Variables:** they are represented using diamonds. The outline is red in case the variable does not have a value yet and green in case the variable has been assigned a fixed value. + * **Operations:** they are represented using oval shapes. + * **Data:** All data objects are represented using rectangles. Most of them represent data inputs, except for the last node in the graph, which represents the data output. + +- A finalization phase. After the graph is finalized, a number of operations can be performed on it: + + * **Visualize:** Using the `visualize()` function the graph can be visualized as it is shown above. + * **Compute:** By simply calling the finalized object of the graph, the value final of the graph is computed and returned. + * **Crosshcheck:** The `crosscheck()` function will iterate through all the different options for a variable and return `True` + only for the ones that return the correct result of the graph. + * **Benchmark:** By using the `benchmark()` function, the computation times of all the different combinations of options for the + variables can be compared. In addition, by using the attribute `record` of the function, all those times can be recorded in a + dataframe. Furthermore, the `record` option allows for comparisons between not only the execution times that result by the various + alternatives for the variables, but also different inputs. + * **Optimize:** By using the `optimize()` function, the variables of the graph can be tuned. Each time it is called, it returns the values that were used for the variables in that trial and the resulting computation time along with the best trial executed so far. + A trial consists of the timing of an execution of the graph using a different combination of values for the variables that are tuned. + + +## Example + +A small example constructing a graph for the multiplication of a matrix and a vector: + +```````````` +@alternatives( + coo = lambda mat: sp.coo_matrix(mat), + csc = lambda mat: sp.csc_matrix(mat), + csr = lambda mat: sp.csr_matrix(mat), + bsr = lambda mat: sp.bsr_matrix(mat) +) +def matrix(mat): + return s.matrix(mat.todense()) + +mat=data(info=["shape","dtype"]) +vec=data(info=["shape","dtype"]) +mat=matrix(mat) +mul=mat*vec +mul=finalize(mul) + +mul.visualize() + +matrix_value = sp.random(100,100,0.1) +vector_value = np.random.rand(100,1) + +# for the result of the graph: +out = mul(mat=matrix_value, vec=vector_value) + +# for comparing the options of the variable which_matrix: +mul.benchmark(mat=matrix_value,vec=vector_value) +```````````` + + +## Acknowledgments + +### Authors +- Simone Bacchio (sbacchio) +- Raphaella Demetriou (raphdem) + +### Fundings +- PRACE-6IP, Grant agreement ID: 823767, Project name: LyNcs. \ No newline at end of file From cc8d42b8da06e38f27e09a46b318895bda97e343 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 1 Jun 2021 13:33:53 +0300 Subject: [PATCH 093/122] updated images to match changes in the code --- docs/images/visualised_graph1.PNG | Bin 13117 -> 15666 bytes docs/images/visualised_graph2.PNG | Bin 14341 -> 17490 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/visualised_graph1.PNG b/docs/images/visualised_graph1.PNG index 43b82dc7e17c1da5c63e21a36093c4227eb2ddea..50af9c1a83b6dd6500d77508820debb09e967176 100644 GIT binary patch literal 15666 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 literal 13117 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 J;zy=${|{yluB-q6 literal 14341 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 Date: Tue, 1 Jun 2021 18:18:28 +0300 Subject: [PATCH 094/122] updated example.rst file --- docs/example.rst | 181 ++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 97 deletions(-) diff --git a/docs/example.rst b/docs/example.rst index 4228224..62f3c57 100644 --- a/docs/example.rst +++ b/docs/example.rst @@ -7,33 +7,33 @@ Example: A small tuning example using sparse matrices from tuneit import * import scipy.sparse as sp + import scipy as s import numpy as np Firstly, a simple graph is constructed, which computes the multiplication of a sparse matrix with a vector. -The graph contains one variable to be tuned, which represents the different formats that can be used for the sparse matrix. -The following function creates the sparse matrix and by using :code:`alternatives` more options are added for the format that will -be used to express the matrix (available in the :code:`scipy.sparse` package). +The graph contains one variable to be tuned, which represents the different formats that can be used for the matrix. +The following function creates a matrix and by using :code:`alternatives` more options are added for the creation of a matrix and its format (available in the :code:`scipy.sparse` package). .. code-block:: python - @alternatives( - var_name='which_sparse', - coo = lambda matrix: sp.coo_matrix(matrix), - csc = lambda matrix: sp.csc_matrix(matrix), - csr = lambda matrix: sp.csr_matrix(matrix) - ) - def bsr(matrix): - return sp.bsr_matrix(matrix) + @alternatives( + coo = lambda mat: sp.coo_matrix(mat), + csc = lambda mat: sp.csc_matrix(mat), + csr = lambda mat: sp.csr_matrix(mat), + bsr = lambda mat: sp.bsr_matrix(mat) + ) + def matrix(mat): + return s.matrix(mat.todense()) -In this way, we have created a function :code:`bsr` that expresses the given sparse matrix in an appropriate format and a variable -to be tuned. The range of the variable :code:`which_sparse` contains all different options that can be used to express the sparse matrix, which are included in the function above (:code:`coo,csc,csr,bsr`). +In this way, we have created a function :code:`matrix` that expresses the given sparse matrix in an appropriate format and a variable +to be tuned. The range of the variable :code:`which_matrix` contains all different options that can be used to express the matrix, which are included in the function above (:code:`matrix,coo,csc,csr,bsr`). The graph takes as input the matrix and the vector to be multiplied. One option is to create a matrix and a vector at random: .. code-block:: python mat=scipy.sparse.random(100,100,0.1) - vec=np.random.rand(100) + vec=np.random.rand(100,1) Or just create two generic data objects, which will take their actual value later on: (this is the option used in this example) @@ -46,29 +46,38 @@ Or just create two generic data objects, which will take their actual value late about the new data object can be given using :code:`info`. As shown above, some characteristics about the new objects are given by the attributes :code:`shape` and :code:`dtype`. -In addition, the :code:`bsr` function constructed previously can now be used. The new :code:`mat` object created after -:code:`bsr` is called on the object :code:`mat` (created above) is a tunable object. +In addition, the :code:`matrix` function constructed previously can now be used. The new :code:`mat` object created after +:code:`matrix` is called on the object :code:`mat` (created above) is a tunable object. .. code-block:: python - mat=bsr(mat) + mat=matrix(mat) + +Furthermore, we define a random sparse matrix and a random vector that will be used later on when actual values are needed to be passed for the :code:`mat,vec` objects created above. + +.. code-block:: python + + matrix_value = sp.random(100,100,0.1) + vector_value = np.random.rand(100) The final graph :code:`mul` that expresses the multiplication between the vector :code:`vec` and the sparse matrix :code:`mat` is created as shown below: .. code-block:: python - mul=finalize(mat*vec) - -.. - Do I need more explanations about the finalize function here and why it is needed? + mul=mat*vec -Furthermore, we define a random sparse matrix and a random vector that will be used later on when actual values are needed to be passed for the :code:`mat,vec` objects created above. +Once the graph is completed we can finalize it with the function :code:`finalize`. .. code-block:: python - matrix = sp.random(100,100,0.1) - vector = np.random.rand(100) + mul = finalize(mul) + +This closes the graph and provides us a high-level interface for processing the graph (e.g. we can simply compute it by calling it). + +.. code-block:: python + + out = mul(mat=matrix_value, vec=vector_value) Visualize @@ -78,7 +87,7 @@ The graph can now be visualized using: .. code-block:: python - visualize(mul) + mul.visualize() The result is shown below: @@ -86,8 +95,8 @@ The result is shown below: The data objects are shown in rectangles, the functions to be computed are presented in oval shapes, while the variables that have not taken a fixed value yet are shown in red diamonds. -Note: Each node in the graph is represented by its name (such as :code:`bsr`) concatenated with a random sequence of characters, which -is not shown in its visualisation (for instance :code:`bsr-2b53519cefa68a68788760b169fee0b4`). +Note: Each node in the graph is represented by its name (such as :code:`matrix`) concatenated with a random sequence of characters, which +is not shown in its visualisation (for instance :code:`matrix-2b53519cefa68a68788760b169fee0b4`). The small indices included in the nodes of the visualised graph allow the user to distinguish between multiple operations of the same kind (e.g. multiplications) and to find out the whole unique name of a node in case it is needed in an operation: @@ -101,105 +110,95 @@ For instance the following code should return the whole name of the node that co Crosscheck ---------- -The function :code:`crosscheck` can be called on the finalised object :code:`mul` as shown below. The function returns a callable sampler -object. +The function :code:`crosscheck` can be called on the finalised object :code:`mul` as shown below. .. code-block:: python - obj = crosscheck(mul) - -If it is then called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object -will iterate through all the possible alternative options for the variable of the graph (:code:`which_sparse`) and return :code:`True` only for the ones -that produce the correct result of the graph. The :code:`crosscheck` function is basically a way to check that all alternatives options return -the correct result. + mul.crosscheck(mat=matrix_value,vec=vector_value) -.. code-block:: python - - obj(mat=matrix,vec=vector) +If it is called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object +created will iterate through all the possible alternative options for the variable of the graph (:code:`which_matrix`) and return :code:`True` only for the ones +that produce the correct result of the graph. The :code:`crosscheck` function is basically a way to check that all alternatives options return the correct result. The result of the above operation is: .. table:: ============== ======== - which_sparse xcheck + which_matrix xcheck ============== ======== coo True csc True csr True bsr True + matrix True ============== ======== Benchmark --------- -The function :code:`benchmark` can be called on the finalised object :code:`mul` as shown below. The function returns a callable sampler -object. +The function :code:`benchmark` can be called on the finalised object :code:`mul` as shown below. .. code-block:: python - obj = benchmark(mul) + mul.benchmark(mat=matrix_value,vec=vector_value) -If it is then called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object -will iterate through all the possible alternative options for the variable of the graph (:code:`which_sparse`) and time the execution of graph using each +If it is called using real values (since the input :code:`mat,vec` of the graph was created using generic data objects) the sampler object +will iterate through all the possible alternative options for the variable of the graph (:code:`which_matrix`) and time the execution of graph using each option. The :code:`benchmark` function is basically a way to compare the execution times of all alternatives options of the variable. -.. code-block:: python - - obj(mat=matrix,vec=vector) - The result of the above operation is: .. table:: ============== ============ - which_sparse Time + which_matrix Time ============== ============ - coo 448.800 usec - csc 663.900 usec - csr 1.796 msec - bsr 1.551 msec + coo 475.300 usec + csc 1.076 msec + csr 1.478 msec + bsr 845.800 usec + matrix 803.200 usec ============== ============ The :code:`bechmark` function has also an argument called :code:`record`, which if it set to :code:`True` allows the execution times of the graph using alternative options for the variable to be stored in a :code:`panda` dataframe. In addition, now there is the option of also comparing the execution times that result not only by the various alternatives for the variable, but also different inputs. For example, in the code below -different sizes of inputs are passed in each execution of the sampler. As a result, the returned dataframe :code:`trials` will contain the execution +different sizes of inputs are passed in each execution of the sampler object :code:`obj`. As a result, the returned dataframe :code:`trials` (can be accessed using the sampler object) will contain the execution time of the graph for all combinations of alternative options of the variable and different sizes of inputs. .. code-block:: python - obj = benchmark(mul, record=True) - for n in for [2**exponent for exponent in range(15)]: - obj(mat=sp.random(n,n,0.1),vec=np.random.rand(n)).run() + sampler = mul.benchmark(record=True) + for n in [2**exponent for exponent in range(15)]: + sampler().run(mat=sp.random(n,n,0.1),vec=np.random.rand(n,1)) The dataframe can be accessed as shown below: .. code-block:: python - obj.trials + sampler.trials The produced dataframe looks like this: .. table:: - - ========== ============== ============== ============ ============ ============ ========== - trial_id which_sparse mat_shape mat_dtype vec_shape vec_dtype time - ========== ============== ============== ============ ============ ============ ========== - 0 coo (1, 1) float64 (1,) float64 0.0004373 - 1 csc (1, 1) float64 (1,) float64 0.0003272 - 2 csr (1, 1) float64 (1,) float64 0.0004419 - 3 bsr (1, 1) float64 (1,) float64 0.0004452 - 4 coo (2, 2) float64 (2,) float64 0.0002657 + ========== ============== ============== =========== =========== =========== ========= + trial_id which_matrix mat_shape mat_dtype vec_shape vec_dtype time + ========== ============== ============== =========== =========== =========== ========= + 0 coo (1, 1) float64 (1, 1) float64 0.0020286 + 1 csc (1, 1) float64 (1, 1) float64 0.0042852 + 2 csr (1, 1) float64 (1, 1) float64 0.0021259 + 3 bsr (1, 1) float64 (1, 1) float64 0.0021831 + 4 matrix (1, 1) float64 (1, 1) float64 0.0005839 ... ... ... ... ... ... ... - 55 bsr (8192, 8192) float64 (8192,) float64 0.882976 - 56 coo (16384, 16384) float64 (16384,) float64 0.18082 - 57 csc (16384, 16384) float64 (16384,) float64 3.64082 - 58 csr (16384, 16384) float64 (16384,) float64 3.59247 - 59 bsr (16384, 16384) float64 (16384,) float64 4.25281 - ========== ============== ============== ============ ============ ============ ========== + 70 coo (16384, 16384) float64 (16384, 1) float64 0.333415 + 71 csc (16384, 16384) float64 (16384, 1) float64 6.21665 + 72 csr (16384, 16384) float64 (16384, 1) float64 6.42704 + 73 bsr (16384, 16384) float64 (16384, 1) float64 7.46502 + 74 matrix (16384, 16384) float64 (16384, 1) float64 6.29298 + ========== ============== ============== =========== =========== =========== ========= The dataframe can be then used to compare different sizes of inputs for the different alternatives for the variable. One way to do this visually is producing a graph like it is shown below: @@ -210,60 +209,48 @@ is producing a graph like it is shown below: Optimize: --------- -For the purposes of this example, we would like to tune the variable :code:`which_sparse` based only on the computation time of the multiplication -(i.e. excluding the time taken by the function :code:`bsr` to construct the matrix). In order to achieve this, a link has to be added -between the multiplication and :code:`which_sparse`, as they are not currently directly connected (:code:`which_sparse` is added as a dependency to the last node +For the purposes of this example, we would like to tune the variable :code:`which_matrix` based only on the computation time of the multiplication +(i.e. excluding the time taken by the function :code:`matrix` to construct the matrix). In order to achieve this, a link has to be added +between the multiplication and :code:`which_matrix`, as they are not currently directly connected (:code:`which_matrix` is added as a dependency to the last node of the graph): .. code-block:: python - mul.add_deps('which_sparse') + mul.add_deps('which_matrix') The new link can be observed by running the code: .. code-block:: python - visualize(mul) + mul.visualize() .. image:: images/visualised_graph2.png -In addition, the :code:`bsr` node in the graph needs to be marked as one to be precomputed so that its computation time is not +In addition, the :code:`matrix` node in the graph needs to be marked as one to be precomputed so that its computation time is not taken into account when the execution of the graph is timed during the tuning of the variable. -Note: In the following operation we can use the name :code:`bsr` for the node only because it is unique in the graph. If there were -multiple operations of the same kind (e.g. the function :code:`bsr` is used twice in the graph), then the full name of the node would +Note: In the following operation we can use the name :code:`matrix` for the node only because it is unique in the graph. If there were +multiple operations of the same kind (e.g. the function :code:`matrix` is used twice in the graph), then the full name of the node would have to be used. .. code-block:: python - mul['bsr'].precompute=True - -.. - should I include a visualisation of the node marked as precomputed? + mul['matrix'].precompute=True The only thing left to do is to actually tune the variable by calling the following functions: .. code-block:: python - obj = optimise(mul,sampler='optuna') + mul.optimize(mat=matrix_value,vec=vector_value,sampler='optuna')() -A tuner object has been created by passing the graph to be tuned along with the sampler to be used to the :code:`optimise()` function. +A tuner object has been created by calling the :code:`optimize()` function on the graph to be tuned and passing it the sampler to be used. The :code:`optuna` package is one of the options that are offered by :code:`tuneit` to be used as a sampler. -.. - maybe include a reference for the optuna package? - -Now, the tuner object can simply be called, while also passing actual values for the sparse matrix and the vector. This is necessary, because +The tuner object is called, while also passing actual values for the sparse matrix and the vector. This is necessary, because during the tuning of the variable the computation of the graph will be carried out for the first time. Each time the tuner object is called, the tuner executes one more trial and it returns the value that was used for the variable in that trial and the resulting computation time along with the best trial executed so far. Note: A trial is a single execution of the objective function (which in this case is the timing of an execution) using a different combination of values for the variables that are tuned. -For example: - -.. code-block:: python - - obj(mat=matrix,vec=vector) - .. do I need to include a picture of the result here? (what the tuner returns after it is called a few times) From 62c30f2a9a3c47f836aec7022c181601f1b93139 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Tue, 1 Jun 2021 18:19:04 +0300 Subject: [PATCH 095/122] updated image to match changes in example.rst --- docs/images/plot.png | Bin 18080 -> 21331 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/plot.png b/docs/images/plot.png index c443934603c56c71d128d2f3bd3282660e945c48..db89e273ff4a56439ae5edaf6f23b42634c3dec7 100644 GIT binary patch literal 21331 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 literal 18080 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 byT7NOAYUu+jTpRD=H6=wdGP{KeXsuoQx9m{ From 2a72c196c29873a43a4ac76afa9d013352696192 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 2 Jun 2021 23:29:40 +0300 Subject: [PATCH 096/122] updated picture --- docs/images/plot.png | Bin 21331 -> 21139 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/plot.png b/docs/images/plot.png index db89e273ff4a56439ae5edaf6f23b42634c3dec7..81c8de677366835d49d6d7b96c7ff8aac4c24ad7 100644 GIT binary patch literal 21139 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 J@!X}T{{Uf`oaF!j literal 21331 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 From 79ef8a6989a98bb4ffe7b619deecc4dc68d9645b Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 2 Jun 2021 23:31:35 +0300 Subject: [PATCH 097/122] removed pictures with wrong extension --- docs/images/computational_graph2.PNG | Bin 10830 -> 0 bytes docs/images/visualised_graph1.PNG | Bin 15666 -> 0 bytes docs/images/visualised_graph2.PNG | Bin 17490 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/images/computational_graph2.PNG delete mode 100644 docs/images/visualised_graph1.PNG delete mode 100644 docs/images/visualised_graph2.PNG diff --git a/docs/images/computational_graph2.PNG b/docs/images/computational_graph2.PNG deleted file mode 100644 index b19830fd20ba800e44310a0db12afceaf55bfa65..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10830 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 B;pPAU diff --git a/docs/images/visualised_graph1.PNG b/docs/images/visualised_graph1.PNG deleted file mode 100644 index 50af9c1a83b6dd6500d77508820debb09e967176..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15666 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 diff --git a/docs/images/visualised_graph2.PNG b/docs/images/visualised_graph2.PNG deleted file mode 100644 index 6cb637621e468d304604d552cc9f600697bd2762..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17490 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 J;zy=${|{yluB-q6 From 4cf99fccd8ee9fae65e4c0edb896115631136904 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 2 Jun 2021 23:34:15 +0300 Subject: [PATCH 098/122] images with correct extensions --- docs/images/computational_graph2.png | Bin 0 -> 10810 bytes docs/images/visualised_graph1.png | Bin 0 -> 15739 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/computational_graph2.png create mode 100644 docs/images/visualised_graph1.png diff --git a/docs/images/computational_graph2.png b/docs/images/computational_graph2.png new file mode 100644 index 0000000000000000000000000000000000000000..dd0011269281b8bcbcdf075e192e09fc36025771 GIT binary patch literal 10810 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 Bv_1d; literal 0 HcmV?d00001 diff --git a/docs/images/visualised_graph1.png b/docs/images/visualised_graph1.png new file mode 100644 index 0000000000000000000000000000000000000000..2c127c065710b65425fdf647bd5a3339ebc284c7 GIT binary patch literal 15739 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 literal 0 HcmV?d00001 From 40c6e9614c569510977492f0c8471ba5f60d4833 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 2 Jun 2021 23:35:08 +0300 Subject: [PATCH 099/122] image with correct extension --- docs/images/visualised_graph2.png | Bin 0 -> 17508 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/images/visualised_graph2.png diff --git a/docs/images/visualised_graph2.png b/docs/images/visualised_graph2.png new file mode 100644 index 0000000000000000000000000000000000000000..3db2d9037cfe14355b2c14f4c956918320f74601 GIT binary patch literal 17508 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 literal 0 HcmV?d00001 From 24a7a33c9fc79091cfc787a47fc46c58baa9ea4f Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Wed, 2 Jun 2021 23:36:15 +0300 Subject: [PATCH 100/122] fixed typo --- docs/example.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/example.rst b/docs/example.rst index 62f3c57..5518912 100644 --- a/docs/example.rst +++ b/docs/example.rst @@ -58,7 +58,7 @@ Furthermore, we define a random sparse matrix and a random vector that will be u .. code-block:: python matrix_value = sp.random(100,100,0.1) - vector_value = np.random.rand(100) + vector_value = np.random.rand(100,1) The final graph :code:`mul` that expresses the multiplication between the vector :code:`vec` and the sparse matrix :code:`mat` is created as shown below: From 696f1bff8422957d353695f4ad9a6c00778c5aeb Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 3 Jun 2021 13:34:22 +0300 Subject: [PATCH 101/122] updated README file --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 08e0b93..fec0ead 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,9 @@ The Tuneit package works with computational graphs, which have two main phases: ## Example -A small example constructing a graph for the multiplication of a matrix and a vector: +This section contains a small example that shows the construction of a graph for the multiplication of a matrix and a vector. + +More details about this example can be found in the [example page](https://tuneit.readthedocs.io/en/latest/example.html) of the tuneit documentation. ```````````` @alternatives( @@ -68,7 +70,11 @@ mul=mat*vec mul=finalize(mul) mul.visualize() +``````````````` +The result of the `visualize()` function: +![visualised graph](docs/images/visualised_graph1.png) +``````````````` matrix_value = sp.random(100,100,0.1) vector_value = np.random.rand(100,1) @@ -77,8 +83,18 @@ out = mul(mat=matrix_value, vec=vector_value) # for comparing the options of the variable which_matrix: mul.benchmark(mat=matrix_value,vec=vector_value) -```````````` - +``````````````` +The result of the `benchmark()` function: + +| which_matrix | Time | +| :--------------: | :------------: | +| coo | 475.300 usec | +| csc | 1.076 msec | +| csr | 1.478 msec | +| bsr | 845.800 usec | +| matrix | 803.200 usec | + +
## Acknowledgments From 32876e7b440b656da8a2a3ee44ac6b35257b92b1 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 3 Jun 2021 13:38:18 +0300 Subject: [PATCH 102/122] fixed image placement --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fec0ead..720c588 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ mul=finalize(mul) mul.visualize() ``````````````` The result of the `visualize()` function: +
![visualised graph](docs/images/visualised_graph1.png) ``````````````` From 38240c523d8d689aa70f2b42a8fdef9af2d942fb Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 15:51:26 +0300 Subject: [PATCH 103/122] Updating workflows --- .github/workflows/black_n_pylint.yml | 4 +--- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/black_n_pylint.yml b/.github/workflows/black_n_pylint.yml index 2d84aa1..ccbbbde 100644 --- a/.github/workflows/black_n_pylint.yml +++ b/.github/workflows/black_n_pylint.yml @@ -25,8 +25,6 @@ jobs: run: | python -m pip install --upgrade pip pip install black lyncs_setuptools[pylint] - python setup.py egg_info - sudo pip install `grep -v '^\[' *.egg-info/requires.txt` - name: Applying black formatting run: | @@ -43,7 +41,7 @@ jobs: - name: Pylint output run: | - badge=$(lyncs_pylint_badge . | sed "s/\&/\\\&/g") + badge=$(lyncs_pylint_badge --disable=import-error . | sed "s/\&/\\\&/g") badge_line=$(awk '/!\[pylint\]/ {print FNR}' README.md) sed -i "${badge_line}s#.*#${badge}#" README.md diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d641d6c..9dfb7a4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,6 +38,6 @@ jobs: run: | sudo pip install -e .[all] - - name: Run serial tests + - name: Run tests run: | pytest -v From 85492b1ebe90e02238f93dbb4c83e67ae3f76d73 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 12:52:44 +0000 Subject: [PATCH 104/122] Updating pylint score (from Github Action) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 720c588..b62397e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![license](https://img.shields.io/github/license/Lyncs-API/tuneit?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/blob/master/LICENSE) [![build & test](https://img.shields.io/github/workflow/status/Lyncs-API/tuneit/build%20&%20test?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/actions) [![codecov](https://img.shields.io/codecov/c/github/Lyncs-API/tuneit?logo=codecov&logoColor=white)](https://codecov.io/gh/Lyncs-API/tuneit) -[![pylint](https://img.shields.io/badge/pylint%20score-8.8%2F10-yellowgreen?logo=python&logoColor=white)](http://pylint.pycqa.org/) +[![pylint](https://img.shields.io/badge/pylint%20score-9.2%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) [![black](https://img.shields.io/badge/code%20style-black-000000.svg?logo=codefactor&logoColor=white)](https://github.com/ambv/black) Tuneit is a generic purpose tool for optimizing and crosschecking calculations. From 68debbcf672d261e9f3263e2cddf3a74b150a1ea Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 15:56:39 +0300 Subject: [PATCH 105/122] Updating workflow --- .github/workflows/ci_cd.yml | 177 ++++++++++++++++++++----------- .github/workflows/ci_cd_test.yml | 114 -------------------- 2 files changed, 113 insertions(+), 178 deletions(-) delete mode 100644 .github/workflows/ci_cd_test.yml diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index f4113f7..70b5873 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -1,81 +1,130 @@ -# This workflow updates the packages on PyPI +# This workflow builds and tests PRs -name: build & test +name: PR build & test on: + pull_request: + branches: + - "main" + paths: + - "lyncs*/__init__.py" push: branches: - - 'master' - paths-ignore: - - 'docs/**' + - "main" jobs: - build-n-publish: - runs-on: ubuntu-latest - + build-n-publish: + runs-on: ${{ matrix.os }} + strategy: + matrix: + py-version: + - 3.x + os: + - ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: '3.x' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - - - name: Install from source - run: | - pip install -e .[all] - - - name: Run tests - run: | - pytest -v --cov-report=xml - export CODECOV_TOKEN="${{ secrets.CODECOV_TOKEN }}" - bash <(curl -s https://codecov.io/bash) -f ./coverage.xml -n tuneit - - - name: Upload if not up to date - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.pypi_password }} - run: | - pip uninstall -y tuneit - pip install tuneit==$(lyncs_setuptools version) || ( + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.py-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.py-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install lyncs_setuptools + + - name: Check github ref + id: check-ref + env: + TEST_PASSWORD: ${{ secrets.test_pypi_password }} + PYPI_PASSWORD: ${{ secrets.pypi_password }} + run: | + if [[ $GITHUB_REF == 'refs/heads/main' || $GITHUB_REF == 'refs/heads/master' ]]; then + echo '::set-output name=main::true' + echo '::set-output name=extra::' + echo '::set-output name=url::' + echo "::set-output name=token::$PYPI_PASSWORD" + else + echo '::set-output name=main::false' + echo '::set-output name=extra::--extra-index-url https://test.pypi.org/simple/' + echo '::set-output name=url::--repository-url https://test.pypi.org/legacy/' + echo "::set-output name=token::$TEST_PASSWORD" + fi + + - name: Trying to install from pip + id: check-version + continue-on-error: true + env: + EXTRA: ${{ steps.check-ref.outputs.extra }} + run: | + pip install $EXTRA $(lyncs_setuptools name)[all]==$(lyncs_setuptools version) + + - name: Install from source + if: ${{ steps.check-version.outcome == 'failure' }} + run: | + pip install -e .[all] + + - name: Run tests + run: | + pytest -v + + - name: Upload if not up to date + if: ${{ steps.check-version.outcome == 'failure' }} + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ steps.check-ref.outputs.token }} + URL: ${{ steps.check-ref.outputs.url }} + run: | pip install twine python setup.py sdist - twine upload dist/* - count=0 - while ! pip install tuneit==$(lyncs_setuptools version) && [ $count -lt 20 ]; do - sleep 1 - count=$((count+1)) - done - ) + twine upload $URL dist/* clean-run: - needs: build-n-publish - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.6, 3.7, 3.8] + py-version: + - 3.6 + - 3.7 + - 3.8 + os: + - ubuntu-latest steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - - - name: Install via pip - run: | - pip install tuneit[all] - - - name: Run tests - run: | - pytest -v --import-mode=importlib + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.py-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.py-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install lyncs_setuptools + + - name: Check github ref + id: check-ref + run: | + if [[ $GITHUB_REF == 'refs/heads/main' || $GITHUB_REF == 'refs/heads/master' ]]; then + echo '::set-output name=extra::' + else + echo '::set-output name=extra::--extra-index-url https://test.pypi.org/simple/' + fi + + - name: Install via pip + env: + EXTRA: ${{ steps.check-ref.outputs.extra }} + run: | + # Keeps trying installing until succeeds (needs to wait for PyPi to update the index) + for i in $(seq 5); do + [ $i -gt 1 ] && sleep 20 + pip install $EXTRA $(lyncs_setuptools name)[all]==$(lyncs_setuptools version) && s=0 && break || s=$? + done + (exit $s) + + - name: Run tests + run: | + pytest -v --import-mode=importlib diff --git a/.github/workflows/ci_cd_test.yml b/.github/workflows/ci_cd_test.yml deleted file mode 100644 index 0826e37..0000000 --- a/.github/workflows/ci_cd_test.yml +++ /dev/null @@ -1,114 +0,0 @@ -# This workflow builds and tests PRs - -name: PR build & test - -on: - pull_request: - branches: - - 'master' - paths-ignore: - - 'docs/**' - -jobs: - build-n-publish: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Set up Python - uses: actions/setup-python@v1 - with: - python-version: '3.x' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - - - name: Applying black formatting - run: | - pip install black - black --diff . - black . - - - name: Pushing changes if any - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Applying black formatting (from Github Action) - commit_user_name: sbacchio - commit_user_email: s.bacchio@gmail.com - commit_author: Simone Bacchio - - - name: Install from source - run: | - pip install -e .[all] - - - name: Pylint output - run: | - pip install lyncs_setuptools[pylint] - badge=$(lyncs_pylint_badge --fail-under 8 . | sed "s/\&/\\\&/g") - badge_line=$(awk '/!\[pylint\]/ {print FNR}' README.md) - sed -i "${badge_line}s#.*#${badge}#" README.md - - - name: Pushing changes if any - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Updating pylint score (from Github Action) - commit_user_name: sbacchio - commit_user_email: s.bacchio@gmail.com - commit_author: Simone Bacchio - - - name: Run tests - run: | - pytest -v - - - name: Run lyncs_setuptools - run: | - lyncs_setuptools - - - name: Upload if not up to date - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.test_pypi_password }} - run: | - pip uninstall -y tuneit - pip install --extra-index-url https://test.pypi.org/simple/ tuneit==$(lyncs_setuptools version) || ( - pip install twine - python setup.py sdist - twine upload --repository-url https://test.pypi.org/legacy/ dist/* - count=0 - while ! pip install --extra-index-url https://test.pypi.org/simple/ tuneit==$(lyncs_setuptools version) && [ $count -lt 20 ]; do - sleep 1 - count=$((count+1)) - done - ) - - clean-run: - - needs: build-n-publish - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.6, 3.7, 3.8] - - steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - - - name: Install via pip - run: | - pip install --extra-index-url https://test.pypi.org/simple/ tuneit[all] - - - name: Run tests - run: | - pytest -v --import-mode=importlib From b11939dc8fdba37898dd39ff3b7b49e9197610c6 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 15:57:12 +0300 Subject: [PATCH 106/122] Incrementing version number --- tuneit/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tuneit/__init__.py b/tuneit/__init__.py index df85f5c..fbec7ba 100644 --- a/tuneit/__init__.py +++ b/tuneit/__init__.py @@ -1,6 +1,6 @@ "A Python tool for optimizing and crosschecking calculations" -__version__ = "0.0.4" +__version__ = "0.1.0" from .graph import * from .tunable import * From d6b305b02131d2f75cd7a0bddc8e1777be296719 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 15:59:11 +0300 Subject: [PATCH 107/122] Updating workflow --- .github/workflows/ci_cd.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 70b5873..8479ad2 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -6,11 +6,13 @@ on: pull_request: branches: - "main" + - "master" paths: - "lyncs*/__init__.py" push: branches: - "main" + - "master" jobs: build-n-publish: From aeacd2b2416b7a682d8c2a757b7333466ec0ae5f Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 16:00:40 +0300 Subject: [PATCH 108/122] Updating __init__ --- tuneit/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tuneit/__init__.py b/tuneit/__init__.py index fbec7ba..5202e47 100644 --- a/tuneit/__init__.py +++ b/tuneit/__init__.py @@ -1,4 +1,4 @@ -"A Python tool for optimizing and crosschecking calculations" +"A Python tool for optimizing, benchmarking and crosschecking calculations" __version__ = "0.1.0" From 2d1750498508f76a91bbea7ad2b468824139da1f Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Thu, 3 Jun 2021 16:02:15 +0300 Subject: [PATCH 109/122] Updating workflow --- .github/workflows/ci_cd.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci_cd.yml b/.github/workflows/ci_cd.yml index 8479ad2..75ed379 100644 --- a/.github/workflows/ci_cd.yml +++ b/.github/workflows/ci_cd.yml @@ -7,8 +7,6 @@ on: branches: - "main" - "master" - paths: - - "lyncs*/__init__.py" push: branches: - "main" From dd63ceb0e226dbba580beae1b0ab27466c1fa4d1 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 28 Jun 2021 16:28:29 +0300 Subject: [PATCH 110/122] data object values are now passed correctly as kwargs --- tuneit/tools/optuna.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py index 21efd62..2e6d34d 100644 --- a/tuneit/tools/optuna.py +++ b/tuneit/tools/optuna.py @@ -68,7 +68,7 @@ def catches(self): def compute(self, **kwargs): "Returns the value of the graph after completing the set number of trials for the tuning of the parameters" self.get_study().optimize( - lambda trial: self.objective(trial, **kwargs), + lambda trial: self.objective(trial, **{**self.compute_kwargs, **kwargs}), self.n_trials, catch=self.catches, ) @@ -78,7 +78,7 @@ def compute(self, **kwargs): def _call_wrapper(self, graph, **kwargs): "Computes and returns the value of the graph" - self._value = graph.compute(**kwargs) + self._value = graph(**kwargs) return self._value def objective(self, trial, **kwargs): From 1439039a01ecde7aa731c01a059074db793e61ab Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 28 Jun 2021 16:29:53 +0300 Subject: [PATCH 111/122] data object values are now included in sampler_kwargs --- tuneit/tools/tuner.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tuneit/tools/tuner.py b/tuneit/tools/tuner.py index d56d4c7..5b0f322 100644 --- a/tuneit/tools/tuner.py +++ b/tuneit/tools/tuner.py @@ -51,11 +51,13 @@ def get_sampler(self): def get_sampler_kwargs(self): "Returns the appropriate arguments for the selected sampler" sampler_kwargs = {} + for d in self.datas: + y = d.split("-")[0] + if y in list(self.tuner_kwargs.keys()): + sampler_kwargs[y] = self.tuner_kwargs.get(y, None) if self.get_sampler() is OptunaSampler: - sampler_kwargs = { - "callback": self.tuner_kwargs.get("callback", None), - "storage": "sqlite:///example.db", - } + sampler_kwargs["callback"] = self.tuner_kwargs.get("callback", None) + sampler_kwargs["storage"] = "sqlite:///example.db" return sampler_kwargs From c60ed82e1551b85af06f0ca794d795e468f5a939 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 26 Jul 2021 13:11:27 +0300 Subject: [PATCH 112/122] added tests that check if an object is able to be pickled --- test/test_finalize.py | 37 +++++++++++++++++++++++++++++++++++++ test/test_graph.py | 22 ++++++++++++++++++++++ test/test_tunable.py | 36 +++++++++++++++++++++++++++++++++--- test/test_variable.py | 10 ++++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) diff --git a/test/test_finalize.py b/test/test_finalize.py index baf4f63..3e832b0 100644 --- a/test/test_finalize.py +++ b/test/test_finalize.py @@ -1,11 +1,16 @@ from tuneit.graph import visualize from tuneit.tunable import * from tuneit.variable import * +from tuneit.class_utils import * from tuneit.tunable import Tunable from tuneit.finalize import finalize from pytest import raises from tuneit.variable import Variable from tuneit.finalize import HighLevel +import testfixtures +import pickle +import scipy.sparse as sp +import scipy as s def test_finalize(): @@ -108,3 +113,35 @@ def test_finalize(): assert set( [dep for node in nodes for dep in node.first_dependencies if dep not in nodes] ) == set(list(merged_graph.get_node(last_node).first_dependencies)) + + +def test_pickle(): + # HighLevel + x = data() + y = data() + z = x * y + z = finalize(z) + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None + + # HighLevel object with alternatives: alternatives is not pickle-able + + # @alternatives( + # coo = sp.coo_matrix, + # csc = sp.csc_matrix, + # csr = sp.csr_matrix, + # bsr = sp.bsr_matrix + # ) + # def matrix(mat): + # return s.matrix(mat.todense()) + + # mat=data(info=["shape","dtype"]) + # vec=data(info=["shape","dtype"]) + # mat=matrix(mat) + # mul=mat*vec + # mul = finalize(mul) + + # a = pickle.dumps(mul) + # b = pickle.loads(a) + # assert testfixtures.compare(mul,b, strict=True) is None diff --git a/test/test_graph.py b/test/test_graph.py index 0982c9d..ae74206 100644 --- a/test/test_graph.py +++ b/test/test_graph.py @@ -1,5 +1,7 @@ import pytest from tuneit.graph import Graph, Node, Key +import pickle +import testfixtures class String(str): @@ -65,3 +67,23 @@ def test_dict_methods(): b.update(a) assert a == b + + +def test_pickle(): + # Key + z = Key("z") + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None + + # Node + z = Node("letter", "z") + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None + + # Graph + z = Graph({"letter": "z", "number": 1}) + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None diff --git a/test/test_tunable.py b/test/test_tunable.py index 8628afe..f3f6269 100644 --- a/test/test_tunable.py +++ b/test/test_tunable.py @@ -1,8 +1,9 @@ -from pickle import dumps +import pickle from pytest import raises from tuneit.graph import visualize, Node from tuneit.tunable import * -from tuneit.tunable import Tunable +from tuneit.tunable import Tunable, Data +import testfixtures class unpickable: @@ -84,4 +85,33 @@ def test_tunable(): with raises(TypeError): bool(tunable(1)) - assert dumps(a) + assert pickle.dumps(a) + + +def test_pickle(): + # Object + z = Object(2, label="z") + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None + + # Data + z = Data("z") + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None + + # Function + z = Function(str) + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None + + # Tunable + x = data() + y = data() + z = x * y + a = pickle.dumps(z) + b = pickle.loads(a) + assert type(z) == type(b) == Tunable + assert testfixtures.compare(Node(z), Node(b), strict=True) is None diff --git a/test/test_variable.py b/test/test_variable.py index f57fb41..d597508 100644 --- a/test/test_variable.py +++ b/test/test_variable.py @@ -4,6 +4,8 @@ from tuneit.tunable import Tunable from tuneit.finalize import finalize from pytest import raises +import pickle +import testfixtures def test_variable(): @@ -71,3 +73,11 @@ def test_copy(): assert not var2.fixed assert var.fixed assert var.uid != var2.uid + + +def test_pickle(): + # Variable + z = Variable([1, 2]) + a = pickle.dumps(z) + b = pickle.loads(a) + assert testfixtures.compare(z, b, strict=True) is None From dfad749ae5dc339990e6b8806d7186122e948481 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 26 Jul 2021 17:44:58 +0300 Subject: [PATCH 113/122] changed dict_keys in alternatives to a tuple because they are not picklable --- tuneit/class_utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tuneit/class_utils.py b/tuneit/class_utils.py index 2ff4779..43c9e0d 100644 --- a/tuneit/class_utils.py +++ b/tuneit/class_utils.py @@ -285,6 +285,8 @@ def __call__(self, *args, _key=None, **kwargs): return function( self, *args, - _key=variable(self.keys(), default=self.default, label=self.var_name), + _key=variable( + tuple(self.keys()), default=self.default, label=self.var_name + ), **kwargs, ) From 5c0574639239c3fe86ec4f3d57fbec7e6f141147 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Mon, 26 Jul 2021 17:46:58 +0300 Subject: [PATCH 114/122] added __reduce__ methods to classes in order to make them picklable --- tuneit/graph.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tuneit/graph.py b/tuneit/graph.py index 12e9327..7fc8ae8 100644 --- a/tuneit/graph.py +++ b/tuneit/graph.py @@ -84,6 +84,9 @@ def copy(self): "Shallow copy of a Graph" return Graph(self.backend.copy()) + def __reduce__(self): + return type(self), (self.backend,) + class Key(metaclass=CastableType, attrs=["key"]): "Namespace for the keys of tunable objects" @@ -110,6 +113,9 @@ def __getattr__(self, key): def __hash__(self): return hash(self.key) + def __reduce__(self): + return type(self), (self.key,) + class Node(Graph, Key, bind=False): """ @@ -134,6 +140,12 @@ def __init__(self, key, value=None): except TypeError: pass + def __reduce__(self): + return type(self), ( + self.key, + self.graph, + ) + @property def key(self): "Returns the key of the node" From f3afaf5c971ac21b1b2fac65599aff514eeafd75 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Thu, 29 Jul 2021 14:24:32 +0300 Subject: [PATCH 115/122] 1. added maximum number of failed trials before error is raise, 2. fixed handling of compute_kwargs --- tuneit/tools/optuna.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tuneit/tools/optuna.py b/tuneit/tools/optuna.py index 2e6d34d..da2fe28 100644 --- a/tuneit/tools/optuna.py +++ b/tuneit/tools/optuna.py @@ -65,20 +65,28 @@ def get_study(self): def catches(self): return (Exception,) - def compute(self, **kwargs): + def compute(self, count=0, **kwargs): "Returns the value of the graph after completing the set number of trials for the tuning of the parameters" - self.get_study().optimize( - lambda trial: self.objective(trial, **{**self.compute_kwargs, **kwargs}), - self.n_trials, - catch=self.catches, - ) - value = self._value - del self._value - return value + try: + self.get_study().optimize( + lambda trial: self.objective( + trial, **{**self.compute_kwargs, **kwargs} + ), + self.n_trials, + catch=self.catches, + ) + value = self._value + del self._value + return value + except: + if count < 10: + return self.compute(count=count + 1, **kwargs) + else: + raise RuntimeError("Trial failed too many times") def _call_wrapper(self, graph, **kwargs): "Computes and returns the value of the graph" - self._value = graph(**kwargs) + self._value = graph(compute_kwargs=kwargs) return self._value def objective(self, trial, **kwargs): From 2f7c3610917234c2ddf74a716d18dd5bb939f893 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Fri, 30 Jul 2021 13:01:10 +0000 Subject: [PATCH 116/122] Updating pylint score (from Github Action) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b62397e..b1d682d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![license](https://img.shields.io/github/license/Lyncs-API/tuneit?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/blob/master/LICENSE) [![build & test](https://img.shields.io/github/workflow/status/Lyncs-API/tuneit/build%20&%20test?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/actions) [![codecov](https://img.shields.io/codecov/c/github/Lyncs-API/tuneit?logo=codecov&logoColor=white)](https://codecov.io/gh/Lyncs-API/tuneit) -[![pylint](https://img.shields.io/badge/pylint%20score-9.2%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) +[![pylint](https://img.shields.io/badge/pylint%20score-9.1%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) [![black](https://img.shields.io/badge/code%20style-black-000000.svg?logo=codefactor&logoColor=white)](https://github.com/ambv/black) Tuneit is a generic purpose tool for optimizing and crosschecking calculations. From 06f74da7f3e50c73efdd8d1453acf1d85d37a6f4 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 30 Jul 2021 18:04:15 +0300 Subject: [PATCH 117/122] updated setup file with testfixtures --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0a45a27..ce7d987 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ "graph": [ "graphviz", ], - "test": ["pytest", "pytest-cov"], + "test": ["pytest", "pytest-cov","testfixtures"], "optuna": ["optuna"], "record": ["pandas"], } From a4fa61a763aed0c2520673cc98f2e3cdd49dad50 Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Fri, 30 Jul 2021 15:07:04 +0000 Subject: [PATCH 118/122] Applying black formatting (from Github Action) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ce7d987..ccdce53 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ "graph": [ "graphviz", ], - "test": ["pytest", "pytest-cov","testfixtures"], + "test": ["pytest", "pytest-cov", "testfixtures"], "optuna": ["optuna"], "record": ["pandas"], } From 7516160b09642406a4934a2337c5290daa4602b0 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 6 Aug 2021 11:46:45 +0300 Subject: [PATCH 119/122] call to optimize updated to match changes about the handling of compute_kwargs in optuna.py --- docs/example.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/example.rst b/docs/example.rst index 5518912..4a78dec 100644 --- a/docs/example.rst +++ b/docs/example.rst @@ -240,7 +240,7 @@ The only thing left to do is to actually tune the variable by calling the follow .. code-block:: python - mul.optimize(mat=matrix_value,vec=vector_value,sampler='optuna')() + mul.optimize(sampler='optuna')(mat=matrix_value,vec=vector_value) A tuner object has been created by calling the :code:`optimize()` function on the graph to be tuned and passing it the sampler to be used. The :code:`optuna` package is one of the options that are offered by :code:`tuneit` to be used as a sampler. From 3ae9344d7be89aabc845167f9525497a09c817f7 Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 6 Aug 2021 11:55:54 +0300 Subject: [PATCH 120/122] 1. added logging using context managers, 2. added the __reduce__ method to make the tunable class pickle-able, 3. multiprocessing draft in compute function (problems occur when trying to return things from the process), 4. fixed compute function so that it does not skip attribute errors when those occur during computation --- tuneit/tunable.py | 61 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/tuneit/tunable.py b/tuneit/tunable.py index c92e6c7..1f008e6 100644 --- a/tuneit/tunable.py +++ b/tuneit/tunable.py @@ -24,6 +24,10 @@ from varname import varname as _varname, VarnameRetrievingError from .graph import Graph, Node, Key from lyncs_utils import isiterable +import multiprocessing +from time import time +from contextlib import contextmanager +import logging def varname(caller=1, default=None): @@ -34,8 +38,23 @@ def varname(caller=1, default=None): return default -def compute(obj, **kwargs): +def compute(obj, timeout=None, **kwargs): "Compute the value of a tunable object" + if timeout: + raise NotImplementedError + kwargs["queue"] = multiprocessing.Queue() + p = multiprocessing.Process(target=compute, args=(obj,), kwargs=kwargs) + p.start() + p.join(timeout) + if p.exitcode is None: + p.terminate() + raise RuntimeError("Timeout") + if p.exitcode < 0: + raise RuntimeError(f"The process was terminated by signal {abs(exitcode)}.") + if not kwargs["queue"].empty(): + obj = kwargs["queue"].get() + return obj + kwargs.setdefault("maxiter", 3) if kwargs["maxiter"] <= 0: return obj @@ -43,12 +62,10 @@ def compute(obj, **kwargs): kwargs.setdefault("graph", Node(obj).graph) if isinstance(obj, Key) and not isinstance(obj, Node): obj = kwargs["graph"][obj].value - try: + if hasattr(obj, "__compute__"): obj = obj.__compute__(**kwargs) kwargs["maxiter"] -= 1 obj = compute(obj, **kwargs) - except AttributeError: - pass return obj @@ -300,8 +317,17 @@ def __compute__(self, **kwargs): cmpt = lambda obj: compute(obj, **kwargs) fnc = cmpt(super().__compute__(**kwargs)) args = tuple(map(cmpt, self.args)) + context = kwargs.get("context", default_context) + log = kwargs.get("log", False) kwargs = dict(zip(self.kwargs.keys(), map(cmpt, self.kwargs.values()))) - res = fnc(*args, **kwargs) + if log: + logging.basicConfig(level=logging.INFO) + else: + logging.basicConfig(level=logging.WARNING) + if context is None: + context = empty_context + with context(self.key): + res = fnc(*args, **kwargs) if res is None: if ismethod(fnc) or fnc is setattr: return args[0] @@ -354,6 +380,18 @@ def callattr(self, key, *args, **kwargs): return getattr(self, key)(*args, **kwargs) +@contextmanager +def default_context(key): + start = time() + yield + logging.info(f"{key}:{time()-start}") + + +@contextmanager +def empty_context(key): + yield + + class Tunable(Node, bind=False): "A class that turns any operation into a graph node" @@ -376,11 +414,15 @@ def __call__(self, *args, **kwargs): and len(tmp.args) == 2 and not tmp.kwargs ): - value = Function(callattr, args=tmp.args + args, kwargs=kwargs) graph = Graph(self).copy() del graph[Key(self)] - graph[value.key] = value - return Tunable(graph[value.key]) + node = graph[tmp.args[0]] + return function(callattr, node, tmp.args[1], *args, **kwargs) + # value = Function(callattr, args=tmp.args + args, kwargs=kwargs) + # graph = Graph(self).copy() + # del graph[Key(self)] + # graph[value.key] = value + # return Tunable(graph[value.key]) return function(Tunable(Node(self).copy()), *args, **kwargs) def __repr__(self): @@ -394,6 +436,9 @@ def __bool__(self): def __getstate__(self): return Node(self).key + def __reduce__(self): + return type(self), (Node(self),) + def __compute__(self, **kwargs): # pylint: disable=W0613 return Node(self).value From b737470673b4c2213102f368585fcdea84c146bd Mon Sep 17 00:00:00 2001 From: Raphaella Demetriou Date: Fri, 6 Aug 2021 11:58:19 +0300 Subject: [PATCH 121/122] formatted --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ce7d987..ccdce53 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ "graph": [ "graphviz", ], - "test": ["pytest", "pytest-cov","testfixtures"], + "test": ["pytest", "pytest-cov", "testfixtures"], "optuna": ["optuna"], "record": ["pandas"], } From d7e216fb223c0bee570aad24a9c8e1977ff6167c Mon Sep 17 00:00:00 2001 From: Simone Bacchio Date: Fri, 6 Aug 2021 09:01:26 +0000 Subject: [PATCH 122/122] Updating pylint score (from Github Action) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b1d682d..7a7232e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![license](https://img.shields.io/github/license/Lyncs-API/tuneit?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/blob/master/LICENSE) [![build & test](https://img.shields.io/github/workflow/status/Lyncs-API/tuneit/build%20&%20test?logo=github&logoColor=white)](https://github.com/Lyncs-API/tuneit/actions) [![codecov](https://img.shields.io/codecov/c/github/Lyncs-API/tuneit?logo=codecov&logoColor=white)](https://codecov.io/gh/Lyncs-API/tuneit) -[![pylint](https://img.shields.io/badge/pylint%20score-9.1%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) +[![pylint](https://img.shields.io/badge/pylint%20score-9.0%2F10-green?logo=python&logoColor=white)](http://pylint.pycqa.org/) [![black](https://img.shields.io/badge/code%20style-black-000000.svg?logo=codefactor&logoColor=white)](https://github.com/ambv/black) Tuneit is a generic purpose tool for optimizing and crosschecking calculations.