From 87b6b3ca27599ca67deeb37fdb403306d9c9410b Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 3 Oct 2021 16:52:54 +0200 Subject: [PATCH] Feat: move to global_config, configs for generator --- mapytex/calculus/random/core/__init__.py | 18 ++--- mapytex/calculus/random/core/generate.py | 80 ++++++++++----------- mapytex/calculus/random/core/random_tree.py | 28 +++++--- mapytex/calculus/random/list.py | 27 +++++-- 4 files changed, 86 insertions(+), 67 deletions(-) diff --git a/mapytex/calculus/random/core/__init__.py b/mapytex/calculus/random/core/__init__.py index 895060e..a51462b 100644 --- a/mapytex/calculus/random/core/__init__.py +++ b/mapytex/calculus/random/core/__init__.py @@ -7,7 +7,7 @@ # Distributed under terms of the MIT license. """ -Tools to extract random leafs, random variables, generate random values and +Tools to extract random leaves, random variables, generate random values and fill new trees Flow @@ -17,7 +17,7 @@ Tree with RdLeaf | | Extract rdLeaf | -List of leafs to generate +List of leaves to generate | | extract_rv | @@ -27,9 +27,9 @@ List random variables to generate | Dictionnary of generated random variables | -| Compute leafs +| Compute leaves | -Dictionnary of computed leafs +Dictionnary of computed leaves | | Replace | @@ -46,15 +46,15 @@ Tree with RdLeaf replaced by generated values / > {a} > {a*k} ->>> leafs = rd_t.random_leaf ->>> leafs = ['a', 'a*k'] ->>> rd_varia = extract_letters(leafs) +>>> leaves = rd_t.random_leaves +>>> leaves = ['a', 'a*k'] +>>> rd_varia = extract_letters(leaves) >>> sorted(list(rd_varia)) ['a', 'k'] ->>> generated = random_generator(rd_varia, conditions=['a%2+1']) +>>> generated = random_generator(rd_varia, conditions=['a%2+1'], global_config={"min_max": (-10, 10), "rejected":[0, 1]}) >>> generated # doctest: +SKIP {'a': 7, 'k': 4} ->>> computed = eval_words(leafs, generated) +>>> computed = eval_words(leaves, generated) >>> computed # doctest: +SKIP {'a': 7, 'a*k': 28} >>> replaced = rd_t.eval_random_leaves(computed) diff --git a/mapytex/calculus/random/core/generate.py b/mapytex/calculus/random/core/generate.py index 4098aeb..6835a92 100644 --- a/mapytex/calculus/random/core/generate.py +++ b/mapytex/calculus/random/core/generate.py @@ -10,63 +10,60 @@ from random import choice -def build_variable_scope(rd_variables, rejected, min_max, variables_scope): - """ Build variables scope from incomplete one +def complete_variable_configs(variables, global_config:dict={}, configs:dict={})->dict: + """ Completes variables configurations with the global configuration - :param rd_variables: list of random variables to generate - :param rejected: Rejected values for the generator - :param min_max: (min, max) limits in between variables will be generated - :param variables_scope: rejected and min_max define for individual variables + :param variables: list of random variables to generate + :param global_config: global parameters + :param configs: global parameters :return: complete variable scope :example: - >>> completed = build_variable_scope(["a", "b", "c", "d"], [0], (-10, 10), - ... {"a": {"rejected": [0, 1]}, + >>> completed = complete_variable_configs(["a", "b", "c", "d"], + ... global_config={"rejected": [], "min_max": (-10, 10)}, + ... configs={ + ... "a": {"rejected": [0, 1]}, ... "b": {"min_max": (-5, 0)}, - ... "c": {"rejected": [2], "min_max": (0, 5)}}) - >>> complete = {'a': {'rejected': [0, 1], 'min_max': (-10, 10)}, - ... 'b': {'rejected': [0], 'min_max': (-5, 0)}, - ... 'c': {'rejected': [2], 'min_max': (0, 5)}, - ... 'd': {'rejected': [0], 'min_max': (-10, 10)}} - >>> completed == complete + ... "c": {"rejected": [2], "min_max": (0, 5)} + ... }) + >>> completed["a"] == {'rejected': [0, 1], 'min_max': (-10, 10)} + True + >>> completed["b"] == {'rejected': [], 'min_max': (-5, 0)} + True + >>> completed['c'] == {'rejected': [2], 'min_max': (0, 5)} + True + >>> completed['d'] == {'rejected': [], 'min_max': (-10, 10)} True """ - complete_scope = variables_scope.copy() - for v in rd_variables: + complete_configs = configs.copy() + for variable in variables: try: - complete_scope[v] + complete_configs[variable] except KeyError: - complete_scope[v] = {"rejected": rejected, "min_max": min_max} + complete_configs[variable] = global_config else: - try: - complete_scope[v]["rejected"] - except KeyError: - complete_scope[v]["rejected"] = rejected - try: - complete_scope[v]["min_max"] - except KeyError: - complete_scope[v]["min_max"] = min_max - return complete_scope + complete_configs[variable] = dict(global_config, **configs[variable]) + return complete_configs def random_generator( - rd_variables, conditions=[], rejected=[0], min_max=(-10, 10), variables_scope={} -): + variables:list[str], conditions:list[str]=[], global_config:dict={}, configs:dict={}, +)-> dict[str, int]: """ Generate random variables - :param rd_variables: list of random variables to generate + :param variables: list of random variables to generate :param conditions: condition over variables - :param rejected: Rejected values for the generator (default [0]) - :param min_max: (min, max) limits in between variables will be generated - :param variables_scope: rejected and min_max define for individual variables + :param global_config: global parameters + :param configs: global parameters :return: dictionnary of generated variables :example: >>> gene = random_generator(["a", "b"], ... ["a > 0"], - ... [0], (-10, 10), + ... {"rejected": [0], "min_max":(-10, 10)}, ... {"a": {"rejected": [0, 1]}, - ... "b": {"min_max": (-5, 0)}}) + ... "b": {"min_max": (-5, 0)}, + ... }) >>> gene["a"] > 0 True >>> gene["a"] != 0 @@ -75,7 +72,8 @@ def random_generator( True >>> gene = random_generator(["a", "b"], ... ["a % b == 0"], - ... [0, 1], (-10, 10)) + ... {"rejected": [0, 1], "min_max":(-10, 10)} + ... ) >>> gene["a"] not in [0, 1] True >>> gene["b"] in list(range(-10, 11)) @@ -83,8 +81,8 @@ def random_generator( >>> gene["a"] % gene["b"] 0 """ - complete_scope = build_variable_scope( - rd_variables, rejected, min_max, variables_scope + complete_scope = complete_variable_configs( + variables, global_config, configs ) choices_list = { v: list( @@ -94,17 +92,17 @@ def random_generator( ) ).difference(complete_scope[v]["rejected"]) ) - for v in rd_variables + for v in variables } # quantity_choices = reduce(lambda x,y : x*y, # [len(choices_list[v]) for v in choices_list]) # TODO: améliorer la méthode de rejet avec un cache |dim. mai 12 17:04:11 CEST 2019 - generate_variable = {v: choice(choices_list[v]) for v in rd_variables} + generate_variable = {v: choice(choices_list[v]) for v in variables} while not all([eval(c, __builtins__, generate_variable) for c in conditions]): - generate_variable = {v: choice(choices_list[v]) for v in rd_variables} + generate_variable = {v: choice(choices_list[v]) for v in variables} return generate_variable diff --git a/mapytex/calculus/random/core/random_tree.py b/mapytex/calculus/random/core/random_tree.py index 68ddb08..cf17ee2 100644 --- a/mapytex/calculus/random/core/random_tree.py +++ b/mapytex/calculus/random/core/random_tree.py @@ -1,5 +1,6 @@ from ...core.tree import MutableTree, Tree -from .grammar import extract_letters +from .grammar import extract_letters, eval_words +from .generate import random_generator from .str2 import rdstr2 class RandomTree(MutableTree): @@ -35,23 +36,18 @@ class RandomTree(MutableTree): """ str_2_mut_tree = rdstr2(cls.sink) return str_2_mut_tree(expression) - - def generate(self, conditions:list[str]=[], rejected:list[int]=[0, 1], min_max:tuple[int]=(-10, 10),scopes:dict={}) -> Tree: - """ Generate a random version of self """ - - pass @property - def random_leaf(self) -> list[str]: + def random_leaves(self) -> list[str]: """ Get list of random leaves :example: >>> from .leaf import RdLeaf >>> random_tree = RandomTree("+", RdLeaf("a"), RdLeaf("a*k")) - >>> random_tree.random_leaf + >>> random_tree.random_leaves ['a', 'a*k'] >>> random_tree = RandomTree("+", RdLeaf("a"), 2) - >>> random_tree.random_leaf + >>> random_tree.random_leaves ['a'] """ rd_leafs = [] @@ -78,7 +74,7 @@ class RandomTree(MutableTree): >>> random_tree.random_value {'a'} """ - return extract_letters(self.random_leaf) + return extract_letters(self.random_leaves) def eval_random_leaves(self, leaves_value:dict[str, int]): @@ -102,3 +98,15 @@ class RandomTree(MutableTree): except AttributeError: return leaf return self.map_on_leaf(replace) + + def generate(self, conditions:list[str]=[], config:dict={} , configs:dict={}) -> Tree: + """ Generate a random version of self + + :param conditions: list of conditions + :param config: global configuration for generated values + :param configs: specific configuration for each generated values + + """ + generated_values = random_generator(self.random_values, config, configs) + leaves = eval_words(self.random_leaves, generated_values) + return self.eval_random_leaves(leaves) diff --git a/mapytex/calculus/random/list.py b/mapytex/calculus/random/list.py index c0ca10b..5ab1e45 100644 --- a/mapytex/calculus/random/list.py +++ b/mapytex/calculus/random/list.py @@ -12,7 +12,12 @@ This function ignores tree structure and works with lists from .core.generate import random_generator from .core.grammar import extract_letters, eval_words -def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), variables_scope={}, dictionnary=False): +DEFAUTL_CONFIG = { + "rejected": [0], + "min_max": (-10, 10), + } + +def list_generator(var_list:list[str], conditions:list[str]=[], global_config:dict={}, configs:dict={})->list[int]: """ Generate random computed values from the list :param rd_variables: list of random variables to generate (can be computed value - "a*b") @@ -20,7 +25,6 @@ def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), var :param rejected Rejected values for the generator (default [0]) :param min_max: (min, max) limits in between variables will be generated :param variables_scope: rejected and min_max define for individual variables - :param dictionnary: the return value will be a dictionnary with var_list as keys (default False) :return: dictionnary of generated variables :example: @@ -33,12 +37,21 @@ def list_generator(var_list, conditions=[], rejected=[0], min_max=(-10, 10), var -20 >>> a, b # doctest: +SKIP 5, -4 - >>> list_generator(["a", "a*b", "b", "c"], dictionnary=True) # doctest: +SKIP - {'a': -3, 'a*b': 18, 'b': -6, 'c': -4} + >>> a, ab, b, c = list_generator(["a", "a*b", "b", "c"], conditions=["a-b==0"]) + >>> a - b == 0 + True + >>> a, ab, b, c = list_generator(["a", "a*b", "b", "c"], global_config={"rejected": [2, 3, 5, 7]}) + >>> a not in [2, 3, 5, 7] + True + >>> b not in [2, 3, 5, 7] + True + >>> c not in [2, 3, 5, 7] + True + >>> a, ab, b, c = list_generator(["a", "a*b", "b", "c"], configs={"a": {"rejected": [2, 3, 5, 7]}) + >>> a not in [2, 3, 5, 7] + True """ rv = extract_letters(var_list) - rv_gen = random_generator(rv, conditions, rejected, min_max, variables_scope) + rv_gen = random_generator(rv, conditions, dict(DEFAUTL_CONFIG, **global_config), variables_scope) generated = eval_words(var_list, rv_gen) - if dictionnary: - return generated return [generated[v] for v in var_list]