From 030ded7c4b6724a0284c3c273b5424857208c2a2 Mon Sep 17 00:00:00 2001 From: KirbyG Date: Sat, 30 Mar 2024 17:32:19 +0000 Subject: [PATCH 1/3] establish pattern: use existing parameter passing to enable optional user-specified random seed --- .gitignore | 1 + alogos/_optimization/ea/algorithm.py | 1 - .../_optimization/ea/operators/selection.py | 19 +++++++++---------- alogos/_optimization/ea/parameters.py | 2 ++ alogos/systems/_shared/crossover.py | 12 +++++------- alogos/systems/_shared/init_individual.py | 5 ++++- alogos/systems/cfggp/crossover.py | 9 ++++----- alogos/systems/cfggpst/crossover.py | 10 ++++------ alogos/systems/dsge/crossover.py | 6 ++---- alogos/systems/ge/_parameters.py | 1 + alogos/systems/whge/crossover.py | 2 +- 11 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e99e36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/alogos/_optimization/ea/algorithm.py b/alogos/_optimization/ea/algorithm.py index 5923a50..3a2dda9 100644 --- a/alogos/_optimization/ea/algorithm.py +++ b/alogos/_optimization/ea/algorithm.py @@ -1,6 +1,5 @@ """Data structure for a evolutionary algorithm that uses G3P systems.""" -import random as _random from numbers import Number as _Number import pylru as _pylru diff --git a/alogos/_optimization/ea/operators/selection.py b/alogos/_optimization/ea/operators/selection.py index 16c74cc..d847b33 100644 --- a/alogos/_optimization/ea/operators/selection.py +++ b/alogos/_optimization/ea/operators/selection.py @@ -1,4 +1,3 @@ -import random as _random from functools import lru_cache as _lru_cache from math import isfinite as _isfinite from math import isnan as _isnan @@ -17,7 +16,7 @@ def uniform(individuals, sample_size, objective, parameters, state): - Eiben, Introduction to Evolutionary Computing (2e 2015): p. 86 """ - sel_inds = _uniform_sampling_with_replacement(individuals, sample_size) + sel_inds = _uniform_sampling_with_replacement(individuals, sample_size, parameters) return sel_inds @@ -60,7 +59,7 @@ def tournament(individuals, sample_size, objective, parameters, state): sel_inds = [] if objective == "min": for _ in range(sample_size): - competitors = _uniform_sampling_with_replacement(individuals, ts) + competitors = _uniform_sampling_with_replacement(individuals, ts, parameters) winner = competitors[0] for competitor in competitors[1:]: if competitor.less_than(winner, objective): @@ -68,7 +67,7 @@ def tournament(individuals, sample_size, objective, parameters, state): sel_inds.append(winner) else: for _ in range(sample_size): - competitors = _uniform_sampling_with_replacement(individuals, ts) + competitors = _uniform_sampling_with_replacement(individuals, ts, parameters) winner = competitors[0] for competitor in competitors[1:]: if competitor.greater_than(winner, objective): @@ -89,7 +88,7 @@ def rank_proportional(individuals, sample_size, objective, parameters, state): probabilities = _calculate_rank_probabilities(num_inds, mu, eta_plus) # Sampling - sel_inds = _stochastic_universal_sampling(srt_inds, probabilities, sample_size) + sel_inds = _stochastic_universal_sampling(srt_inds, probabilities, sample_size, parameters) return sel_inds @@ -115,7 +114,7 @@ def fitness_proportional(individuals, sample_size, objective, parameters, state) probabilities = _calculate_fitness_probabilities(scaled_fitnesses, usage_tracking) # Sampling - sel_inds = _stochastic_universal_sampling(individuals, probabilities, sample_size) + sel_inds = _stochastic_universal_sampling(individuals, probabilities, sample_size, parameters) return sel_inds @@ -231,13 +230,13 @@ def _sort_individuals(individuals, reverse=False): # Sampling algorithms: draw individuals from a population according to a probability distribution -def _uniform_sampling_with_replacement(population, sample_size): +def _uniform_sampling_with_replacement(population, sample_size, paremeters): """Uniform sampling with replacement.""" - selected_individuals = [_random.choice(population) for _ in range(sample_size)] + selected_individuals = [parameters["rng"].choice(population) for _ in range(sample_size)] return selected_individuals -def _stochastic_universal_sampling(population, probabilities, sample_size): +def _stochastic_universal_sampling(population, probabilities, sample_size, parameters): """Stochastic universal sampling (SUS) for drawing individuals from a probability distribution. A sampling algorithm by James Baker, designed as improvement to roulette wheel sampling. @@ -251,7 +250,7 @@ def _stochastic_universal_sampling(population, probabilities, sample_size): lambda_inv = 1.0 / sample_size # Draw from uniform distribution - rand_uniform = _random.uniform(0.0, lambda_inv) + rand_uniform = parameters["rng"].uniform(0.0, lambda_inv) # Decide which individual gets how many children (=copies of itself) num_children = [] diff --git a/alogos/_optimization/ea/parameters.py b/alogos/_optimization/ea/parameters.py index e25df05..e964b3f 100644 --- a/alogos/_optimization/ea/parameters.py +++ b/alogos/_optimization/ea/parameters.py @@ -1,5 +1,6 @@ """Default parameters for an evolutionary algorithm.""" +import random as _random from ..._utilities.parametrization import ParameterCollection as _ParameterCollection @@ -9,6 +10,7 @@ population_size=100, offspring_size=100, verbose=False, + rng=_random.Random(), # Storage database_on=False, database_location=":memory:", diff --git a/alogos/systems/_shared/crossover.py b/alogos/systems/_shared/crossover.py index 3d86fab..70e79fb 100644 --- a/alogos/systems/_shared/crossover.py +++ b/alogos/systems/_shared/crossover.py @@ -1,7 +1,5 @@ """Shared crossover functions for several systems.""" -from random import randint as _ri - from ... import exceptions as _exceptions @@ -51,11 +49,11 @@ def two_point_length_preserving(grammar, gt1, gt2, parameters, representation): _exceptions.raise_crossover_lp_error2() # Get a random segment in genotype 1: choose two valid random points - s1, e1 = _get_two_different_points(l1) + s1, e1 = _get_two_different_points(l1, parameters) # Get a random segment in genotype 2: choose a valid start position for a same-sized segment lseg = e1 - s1 - s2 = _ri(0, l2 - lseg) + s2 = parameters["rng"].randint(0, l2 - lseg) e2 = s2 + lseg # Crossover: Swap two randomly positioned, but equally long segments @@ -64,11 +62,11 @@ def two_point_length_preserving(grammar, gt1, gt2, parameters, representation): return _GT(n1), _GT(n2) -def _get_two_different_points(n): +def _get_two_different_points(n, parameters): """Get two different numbers between 0 and n-1 to use as indices.""" while True: - p1 = _ri(0, n) - p2 = _ri(0, n) + p1 = parameters["rng"].randint(0, n) + p2 = parameters["rng"].randint(0, n) if p1 == p2: continue if (p1 == 0 and p2 == n) or (p1 == n and p2 == 0): diff --git a/alogos/systems/_shared/init_individual.py b/alogos/systems/_shared/init_individual.py index 0c2aced..4c4bb02 100644 --- a/alogos/systems/_shared/init_individual.py +++ b/alogos/systems/_shared/init_individual.py @@ -193,12 +193,15 @@ def random_genotype(grammar, parameters, default_parameters, representation, map # Parameter extraction gl = _get_given_or_default("genotype_length", parameters, default_parameters) cs = _get_given_or_default("codon_size", parameters, default_parameters) + + seed = _get_given_or_default("seed", parameters, default_parameters) + rng = _random.Random(seed) # Random(None) creates an unseeded generator (default) # Transformation try: assert cs > 0 max_int = 2**cs - 1 - random_genotype = [_random.randint(0, max_int) for _ in range(gl)] + random_genotype = [rng.randint(0, max_int) for _ in range(gl)] except Exception: _exceptions.raise_init_ind_rand_gt_error() diff --git a/alogos/systems/cfggp/crossover.py b/alogos/systems/cfggp/crossover.py index 2d104b3..ccb3ea3 100644 --- a/alogos/systems/cfggp/crossover.py +++ b/alogos/systems/cfggp/crossover.py @@ -1,13 +1,11 @@ """Crossover functions for CFG-GP.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import default_parameters as _default_parameters from . import representation as _representation -def subtree_exchange(grammar, genotype1, genotype2, parameters=None): +def subtree_exchange(grammar, genotype1, genotype2, parameters): """Generate new CFG-GP genotypes by exchanging suitable subtrees. Randomly select nodes containing the same nonterminal in two trees @@ -74,6 +72,7 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters=None): """ # Parameter extraction max_depth = _get_given_or_default("max_depth", parameters, _default_parameters) + rng = _get_given_or_default("rng", parameters, _default_parameters) # Argument processing if not isinstance(genotype1, _representation.Genotype): @@ -89,10 +88,10 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters=None): si = s1.intersection(s2) if si: # Randomly select a non-terminal in the first tree, which is also part of second tree - n1 = _random.choice([n for n in ns1 if n.symbol.text in si]) + n1 = rng.choice([n for n in ns1 if n.symbol.text in si]) t1 = n1.symbol.text # Randomly select the same non-terminal in the second genotype - n2 = _random.choice([n for n in ns2 if n.symbol.text == t1]) + n2 = rng.choice([n for n in ns2 if n.symbol.text == t1]) # Swap subtrees by exchanging the list of child nodes n1.children, n2.children = n2.children, n1.children # Ensure max_depth constraint is not violated diff --git a/alogos/systems/cfggpst/crossover.py b/alogos/systems/cfggpst/crossover.py index 5365e90..f510e20 100644 --- a/alogos/systems/cfggpst/crossover.py +++ b/alogos/systems/cfggpst/crossover.py @@ -1,7 +1,5 @@ """Crossover functions for CFG-GP-ST.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import default_parameters as _default_parameters from . import representation as _representation @@ -10,10 +8,9 @@ # Shortcuts for minor speedup _GT = _representation.Genotype _fe = _representation._find_subtree_end -_rc = _random.choice -def subtree_exchange(grammar, genotype1, genotype2, parameters=None): +def subtree_exchange(grammar, genotype1, genotype2, parameters): """Generate new CFG-GP-ST genotypes by exchanging suitable subtrees. Randomly select nodes containing the same nonterminal in two trees @@ -89,6 +86,7 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters=None): # Parameter extraction mn = _get_given_or_default("max_nodes", parameters, _default_parameters) + rng = _get_given_or_default("rng", parameters, default_parameters) # Crossover s1, c1 = genotype1.data @@ -97,11 +95,11 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters=None): nb = {s for s in s2 if s in n1} # set of nonterminals in both trees if nb: # - Randomly select a nonterminal in tree 1, which is also present in tree 2 - a1 = _rc([i for i, s in enumerate(s1) if s in nb]) + a1 = rng.choice([i for i, s in enumerate(s1) if s in nb]) b1 = _fe(a1, c1) + 1 # - Randomly select the same nonterminal at some position in tree 2 sym = s1[a1] - a2 = _rc([i for i, s in enumerate(s2) if s == sym]) + a2 = rng.choice([i for i, s in enumerate(s2) if s == sym]) b2 = _fe(a2, c2) + 1 # - Swap: only if max_nodes condition is not violated afterwards for both trees l1 = b1 - a1 diff --git a/alogos/systems/dsge/crossover.py b/alogos/systems/dsge/crossover.py index ece7e88..7ecea70 100644 --- a/alogos/systems/dsge/crossover.py +++ b/alogos/systems/dsge/crossover.py @@ -1,7 +1,5 @@ """Crossover functions for DSGE.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import default_parameters as _default_parameters from . import repair as _repair @@ -10,7 +8,6 @@ # Shortcuts for minor speedup _GT = _representation.Genotype -_rc = _random.choice def gene_swap(grammar, genotype1, genotype2, parameters=None): @@ -68,6 +65,7 @@ def gene_swap(grammar, genotype1, genotype2, parameters=None): repair = _get_given_or_default( "repair_after_crossover", parameters, _default_parameters ) + rng = _get_given_or_default("rng", parameters, default_parameters) # Argument processing if not isinstance(genotype1, _GT): @@ -79,7 +77,7 @@ def gene_swap(grammar, genotype1, genotype2, parameters=None): d1 = genotype1.data d2 = genotype2.data vals = (True, False) - mask = [_rc(vals) for _ in range(len(d1))] + mask = [rng.choice(vals) for _ in range(len(d1))] n1 = tuple(d1[i] if x else d2[i] for i, x in enumerate(mask)) n2 = tuple(d2[i] if x else d1[i] for i, x in enumerate(mask)) diff --git a/alogos/systems/ge/_parameters.py b/alogos/systems/ge/_parameters.py index 16f8634..f2e95a3 100644 --- a/alogos/systems/ge/_parameters.py +++ b/alogos/systems/ge/_parameters.py @@ -12,6 +12,7 @@ "max_expansions": 1_000_000, # Reverse mapping "codon_randomization": True, + "seed": None, # Individual initialization "init_ind_operator": "random_genotype", "init_ind_given_genotype": None, diff --git a/alogos/systems/whge/crossover.py b/alogos/systems/whge/crossover.py index d72ffd3..5584e3d 100644 --- a/alogos/systems/whge/crossover.py +++ b/alogos/systems/whge/crossover.py @@ -4,7 +4,7 @@ from . import representation as _representation -def two_point_length_preserving(grammar, genotype1, genotype2, parameters=None): +def two_point_length_preserving(grammar, genotype1, genotype2, parameters): """Generate new WHGE genotypes by exchanging sequence parts. Select two random, but equally long subsequences in the two From 8a8abeaa4ec95bf84b0b7c2e808431cebb098734 Mon Sep 17 00:00:00 2001 From: KirbyG Date: Mon, 1 Apr 2024 23:05:01 +0000 Subject: [PATCH 2/3] random seeding used everywhere --- alogos/_grammar/data_structures.py | 5 ++++- alogos/_optimization/ea/algorithm.py | 2 +- alogos/_utilities/operating_system.py | 11 +++++++++++ alogos/systems/_shared/crossover.py | 6 +++--- alogos/systems/_shared/init_individual.py | 7 +------ alogos/systems/_shared/init_tree.py | 17 ++++++++--------- alogos/systems/_shared/neighborhood.py | 8 ++++---- alogos/systems/cfggp/crossover.py | 5 ++--- alogos/systems/cfggp/mutation.py | 6 ++---- alogos/systems/cfggp/neighborhood.py | 4 ++-- alogos/systems/cfggpst/crossover.py | 5 ++--- alogos/systems/cfggpst/mutation.py | 11 +++-------- alogos/systems/cfggpst/neighborhood.py | 4 ++-- alogos/systems/dsge/_cached_calculations.py | 8 +++----- alogos/systems/dsge/crossover.py | 3 +-- alogos/systems/dsge/init_individual.py | 1 + alogos/systems/dsge/mutation.py | 13 +++++++------ alogos/systems/dsge/neighborhood.py | 4 ++-- alogos/systems/dsge/repair.py | 2 ++ alogos/systems/ge/mapping.py | 4 +--- alogos/systems/ge/neighborhood.py | 4 ++-- alogos/systems/pige/mapping.py | 8 +++----- alogos/systems/pige/neighborhood.py | 4 ++-- alogos/systems/whge/init_individual.py | 4 +--- alogos/systems/whge/mutation.py | 6 ++---- alogos/systems/whge/neighborhood.py | 4 ++-- 26 files changed, 74 insertions(+), 82 deletions(-) diff --git a/alogos/_grammar/data_structures.py b/alogos/_grammar/data_structures.py index 4a53851..c5fd781 100644 --- a/alogos/_grammar/data_structures.py +++ b/alogos/_grammar/data_structures.py @@ -126,6 +126,7 @@ class Grammar: "production_rules", "start_symbol", "_cache", + "rng", ) # Initialization and reset @@ -246,6 +247,8 @@ def __init__( ): _warnings._warn_multiple_grammar_specs() + self.rng = kwargs.pop("rng", _random.Random()) + # Transformation if bnf_text is not None: self.from_bnf_text(bnf_text, **kwargs) @@ -2560,7 +2563,7 @@ def node_seq_repr(node_seq): derivation = [[self.root_node]] stack = [self.root_node] while stack: - idx = _random.randint(0, len(stack) - 1) + idx = rng.randint(0, len(stack) - 1) nt_node = stack.pop(idx) derivation = next_derivation_step(derivation, nt_node, nt_node.children) new_nt_nodes = [node for node in nt_node.children if node.children] diff --git a/alogos/_optimization/ea/algorithm.py b/alogos/_optimization/ea/algorithm.py index 3a2dda9..55eed75 100644 --- a/alogos/_optimization/ea/algorithm.py +++ b/alogos/_optimization/ea/algorithm.py @@ -969,7 +969,7 @@ def _cross_over(self, parent_population): n = len(parent_population) while len(crossed_over_individuals) < n: if n > 2: - parent0, parent1 = _random.sample(parent_population.individuals, 2) + parent0, parent1 = pr["rng"].sample(parent_population.individuals, 2) elif n == 2: parent0, parent1 = parent_population.individuals else: diff --git a/alogos/_utilities/operating_system.py b/alogos/_utilities/operating_system.py index c2d6532..d4f0ffb 100644 --- a/alogos/_utilities/operating_system.py +++ b/alogos/_utilities/operating_system.py @@ -41,6 +41,16 @@ def delete_file(filepath): """ _os.remove(filepath) +def ensure_new_path_2(path): + while _os.path.exists(path): + root, ext = _os.path.splitext(path) + a, b, c = root.rpartition('_') + if c.isdecimal(): + c = str(int(c) + 1) + else: + c = c + '_1' + path = a + b + c + ext + return path def ensure_new_path(path): """Check whether a given path exists. If yes, try to find a novel, incremented variant of it. @@ -95,6 +105,7 @@ def parse_type3(string): path = base + str(int(num) + 1) else: path = path + "_1" + print(f'ensure_new_path returns {path}') return path diff --git a/alogos/systems/_shared/crossover.py b/alogos/systems/_shared/crossover.py index 70e79fb..9de332c 100644 --- a/alogos/systems/_shared/crossover.py +++ b/alogos/systems/_shared/crossover.py @@ -53,7 +53,7 @@ def two_point_length_preserving(grammar, gt1, gt2, parameters, representation): # Get a random segment in genotype 2: choose a valid start position for a same-sized segment lseg = e1 - s1 - s2 = parameters["rng"].randint(0, l2 - lseg) + s2 = grammar.rng.randint(0, l2 - lseg) e2 = s2 + lseg # Crossover: Swap two randomly positioned, but equally long segments @@ -65,8 +65,8 @@ def two_point_length_preserving(grammar, gt1, gt2, parameters, representation): def _get_two_different_points(n, parameters): """Get two different numbers between 0 and n-1 to use as indices.""" while True: - p1 = parameters["rng"].randint(0, n) - p2 = parameters["rng"].randint(0, n) + p1 = grammar.rng.randint(0, n) + p2 = grammar.rng.randint(0, n) if p1 == p2: continue if (p1 == 0 and p2 == n) or (p1 == n and p2 == 0): diff --git a/alogos/systems/_shared/init_individual.py b/alogos/systems/_shared/init_individual.py index 4c4bb02..f5b7fb8 100644 --- a/alogos/systems/_shared/init_individual.py +++ b/alogos/systems/_shared/init_individual.py @@ -1,7 +1,5 @@ """Shared individual initialization functions of different systems.""" -import random as _random - from ... import exceptions as _exceptions from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import init_tree as _init_tree @@ -194,14 +192,11 @@ def random_genotype(grammar, parameters, default_parameters, representation, map gl = _get_given_or_default("genotype_length", parameters, default_parameters) cs = _get_given_or_default("codon_size", parameters, default_parameters) - seed = _get_given_or_default("seed", parameters, default_parameters) - rng = _random.Random(seed) # Random(None) creates an unseeded generator (default) - # Transformation try: assert cs > 0 max_int = 2**cs - 1 - random_genotype = [rng.randint(0, max_int) for _ in range(gl)] + random_genotype = [grammar.rng.randint(0, max_int) for _ in range(gl)] except Exception: _exceptions.raise_init_ind_rand_gt_error() diff --git a/alogos/systems/_shared/init_tree.py b/alogos/systems/_shared/init_tree.py index 3e78c7b..2904c3a 100644 --- a/alogos/systems/_shared/init_tree.py +++ b/alogos/systems/_shared/init_tree.py @@ -2,7 +2,6 @@ import copy as _copy import itertools as _itertools -import random as _random from ... import exceptions as _exceptions from ..._grammar import data_structures as _data_structures @@ -59,7 +58,7 @@ def uniform(grammar, max_expansions=10_000): # 2) Choose rule: randomly rules = grammar.production_rules[chosen_nt_node.symbol] if len(rules) > 1: - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) else: chosen_rule_idx = 0 chosen_rule = rules[chosen_rule_idx] @@ -123,7 +122,7 @@ def weighted(grammar, max_expansions=10_000, reduction_factor=0.96): def weighted_choice(lhs, weights): rhs_list = grammar.production_rules[lhs] weight_list = weights[lhs] - chosen_rule_cumulative_weight = sum(weight_list) * _random.random() + chosen_rule_cumulative_weight = sum(weight_list) * grammar.rng.random() chosen_rule_idx = 0 for cumulative_weight in _itertools.accumulate(weight_list): if cumulative_weight >= chosen_rule_cumulative_weight: @@ -207,7 +206,7 @@ def ptc2(grammar, max_expansions=100): expansions = 0 while stack: # 1) Choose nonterminal: random - chosen_nt_idx = _random.choice(range(len(stack))) + chosen_nt_idx = grammar.rng.choice(range(len(stack))) chosen_nt_node = stack.pop(chosen_nt_idx) # 2) Choose rule: randomly from those that do not lead over the wanted expansions rules = grammar.production_rules[chosen_nt_node.symbol] @@ -222,7 +221,7 @@ def ptc2(grammar, max_expansions=100): min_expansions_per_symbol, stack, ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) @@ -267,7 +266,7 @@ def grow_one_branch_to_max_depth(grammar, max_depth=20): stack = [(dt.root_node, 0)] while stack: # 1) Choose nonterminal: random - chosen_nt_idx = _random.choice(range(len(stack))) + chosen_nt_idx = grammar.rng.choice(range(len(stack))) chosen_nt_node, depth = stack.pop(chosen_nt_idx) # 2) Choose rule: randomly from those that do not lead over the wanted depth rules = grammar.production_rules[chosen_nt_node.symbol] @@ -288,7 +287,7 @@ def grow_one_branch_to_max_depth(grammar, max_depth=20): rules = _filter_rules_for_grow( chosen_nt_node.symbol, rules, depth, max_depth, min_depths ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) @@ -333,7 +332,7 @@ def grow_all_branches_within_max_depth(grammar, max_depth=20): rules = _filter_rules_for_grow( chosen_nt_node.symbol, rules, depth, max_depth, min_depths ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) @@ -381,7 +380,7 @@ def grow_all_branches_to_max_depth(grammar, max_depth=20): rules = _filter_rules_for_full( chosen_nt_node.symbol, rules, depth, max_depth, min_depths, is_recursive ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) diff --git a/alogos/systems/_shared/neighborhood.py b/alogos/systems/_shared/neighborhood.py index cb996f2..8f3663d 100644 --- a/alogos/systems/_shared/neighborhood.py +++ b/alogos/systems/_shared/neighborhood.py @@ -3,10 +3,9 @@ import functools as _functools import itertools as _itertools import operator as _operator -import random as _random -def generate_combinations(num_choices_per_pos, distance, max_size=None): +def generate_combinations(num_choices_per_pos, distance, parameters, max_size=None): """Generate all combinations of choices available at each position. In each returned combination, the value 0 means that the original choice @@ -59,6 +58,7 @@ def generate_combinations(num_choices_per_pos, distance, max_size=None): max_size, count, count_per_pos_comb, + parameters ) else: # If no, generate the entire set @@ -75,11 +75,11 @@ def _product(iterable): def _generate_some_combinations( - choices_per_pos, pos_combinations, num_pos, max_size, count, count_per_pos_comb + choices_per_pos, pos_combinations, num_pos, max_size, count, count_per_pos_comb, parameters ): """Generate a random subset of all possible combinations.""" # Choose random neighbors by selecting indices from the enumerated possibilities - chosen = sorted(_random.sample(range(count), max_size)) + chosen = sorted(parameters["rng"].sample(range(count), max_size)) # Construct the chosen neighbors if chosen: diff --git a/alogos/systems/cfggp/crossover.py b/alogos/systems/cfggp/crossover.py index ccb3ea3..dca4cbc 100644 --- a/alogos/systems/cfggp/crossover.py +++ b/alogos/systems/cfggp/crossover.py @@ -72,7 +72,6 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): """ # Parameter extraction max_depth = _get_given_or_default("max_depth", parameters, _default_parameters) - rng = _get_given_or_default("rng", parameters, _default_parameters) # Argument processing if not isinstance(genotype1, _representation.Genotype): @@ -88,10 +87,10 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): si = s1.intersection(s2) if si: # Randomly select a non-terminal in the first tree, which is also part of second tree - n1 = rng.choice([n for n in ns1 if n.symbol.text in si]) + n1 = grammar.rng.choice([n for n in ns1 if n.symbol.text in si]) t1 = n1.symbol.text # Randomly select the same non-terminal in the second genotype - n2 = rng.choice([n for n in ns2 if n.symbol.text == t1]) + n2 = grammar.rng.choice([n for n in ns2 if n.symbol.text == t1]) # Swap subtrees by exchanging the list of child nodes n1.children, n2.children = n2.children, n1.children # Ensure max_depth constraint is not violated diff --git a/alogos/systems/cfggp/mutation.py b/alogos/systems/cfggp/mutation.py index 50431ab..5a7e23f 100644 --- a/alogos/systems/cfggp/mutation.py +++ b/alogos/systems/cfggp/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for CFG-GP.""" -import random as _random - from ..._grammar import data_structures as _data_structures from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from .._shared import _cached_calculations @@ -59,7 +57,7 @@ def subtree_replacement(grammar, genotype, parameters=None): nodes_and_depths.append((node, depth)) stack = stack + [(node, depth + 1) for node in node.children] # - Randomly select a node for mutation - node, depth = _random.choice(nodes_and_depths) + node, depth = grammar.rng.choice(nodes_and_depths) # - Replace the node's subtree with a randomly generated new one node.children = [] _grow_random_subtree(grammar, max_depth, start_depth=depth, root_node=node) @@ -85,7 +83,7 @@ def _grow_random_subtree(grammar, max_depth, start_depth, root_node): rules = _filter_rules_for_grow( chosen_nt_node.symbol, rules, depth, max_depth, min_depths ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) diff --git a/alogos/systems/cfggp/neighborhood.py b/alogos/systems/cfggp/neighborhood.py index e13b03c..239bccb 100644 --- a/alogos/systems/cfggp/neighborhood.py +++ b/alogos/systems/cfggp/neighborhood.py @@ -13,7 +13,7 @@ _T = _grammar.data_structures.TerminalSymbol -def subtree_replacement(grammar, genotype, parameters=None): +def subtree_replacement(grammar, genotype, parameters): """Systematically change a chosen number of nodes. Parameters @@ -58,7 +58,7 @@ def subtree_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Neighborhood construction diff --git a/alogos/systems/cfggpst/crossover.py b/alogos/systems/cfggpst/crossover.py index f510e20..50de921 100644 --- a/alogos/systems/cfggpst/crossover.py +++ b/alogos/systems/cfggpst/crossover.py @@ -86,7 +86,6 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): # Parameter extraction mn = _get_given_or_default("max_nodes", parameters, _default_parameters) - rng = _get_given_or_default("rng", parameters, default_parameters) # Crossover s1, c1 = genotype1.data @@ -95,11 +94,11 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): nb = {s for s in s2 if s in n1} # set of nonterminals in both trees if nb: # - Randomly select a nonterminal in tree 1, which is also present in tree 2 - a1 = rng.choice([i for i, s in enumerate(s1) if s in nb]) + a1 = grammar.rng.choice([i for i, s in enumerate(s1) if s in nb]) b1 = _fe(a1, c1) + 1 # - Randomly select the same nonterminal at some position in tree 2 sym = s1[a1] - a2 = rng.choice([i for i, s in enumerate(s2) if s == sym]) + a2 = grammar.rng.choice([i for i, s in enumerate(s2) if s == sym]) b2 = _fe(a2, c2) + 1 # - Swap: only if max_nodes condition is not violated afterwards for both trees l1 = b1 - a1 diff --git a/alogos/systems/cfggpst/mutation.py b/alogos/systems/cfggpst/mutation.py index 62f8d63..cfc6140 100644 --- a/alogos/systems/cfggpst/mutation.py +++ b/alogos/systems/cfggpst/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for CFG-GP-ST.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from .. import _shared from . import _cached_calculations @@ -12,9 +10,6 @@ # Shortcuts for minor speedup _GT = _representation.Genotype _fe = _representation._find_subtree_end -_rc = _random.choice -_ri = _random.randint -_rs = _random.shuffle def subtree_replacement(grammar, genotype, parameters=None): @@ -71,7 +66,7 @@ def subtree_replacement(grammar, genotype, parameters=None): # Mutation # - Random choice of a nonterminal to mutate s1, c1 = genotype.data - a1 = _rc([i for i, c in enumerate(c1) if c]) # choose idx of a random nonterminal + a1 = grammar.rng.choice([i for i, c in enumerate(c1) if c]) # choose idx of a random nonterminal b1 = _fe(a1, c1) + 1 # find idx of last symbol in its subtree # - Grow a random new subtree s2, c2 = _grow_random_subtree(grammar, s1[a1], mn - len(s1) + (b1 - a1)) @@ -106,11 +101,11 @@ def trav(sy): try: # 1) Symbol is a nonterminal, therefore requires expansion and will have >0 children # Choose rule: randomly from those rules that do not lead over the wanted num nodes - rhs = _rc(_filter_rules(sy, ipr[sy], imn[sy], max_nodes - n)) + rhs = grammar.rng.choice(_filter_rules(sy, ipr[sy], imn[sy], max_nodes - n)) # Expand the nonterminal with the rhs of the chosen rule m = len(rhs) r = list(range(m)) - _rs(r) + grammar.rng.shuffle(r) ret = [None] * m for i in r: # Recursive call for each child in random order, return in original order (dfs) diff --git a/alogos/systems/cfggpst/neighborhood.py b/alogos/systems/cfggpst/neighborhood.py index e5625f5..4d29128 100644 --- a/alogos/systems/cfggpst/neighborhood.py +++ b/alogos/systems/cfggpst/neighborhood.py @@ -14,7 +14,7 @@ _T = _grammar.data_structures.TerminalSymbol -def subtree_replacement(grammar, genotype, parameters=None): +def subtree_replacement(grammar, genotype, parameters): """Systematically change a chosen number of nodes. Parameters @@ -60,7 +60,7 @@ def subtree_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Neighborhood construction diff --git a/alogos/systems/dsge/_cached_calculations.py b/alogos/systems/dsge/_cached_calculations.py index 55d07ce..00bd33d 100644 --- a/alogos/systems/dsge/_cached_calculations.py +++ b/alogos/systems/dsge/_cached_calculations.py @@ -1,5 +1,3 @@ -import random as _random - from ... import _grammar @@ -63,14 +61,14 @@ def get_first_valid_codon( def get_random_valid_codon( - nt, current_depth, max_depth, nt_to_num_options, non_recursive_rhs + nt, current_depth, max_depth, nt_to_num_options, non_recursive_rhs, grammar ): - """Get a random valid codons depending on current and max depth.""" + """Get a random valid codon depending on current and max depth.""" # Used in stochastic repair mechanism options = get_all_valid_codons( nt, current_depth, max_depth, nt_to_num_options, non_recursive_rhs ) - return _random.choice(options) + return grammar.rng.choice(options) def get_tree_depth(grammar, genotype, nt_to_gene, nt_to_cnt): diff --git a/alogos/systems/dsge/crossover.py b/alogos/systems/dsge/crossover.py index 7ecea70..ec7ea43 100644 --- a/alogos/systems/dsge/crossover.py +++ b/alogos/systems/dsge/crossover.py @@ -65,7 +65,6 @@ def gene_swap(grammar, genotype1, genotype2, parameters=None): repair = _get_given_or_default( "repair_after_crossover", parameters, _default_parameters ) - rng = _get_given_or_default("rng", parameters, default_parameters) # Argument processing if not isinstance(genotype1, _GT): @@ -77,7 +76,7 @@ def gene_swap(grammar, genotype1, genotype2, parameters=None): d1 = genotype1.data d2 = genotype2.data vals = (True, False) - mask = [rng.choice(vals) for _ in range(len(d1))] + mask = [grammar.rng.choice(vals) for _ in range(len(d1))] n1 = tuple(d1[i] if x else d2[i] for i, x in enumerate(mask)) n2 = tuple(d2[i] if x else d1[i] for i, x in enumerate(mask)) diff --git a/alogos/systems/dsge/init_individual.py b/alogos/systems/dsge/init_individual.py index a632818..8cf2ed3 100644 --- a/alogos/systems/dsge/init_individual.py +++ b/alogos/systems/dsge/init_individual.py @@ -161,6 +161,7 @@ def random_genotype(grammar, parameters=None): init_max_depth, nt_to_num_options, non_recursive_rhs, + grammar, ) chosen_rule = rules[chosen_rule_idx] genotype[gene_idx].append(chosen_rule_idx) diff --git a/alogos/systems/dsge/mutation.py b/alogos/systems/dsge/mutation.py index 79c0632..f72bfb8 100644 --- a/alogos/systems/dsge/mutation.py +++ b/alogos/systems/dsge/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for DSGE.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import _cached_calculations from . import default_parameters as _dp @@ -79,7 +77,7 @@ def int_replacement_by_probability(grammar, genotype, parameters=None): # Mutation: Randomly decide for each positions in the genotype whether it shall be modified data = genotype.data num_pos = sum(len(data[gi]) for gi in mutable_genes) - pos = set(i for i in range(num_pos) if _random.random() < probability) + pos = set(i for i in range(num_pos) if grammar.rng.random() < probability) data = _change_chosen_positions( data, pos, @@ -89,6 +87,7 @@ def int_replacement_by_probability(grammar, genotype, parameters=None): non_recursive_rhs, nt_to_num_options, mutable_genes, + grammar, ) # Optional repair of the new genotype @@ -146,7 +145,7 @@ def int_replacement_by_count(grammar, genotype, parameters=None): num_pos = sum(len(data[gi]) for gi in mutable_genes) pos = range(num_pos) if num_pos > flip_count: - pos = _random.sample(pos, flip_count) + pos = grammar.rng.sample(pos, flip_count) pos = set(pos) data = _change_chosen_positions( data, @@ -157,6 +156,7 @@ def int_replacement_by_count(grammar, genotype, parameters=None): non_recursive_rhs, nt_to_num_options, mutable_genes, + grammar, ) # Optional repair of the new genotype (either here and/or later) @@ -176,6 +176,7 @@ def _change_chosen_positions( non_recursive_rhs, nt_to_num_options, mutable_genes, + grammar, ): """Mutate the selected positions by replacing the integer with another valid option. @@ -205,12 +206,12 @@ def _change_chosen_positions( ): # Note: >= is used in the original authors' implementation def get_new_codon(nt, codon): - return _random.choice([x for x in non_recursive_rhs[nt] if x != codon]) + return grammar.rng.choice([x for x in non_recursive_rhs[nt] if x != codon]) else: def get_new_codon(nt, codon): - return _random.choice( + return grammar.rng.choice( [x for x in range(nt_to_num_options[nt]) if x != codon] ) diff --git a/alogos/systems/dsge/neighborhood.py b/alogos/systems/dsge/neighborhood.py index b0b2706..62dc069 100644 --- a/alogos/systems/dsge/neighborhood.py +++ b/alogos/systems/dsge/neighborhood.py @@ -13,7 +13,7 @@ _T = _grammar.data_structures.TerminalSymbol -def int_replacement(grammar, genotype, parameters=None): +def int_replacement(grammar, genotype, parameters): """Change systematically chosen integers.""" # Parameter extraction distance = _get_given_or_default( @@ -39,7 +39,7 @@ def int_replacement(grammar, genotype, parameters=None): # Generate combinations combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Construct neighborhood genotypes from combinations diff --git a/alogos/systems/dsge/repair.py b/alogos/systems/dsge/repair.py index fb4e235..f37ba0e 100644 --- a/alogos/systems/dsge/repair.py +++ b/alogos/systems/dsge/repair.py @@ -113,6 +113,7 @@ def fix_genotype(grammar, genotype, parameters=None, raise_errors=True): max_depth, nt_to_num_options, non_recursive_rhs, + grammar, ) else: chosen_rule_idx = _cached_calculations.get_first_valid_codon( @@ -135,6 +136,7 @@ def fix_genotype(grammar, genotype, parameters=None, raise_errors=True): max_depth, nt_to_num_options, non_recursive_rhs, + grammar, ) else: chosen_rule_idx = _cached_calculations.get_first_valid_codon( diff --git a/alogos/systems/ge/mapping.py b/alogos/systems/ge/mapping.py index 3fd50e8..f766fe5 100644 --- a/alogos/systems/ge/mapping.py +++ b/alogos/systems/ge/mapping.py @@ -1,7 +1,5 @@ """Forward and reverse mapping functions for GE.""" -import random as _random - from ... import _grammar from ... import exceptions as _exceptions from ..._utilities.parametrization import get_given_or_default as _get_given_or_default @@ -365,7 +363,7 @@ def reverse( _exceptions.raise_limited_codon_size_error(chosen_rule_idx, max_int) if codon_randomization: options = range(chosen_rule_idx, max_int + 1, len(rules)) - chosen_rule_idx = _random.choice(options) + chosen_rule_idx = grammar.rng.choice(options) # 3) Expand the chosen nonterminal with the rhs of the chosen rule -> Follow the expansion new_nt_nodes = [ diff --git a/alogos/systems/ge/neighborhood.py b/alogos/systems/ge/neighborhood.py index e79494a..3ec5cd7 100644 --- a/alogos/systems/ge/neighborhood.py +++ b/alogos/systems/ge/neighborhood.py @@ -11,7 +11,7 @@ _T = _grammar.data_structures.TerminalSymbol -def int_replacement(grammar, genotype, parameters=None): +def int_replacement(grammar, genotype, parameters): """Systematically change a chosen number of int codons. Parameters @@ -65,7 +65,7 @@ def int_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Construct neighborhood genotypes from combinations diff --git a/alogos/systems/pige/mapping.py b/alogos/systems/pige/mapping.py index 878ba9c..5b97d5f 100644 --- a/alogos/systems/pige/mapping.py +++ b/alogos/systems/pige/mapping.py @@ -1,7 +1,5 @@ """Forward and reverse mapping functions for piGE.""" -import random as _random - from ... import _grammar from ... import exceptions as _exceptions from ..._utilities import argument_processing as _ap @@ -410,7 +408,7 @@ def reverse( elif derivation_order == "rightmost": chosen_nt_idx_tlo = len(stack_tlo) - 1 elif derivation_order == "random": - chosen_nt_idx_tlo = _random.randint(0, len(stack_tlo) - 1) + chosen_nt_idx_tlo = grammar.rng.randint(0, len(stack_tlo) - 1) chosen_nt_node = stack_tlo.pop(chosen_nt_idx_tlo) chosen_nt_idx = stack.index(chosen_nt_node) chosen_nt_idx_stored = chosen_nt_idx @@ -419,7 +417,7 @@ def reverse( _exceptions.raise_limited_codon_size_error(chosen_nt_idx, max_int) if codon_randomization: options = range(chosen_nt_idx, max_int + 1, len(stack) + 1) - chosen_nt_idx_stored = _random.choice(options) + chosen_nt_idx_stored = grammar.rng.choice(options) # 2) Choose rule: piGE decides via next "content codon" -> Deduce it from tree try: @@ -435,7 +433,7 @@ def reverse( _exceptions.raise_limited_codon_size_error(chosen_rule_idx, max_int) if codon_randomization: options = range(chosen_rule_idx, max_int + 1, len(rules)) - chosen_rule_idx = _random.choice(options) + chosen_rule_idx = grammar.rng.choice(options) # 3) Expand the chosen nonterminal with the rhs of the chosen rule -> Follow the expansion new_nt_nodes = [ diff --git a/alogos/systems/pige/neighborhood.py b/alogos/systems/pige/neighborhood.py index 9a1403b..005792c 100644 --- a/alogos/systems/pige/neighborhood.py +++ b/alogos/systems/pige/neighborhood.py @@ -13,7 +13,7 @@ _T = _grammar.data_structures.TerminalSymbol -def int_replacement(grammar, genotype, parameters=None): +def int_replacement(grammar, genotype, parameters): """Systematically change a chosen number of int codons. Parameters @@ -78,7 +78,7 @@ def int_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Construct neighborhood genotypes from combinations diff --git a/alogos/systems/whge/init_individual.py b/alogos/systems/whge/init_individual.py index 4a40673..5dcaaf9 100644 --- a/alogos/systems/whge/init_individual.py +++ b/alogos/systems/whge/init_individual.py @@ -1,7 +1,5 @@ """Initialization functions to create a WHGE individual.""" -import random as _random - from bitarray.util import int2ba as _int2ba from ... import exceptions as _exceptions @@ -67,7 +65,7 @@ def random_genotype(grammar, parameters=None): # Transformation try: - rand_int = _random.getrandbits(gl) + rand_int = grammar.rng.getrandbits(gl) rand_bitstring = _int2ba(rand_int, gl) rand_genotype = _representation.Genotype(rand_bitstring) except Exception: diff --git a/alogos/systems/whge/mutation.py b/alogos/systems/whge/mutation.py index 02611c4..d54442f 100644 --- a/alogos/systems/whge/mutation.py +++ b/alogos/systems/whge/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for WHGE.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import default_parameters as _dp from . import representation as _representation @@ -83,7 +81,7 @@ def bit_flip_by_probability(grammar, genotype, parameters=None): # Mutation: For each bit decide randomly whether it is flipped data = genotype.data.copy() for i in range(len(data)): - if _random.random() < probability: + if grammar.rng.random() < probability: data[i] = not data[i] return _representation.Genotype(data) @@ -103,7 +101,7 @@ def bit_flip_by_count(grammar, genotype, parameters=None): if flip_count > num_pos: positions = range(num_pos) else: - positions = _random.sample(range(num_pos), flip_count) + positions = grammar.rng.sample(range(num_pos), flip_count) for i in positions: data[i] = not data[i] return _representation.Genotype(data) diff --git a/alogos/systems/whge/neighborhood.py b/alogos/systems/whge/neighborhood.py index 0cb6419..60b6fba 100644 --- a/alogos/systems/whge/neighborhood.py +++ b/alogos/systems/whge/neighborhood.py @@ -6,7 +6,7 @@ from . import representation as _representation -def bit_flip(grammar, genotype, parameters=None): +def bit_flip(grammar, genotype, parameters): """Generate nearby genotypes by flipping n bits. Parameters @@ -44,7 +44,7 @@ def bit_flip(grammar, genotype, parameters=None): # Generate combinations combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) for comb in combinations: print(" ", comb) From 3dcd7aae4663e30268d773637e1c40470c4280ad Mon Sep 17 00:00:00 2001 From: KirbyG Date: Mon, 1 Apr 2024 23:05:01 +0000 Subject: [PATCH 3/3] random seeding used everywhere --- alogos/_grammar/data_structures.py | 5 ++++- alogos/_optimization/ea/algorithm.py | 2 +- alogos/_optimization/ea/operators/selection.py | 2 +- alogos/systems/_shared/crossover.py | 6 +++--- alogos/systems/_shared/init_individual.py | 7 +------ alogos/systems/_shared/init_tree.py | 17 ++++++++--------- alogos/systems/_shared/neighborhood.py | 8 ++++---- alogos/systems/cfggp/crossover.py | 5 ++--- alogos/systems/cfggp/mutation.py | 6 ++---- alogos/systems/cfggp/neighborhood.py | 4 ++-- alogos/systems/cfggpst/crossover.py | 5 ++--- alogos/systems/cfggpst/mutation.py | 11 +++-------- alogos/systems/cfggpst/neighborhood.py | 4 ++-- alogos/systems/dsge/_cached_calculations.py | 8 +++----- alogos/systems/dsge/crossover.py | 3 +-- alogos/systems/dsge/init_individual.py | 1 + alogos/systems/dsge/mutation.py | 13 +++++++------ alogos/systems/dsge/neighborhood.py | 4 ++-- alogos/systems/dsge/repair.py | 2 ++ alogos/systems/ge/mapping.py | 4 +--- alogos/systems/ge/neighborhood.py | 4 ++-- alogos/systems/pige/mapping.py | 8 +++----- alogos/systems/pige/neighborhood.py | 4 ++-- alogos/systems/whge/init_individual.py | 4 +--- alogos/systems/whge/mutation.py | 6 ++---- alogos/systems/whge/neighborhood.py | 4 ++-- 26 files changed, 64 insertions(+), 83 deletions(-) diff --git a/alogos/_grammar/data_structures.py b/alogos/_grammar/data_structures.py index 4a53851..c5fd781 100644 --- a/alogos/_grammar/data_structures.py +++ b/alogos/_grammar/data_structures.py @@ -126,6 +126,7 @@ class Grammar: "production_rules", "start_symbol", "_cache", + "rng", ) # Initialization and reset @@ -246,6 +247,8 @@ def __init__( ): _warnings._warn_multiple_grammar_specs() + self.rng = kwargs.pop("rng", _random.Random()) + # Transformation if bnf_text is not None: self.from_bnf_text(bnf_text, **kwargs) @@ -2560,7 +2563,7 @@ def node_seq_repr(node_seq): derivation = [[self.root_node]] stack = [self.root_node] while stack: - idx = _random.randint(0, len(stack) - 1) + idx = rng.randint(0, len(stack) - 1) nt_node = stack.pop(idx) derivation = next_derivation_step(derivation, nt_node, nt_node.children) new_nt_nodes = [node for node in nt_node.children if node.children] diff --git a/alogos/_optimization/ea/algorithm.py b/alogos/_optimization/ea/algorithm.py index 3a2dda9..55eed75 100644 --- a/alogos/_optimization/ea/algorithm.py +++ b/alogos/_optimization/ea/algorithm.py @@ -969,7 +969,7 @@ def _cross_over(self, parent_population): n = len(parent_population) while len(crossed_over_individuals) < n: if n > 2: - parent0, parent1 = _random.sample(parent_population.individuals, 2) + parent0, parent1 = pr["rng"].sample(parent_population.individuals, 2) elif n == 2: parent0, parent1 = parent_population.individuals else: diff --git a/alogos/_optimization/ea/operators/selection.py b/alogos/_optimization/ea/operators/selection.py index d847b33..e422744 100644 --- a/alogos/_optimization/ea/operators/selection.py +++ b/alogos/_optimization/ea/operators/selection.py @@ -230,7 +230,7 @@ def _sort_individuals(individuals, reverse=False): # Sampling algorithms: draw individuals from a population according to a probability distribution -def _uniform_sampling_with_replacement(population, sample_size, paremeters): +def _uniform_sampling_with_replacement(population, sample_size, parameters): """Uniform sampling with replacement.""" selected_individuals = [parameters["rng"].choice(population) for _ in range(sample_size)] return selected_individuals diff --git a/alogos/systems/_shared/crossover.py b/alogos/systems/_shared/crossover.py index 70e79fb..9de332c 100644 --- a/alogos/systems/_shared/crossover.py +++ b/alogos/systems/_shared/crossover.py @@ -53,7 +53,7 @@ def two_point_length_preserving(grammar, gt1, gt2, parameters, representation): # Get a random segment in genotype 2: choose a valid start position for a same-sized segment lseg = e1 - s1 - s2 = parameters["rng"].randint(0, l2 - lseg) + s2 = grammar.rng.randint(0, l2 - lseg) e2 = s2 + lseg # Crossover: Swap two randomly positioned, but equally long segments @@ -65,8 +65,8 @@ def two_point_length_preserving(grammar, gt1, gt2, parameters, representation): def _get_two_different_points(n, parameters): """Get two different numbers between 0 and n-1 to use as indices.""" while True: - p1 = parameters["rng"].randint(0, n) - p2 = parameters["rng"].randint(0, n) + p1 = grammar.rng.randint(0, n) + p2 = grammar.rng.randint(0, n) if p1 == p2: continue if (p1 == 0 and p2 == n) or (p1 == n and p2 == 0): diff --git a/alogos/systems/_shared/init_individual.py b/alogos/systems/_shared/init_individual.py index 4c4bb02..f5b7fb8 100644 --- a/alogos/systems/_shared/init_individual.py +++ b/alogos/systems/_shared/init_individual.py @@ -1,7 +1,5 @@ """Shared individual initialization functions of different systems.""" -import random as _random - from ... import exceptions as _exceptions from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import init_tree as _init_tree @@ -194,14 +192,11 @@ def random_genotype(grammar, parameters, default_parameters, representation, map gl = _get_given_or_default("genotype_length", parameters, default_parameters) cs = _get_given_or_default("codon_size", parameters, default_parameters) - seed = _get_given_or_default("seed", parameters, default_parameters) - rng = _random.Random(seed) # Random(None) creates an unseeded generator (default) - # Transformation try: assert cs > 0 max_int = 2**cs - 1 - random_genotype = [rng.randint(0, max_int) for _ in range(gl)] + random_genotype = [grammar.rng.randint(0, max_int) for _ in range(gl)] except Exception: _exceptions.raise_init_ind_rand_gt_error() diff --git a/alogos/systems/_shared/init_tree.py b/alogos/systems/_shared/init_tree.py index 3e78c7b..2904c3a 100644 --- a/alogos/systems/_shared/init_tree.py +++ b/alogos/systems/_shared/init_tree.py @@ -2,7 +2,6 @@ import copy as _copy import itertools as _itertools -import random as _random from ... import exceptions as _exceptions from ..._grammar import data_structures as _data_structures @@ -59,7 +58,7 @@ def uniform(grammar, max_expansions=10_000): # 2) Choose rule: randomly rules = grammar.production_rules[chosen_nt_node.symbol] if len(rules) > 1: - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) else: chosen_rule_idx = 0 chosen_rule = rules[chosen_rule_idx] @@ -123,7 +122,7 @@ def weighted(grammar, max_expansions=10_000, reduction_factor=0.96): def weighted_choice(lhs, weights): rhs_list = grammar.production_rules[lhs] weight_list = weights[lhs] - chosen_rule_cumulative_weight = sum(weight_list) * _random.random() + chosen_rule_cumulative_weight = sum(weight_list) * grammar.rng.random() chosen_rule_idx = 0 for cumulative_weight in _itertools.accumulate(weight_list): if cumulative_weight >= chosen_rule_cumulative_weight: @@ -207,7 +206,7 @@ def ptc2(grammar, max_expansions=100): expansions = 0 while stack: # 1) Choose nonterminal: random - chosen_nt_idx = _random.choice(range(len(stack))) + chosen_nt_idx = grammar.rng.choice(range(len(stack))) chosen_nt_node = stack.pop(chosen_nt_idx) # 2) Choose rule: randomly from those that do not lead over the wanted expansions rules = grammar.production_rules[chosen_nt_node.symbol] @@ -222,7 +221,7 @@ def ptc2(grammar, max_expansions=100): min_expansions_per_symbol, stack, ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) @@ -267,7 +266,7 @@ def grow_one_branch_to_max_depth(grammar, max_depth=20): stack = [(dt.root_node, 0)] while stack: # 1) Choose nonterminal: random - chosen_nt_idx = _random.choice(range(len(stack))) + chosen_nt_idx = grammar.rng.choice(range(len(stack))) chosen_nt_node, depth = stack.pop(chosen_nt_idx) # 2) Choose rule: randomly from those that do not lead over the wanted depth rules = grammar.production_rules[chosen_nt_node.symbol] @@ -288,7 +287,7 @@ def grow_one_branch_to_max_depth(grammar, max_depth=20): rules = _filter_rules_for_grow( chosen_nt_node.symbol, rules, depth, max_depth, min_depths ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) @@ -333,7 +332,7 @@ def grow_all_branches_within_max_depth(grammar, max_depth=20): rules = _filter_rules_for_grow( chosen_nt_node.symbol, rules, depth, max_depth, min_depths ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) @@ -381,7 +380,7 @@ def grow_all_branches_to_max_depth(grammar, max_depth=20): rules = _filter_rules_for_full( chosen_nt_node.symbol, rules, depth, max_depth, min_depths, is_recursive ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) diff --git a/alogos/systems/_shared/neighborhood.py b/alogos/systems/_shared/neighborhood.py index cb996f2..8f3663d 100644 --- a/alogos/systems/_shared/neighborhood.py +++ b/alogos/systems/_shared/neighborhood.py @@ -3,10 +3,9 @@ import functools as _functools import itertools as _itertools import operator as _operator -import random as _random -def generate_combinations(num_choices_per_pos, distance, max_size=None): +def generate_combinations(num_choices_per_pos, distance, parameters, max_size=None): """Generate all combinations of choices available at each position. In each returned combination, the value 0 means that the original choice @@ -59,6 +58,7 @@ def generate_combinations(num_choices_per_pos, distance, max_size=None): max_size, count, count_per_pos_comb, + parameters ) else: # If no, generate the entire set @@ -75,11 +75,11 @@ def _product(iterable): def _generate_some_combinations( - choices_per_pos, pos_combinations, num_pos, max_size, count, count_per_pos_comb + choices_per_pos, pos_combinations, num_pos, max_size, count, count_per_pos_comb, parameters ): """Generate a random subset of all possible combinations.""" # Choose random neighbors by selecting indices from the enumerated possibilities - chosen = sorted(_random.sample(range(count), max_size)) + chosen = sorted(parameters["rng"].sample(range(count), max_size)) # Construct the chosen neighbors if chosen: diff --git a/alogos/systems/cfggp/crossover.py b/alogos/systems/cfggp/crossover.py index ccb3ea3..dca4cbc 100644 --- a/alogos/systems/cfggp/crossover.py +++ b/alogos/systems/cfggp/crossover.py @@ -72,7 +72,6 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): """ # Parameter extraction max_depth = _get_given_or_default("max_depth", parameters, _default_parameters) - rng = _get_given_or_default("rng", parameters, _default_parameters) # Argument processing if not isinstance(genotype1, _representation.Genotype): @@ -88,10 +87,10 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): si = s1.intersection(s2) if si: # Randomly select a non-terminal in the first tree, which is also part of second tree - n1 = rng.choice([n for n in ns1 if n.symbol.text in si]) + n1 = grammar.rng.choice([n for n in ns1 if n.symbol.text in si]) t1 = n1.symbol.text # Randomly select the same non-terminal in the second genotype - n2 = rng.choice([n for n in ns2 if n.symbol.text == t1]) + n2 = grammar.rng.choice([n for n in ns2 if n.symbol.text == t1]) # Swap subtrees by exchanging the list of child nodes n1.children, n2.children = n2.children, n1.children # Ensure max_depth constraint is not violated diff --git a/alogos/systems/cfggp/mutation.py b/alogos/systems/cfggp/mutation.py index 50431ab..5a7e23f 100644 --- a/alogos/systems/cfggp/mutation.py +++ b/alogos/systems/cfggp/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for CFG-GP.""" -import random as _random - from ..._grammar import data_structures as _data_structures from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from .._shared import _cached_calculations @@ -59,7 +57,7 @@ def subtree_replacement(grammar, genotype, parameters=None): nodes_and_depths.append((node, depth)) stack = stack + [(node, depth + 1) for node in node.children] # - Randomly select a node for mutation - node, depth = _random.choice(nodes_and_depths) + node, depth = grammar.rng.choice(nodes_and_depths) # - Replace the node's subtree with a randomly generated new one node.children = [] _grow_random_subtree(grammar, max_depth, start_depth=depth, root_node=node) @@ -85,7 +83,7 @@ def _grow_random_subtree(grammar, max_depth, start_depth, root_node): rules = _filter_rules_for_grow( chosen_nt_node.symbol, rules, depth, max_depth, min_depths ) - chosen_rule_idx = _random.randint(0, len(rules) - 1) + chosen_rule_idx = grammar.rng.randint(0, len(rules) - 1) chosen_rule = rules[chosen_rule_idx] # 3) Expand the chosen nonterminal with the rhs of the chosen rule new_nodes = dt._expand(chosen_nt_node, chosen_rule) diff --git a/alogos/systems/cfggp/neighborhood.py b/alogos/systems/cfggp/neighborhood.py index e13b03c..239bccb 100644 --- a/alogos/systems/cfggp/neighborhood.py +++ b/alogos/systems/cfggp/neighborhood.py @@ -13,7 +13,7 @@ _T = _grammar.data_structures.TerminalSymbol -def subtree_replacement(grammar, genotype, parameters=None): +def subtree_replacement(grammar, genotype, parameters): """Systematically change a chosen number of nodes. Parameters @@ -58,7 +58,7 @@ def subtree_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Neighborhood construction diff --git a/alogos/systems/cfggpst/crossover.py b/alogos/systems/cfggpst/crossover.py index f510e20..50de921 100644 --- a/alogos/systems/cfggpst/crossover.py +++ b/alogos/systems/cfggpst/crossover.py @@ -86,7 +86,6 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): # Parameter extraction mn = _get_given_or_default("max_nodes", parameters, _default_parameters) - rng = _get_given_or_default("rng", parameters, default_parameters) # Crossover s1, c1 = genotype1.data @@ -95,11 +94,11 @@ def subtree_exchange(grammar, genotype1, genotype2, parameters): nb = {s for s in s2 if s in n1} # set of nonterminals in both trees if nb: # - Randomly select a nonterminal in tree 1, which is also present in tree 2 - a1 = rng.choice([i for i, s in enumerate(s1) if s in nb]) + a1 = grammar.rng.choice([i for i, s in enumerate(s1) if s in nb]) b1 = _fe(a1, c1) + 1 # - Randomly select the same nonterminal at some position in tree 2 sym = s1[a1] - a2 = rng.choice([i for i, s in enumerate(s2) if s == sym]) + a2 = grammar.rng.choice([i for i, s in enumerate(s2) if s == sym]) b2 = _fe(a2, c2) + 1 # - Swap: only if max_nodes condition is not violated afterwards for both trees l1 = b1 - a1 diff --git a/alogos/systems/cfggpst/mutation.py b/alogos/systems/cfggpst/mutation.py index 62f8d63..cfc6140 100644 --- a/alogos/systems/cfggpst/mutation.py +++ b/alogos/systems/cfggpst/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for CFG-GP-ST.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from .. import _shared from . import _cached_calculations @@ -12,9 +10,6 @@ # Shortcuts for minor speedup _GT = _representation.Genotype _fe = _representation._find_subtree_end -_rc = _random.choice -_ri = _random.randint -_rs = _random.shuffle def subtree_replacement(grammar, genotype, parameters=None): @@ -71,7 +66,7 @@ def subtree_replacement(grammar, genotype, parameters=None): # Mutation # - Random choice of a nonterminal to mutate s1, c1 = genotype.data - a1 = _rc([i for i, c in enumerate(c1) if c]) # choose idx of a random nonterminal + a1 = grammar.rng.choice([i for i, c in enumerate(c1) if c]) # choose idx of a random nonterminal b1 = _fe(a1, c1) + 1 # find idx of last symbol in its subtree # - Grow a random new subtree s2, c2 = _grow_random_subtree(grammar, s1[a1], mn - len(s1) + (b1 - a1)) @@ -106,11 +101,11 @@ def trav(sy): try: # 1) Symbol is a nonterminal, therefore requires expansion and will have >0 children # Choose rule: randomly from those rules that do not lead over the wanted num nodes - rhs = _rc(_filter_rules(sy, ipr[sy], imn[sy], max_nodes - n)) + rhs = grammar.rng.choice(_filter_rules(sy, ipr[sy], imn[sy], max_nodes - n)) # Expand the nonterminal with the rhs of the chosen rule m = len(rhs) r = list(range(m)) - _rs(r) + grammar.rng.shuffle(r) ret = [None] * m for i in r: # Recursive call for each child in random order, return in original order (dfs) diff --git a/alogos/systems/cfggpst/neighborhood.py b/alogos/systems/cfggpst/neighborhood.py index e5625f5..4d29128 100644 --- a/alogos/systems/cfggpst/neighborhood.py +++ b/alogos/systems/cfggpst/neighborhood.py @@ -14,7 +14,7 @@ _T = _grammar.data_structures.TerminalSymbol -def subtree_replacement(grammar, genotype, parameters=None): +def subtree_replacement(grammar, genotype, parameters): """Systematically change a chosen number of nodes. Parameters @@ -60,7 +60,7 @@ def subtree_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Neighborhood construction diff --git a/alogos/systems/dsge/_cached_calculations.py b/alogos/systems/dsge/_cached_calculations.py index 55d07ce..00bd33d 100644 --- a/alogos/systems/dsge/_cached_calculations.py +++ b/alogos/systems/dsge/_cached_calculations.py @@ -1,5 +1,3 @@ -import random as _random - from ... import _grammar @@ -63,14 +61,14 @@ def get_first_valid_codon( def get_random_valid_codon( - nt, current_depth, max_depth, nt_to_num_options, non_recursive_rhs + nt, current_depth, max_depth, nt_to_num_options, non_recursive_rhs, grammar ): - """Get a random valid codons depending on current and max depth.""" + """Get a random valid codon depending on current and max depth.""" # Used in stochastic repair mechanism options = get_all_valid_codons( nt, current_depth, max_depth, nt_to_num_options, non_recursive_rhs ) - return _random.choice(options) + return grammar.rng.choice(options) def get_tree_depth(grammar, genotype, nt_to_gene, nt_to_cnt): diff --git a/alogos/systems/dsge/crossover.py b/alogos/systems/dsge/crossover.py index 7ecea70..ec7ea43 100644 --- a/alogos/systems/dsge/crossover.py +++ b/alogos/systems/dsge/crossover.py @@ -65,7 +65,6 @@ def gene_swap(grammar, genotype1, genotype2, parameters=None): repair = _get_given_or_default( "repair_after_crossover", parameters, _default_parameters ) - rng = _get_given_or_default("rng", parameters, default_parameters) # Argument processing if not isinstance(genotype1, _GT): @@ -77,7 +76,7 @@ def gene_swap(grammar, genotype1, genotype2, parameters=None): d1 = genotype1.data d2 = genotype2.data vals = (True, False) - mask = [rng.choice(vals) for _ in range(len(d1))] + mask = [grammar.rng.choice(vals) for _ in range(len(d1))] n1 = tuple(d1[i] if x else d2[i] for i, x in enumerate(mask)) n2 = tuple(d2[i] if x else d1[i] for i, x in enumerate(mask)) diff --git a/alogos/systems/dsge/init_individual.py b/alogos/systems/dsge/init_individual.py index a632818..8cf2ed3 100644 --- a/alogos/systems/dsge/init_individual.py +++ b/alogos/systems/dsge/init_individual.py @@ -161,6 +161,7 @@ def random_genotype(grammar, parameters=None): init_max_depth, nt_to_num_options, non_recursive_rhs, + grammar, ) chosen_rule = rules[chosen_rule_idx] genotype[gene_idx].append(chosen_rule_idx) diff --git a/alogos/systems/dsge/mutation.py b/alogos/systems/dsge/mutation.py index 79c0632..f72bfb8 100644 --- a/alogos/systems/dsge/mutation.py +++ b/alogos/systems/dsge/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for DSGE.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import _cached_calculations from . import default_parameters as _dp @@ -79,7 +77,7 @@ def int_replacement_by_probability(grammar, genotype, parameters=None): # Mutation: Randomly decide for each positions in the genotype whether it shall be modified data = genotype.data num_pos = sum(len(data[gi]) for gi in mutable_genes) - pos = set(i for i in range(num_pos) if _random.random() < probability) + pos = set(i for i in range(num_pos) if grammar.rng.random() < probability) data = _change_chosen_positions( data, pos, @@ -89,6 +87,7 @@ def int_replacement_by_probability(grammar, genotype, parameters=None): non_recursive_rhs, nt_to_num_options, mutable_genes, + grammar, ) # Optional repair of the new genotype @@ -146,7 +145,7 @@ def int_replacement_by_count(grammar, genotype, parameters=None): num_pos = sum(len(data[gi]) for gi in mutable_genes) pos = range(num_pos) if num_pos > flip_count: - pos = _random.sample(pos, flip_count) + pos = grammar.rng.sample(pos, flip_count) pos = set(pos) data = _change_chosen_positions( data, @@ -157,6 +156,7 @@ def int_replacement_by_count(grammar, genotype, parameters=None): non_recursive_rhs, nt_to_num_options, mutable_genes, + grammar, ) # Optional repair of the new genotype (either here and/or later) @@ -176,6 +176,7 @@ def _change_chosen_positions( non_recursive_rhs, nt_to_num_options, mutable_genes, + grammar, ): """Mutate the selected positions by replacing the integer with another valid option. @@ -205,12 +206,12 @@ def _change_chosen_positions( ): # Note: >= is used in the original authors' implementation def get_new_codon(nt, codon): - return _random.choice([x for x in non_recursive_rhs[nt] if x != codon]) + return grammar.rng.choice([x for x in non_recursive_rhs[nt] if x != codon]) else: def get_new_codon(nt, codon): - return _random.choice( + return grammar.rng.choice( [x for x in range(nt_to_num_options[nt]) if x != codon] ) diff --git a/alogos/systems/dsge/neighborhood.py b/alogos/systems/dsge/neighborhood.py index b0b2706..62dc069 100644 --- a/alogos/systems/dsge/neighborhood.py +++ b/alogos/systems/dsge/neighborhood.py @@ -13,7 +13,7 @@ _T = _grammar.data_structures.TerminalSymbol -def int_replacement(grammar, genotype, parameters=None): +def int_replacement(grammar, genotype, parameters): """Change systematically chosen integers.""" # Parameter extraction distance = _get_given_or_default( @@ -39,7 +39,7 @@ def int_replacement(grammar, genotype, parameters=None): # Generate combinations combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Construct neighborhood genotypes from combinations diff --git a/alogos/systems/dsge/repair.py b/alogos/systems/dsge/repair.py index fb4e235..f37ba0e 100644 --- a/alogos/systems/dsge/repair.py +++ b/alogos/systems/dsge/repair.py @@ -113,6 +113,7 @@ def fix_genotype(grammar, genotype, parameters=None, raise_errors=True): max_depth, nt_to_num_options, non_recursive_rhs, + grammar, ) else: chosen_rule_idx = _cached_calculations.get_first_valid_codon( @@ -135,6 +136,7 @@ def fix_genotype(grammar, genotype, parameters=None, raise_errors=True): max_depth, nt_to_num_options, non_recursive_rhs, + grammar, ) else: chosen_rule_idx = _cached_calculations.get_first_valid_codon( diff --git a/alogos/systems/ge/mapping.py b/alogos/systems/ge/mapping.py index 3fd50e8..f766fe5 100644 --- a/alogos/systems/ge/mapping.py +++ b/alogos/systems/ge/mapping.py @@ -1,7 +1,5 @@ """Forward and reverse mapping functions for GE.""" -import random as _random - from ... import _grammar from ... import exceptions as _exceptions from ..._utilities.parametrization import get_given_or_default as _get_given_or_default @@ -365,7 +363,7 @@ def reverse( _exceptions.raise_limited_codon_size_error(chosen_rule_idx, max_int) if codon_randomization: options = range(chosen_rule_idx, max_int + 1, len(rules)) - chosen_rule_idx = _random.choice(options) + chosen_rule_idx = grammar.rng.choice(options) # 3) Expand the chosen nonterminal with the rhs of the chosen rule -> Follow the expansion new_nt_nodes = [ diff --git a/alogos/systems/ge/neighborhood.py b/alogos/systems/ge/neighborhood.py index e79494a..3ec5cd7 100644 --- a/alogos/systems/ge/neighborhood.py +++ b/alogos/systems/ge/neighborhood.py @@ -11,7 +11,7 @@ _T = _grammar.data_structures.TerminalSymbol -def int_replacement(grammar, genotype, parameters=None): +def int_replacement(grammar, genotype, parameters): """Systematically change a chosen number of int codons. Parameters @@ -65,7 +65,7 @@ def int_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Construct neighborhood genotypes from combinations diff --git a/alogos/systems/pige/mapping.py b/alogos/systems/pige/mapping.py index 878ba9c..5b97d5f 100644 --- a/alogos/systems/pige/mapping.py +++ b/alogos/systems/pige/mapping.py @@ -1,7 +1,5 @@ """Forward and reverse mapping functions for piGE.""" -import random as _random - from ... import _grammar from ... import exceptions as _exceptions from ..._utilities import argument_processing as _ap @@ -410,7 +408,7 @@ def reverse( elif derivation_order == "rightmost": chosen_nt_idx_tlo = len(stack_tlo) - 1 elif derivation_order == "random": - chosen_nt_idx_tlo = _random.randint(0, len(stack_tlo) - 1) + chosen_nt_idx_tlo = grammar.rng.randint(0, len(stack_tlo) - 1) chosen_nt_node = stack_tlo.pop(chosen_nt_idx_tlo) chosen_nt_idx = stack.index(chosen_nt_node) chosen_nt_idx_stored = chosen_nt_idx @@ -419,7 +417,7 @@ def reverse( _exceptions.raise_limited_codon_size_error(chosen_nt_idx, max_int) if codon_randomization: options = range(chosen_nt_idx, max_int + 1, len(stack) + 1) - chosen_nt_idx_stored = _random.choice(options) + chosen_nt_idx_stored = grammar.rng.choice(options) # 2) Choose rule: piGE decides via next "content codon" -> Deduce it from tree try: @@ -435,7 +433,7 @@ def reverse( _exceptions.raise_limited_codon_size_error(chosen_rule_idx, max_int) if codon_randomization: options = range(chosen_rule_idx, max_int + 1, len(rules)) - chosen_rule_idx = _random.choice(options) + chosen_rule_idx = grammar.rng.choice(options) # 3) Expand the chosen nonterminal with the rhs of the chosen rule -> Follow the expansion new_nt_nodes = [ diff --git a/alogos/systems/pige/neighborhood.py b/alogos/systems/pige/neighborhood.py index 9a1403b..005792c 100644 --- a/alogos/systems/pige/neighborhood.py +++ b/alogos/systems/pige/neighborhood.py @@ -13,7 +13,7 @@ _T = _grammar.data_structures.TerminalSymbol -def int_replacement(grammar, genotype, parameters=None): +def int_replacement(grammar, genotype, parameters): """Systematically change a chosen number of int codons. Parameters @@ -78,7 +78,7 @@ def int_replacement(grammar, genotype, parameters=None): # Generate combinations of choices combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) # Construct neighborhood genotypes from combinations diff --git a/alogos/systems/whge/init_individual.py b/alogos/systems/whge/init_individual.py index 4a40673..5dcaaf9 100644 --- a/alogos/systems/whge/init_individual.py +++ b/alogos/systems/whge/init_individual.py @@ -1,7 +1,5 @@ """Initialization functions to create a WHGE individual.""" -import random as _random - from bitarray.util import int2ba as _int2ba from ... import exceptions as _exceptions @@ -67,7 +65,7 @@ def random_genotype(grammar, parameters=None): # Transformation try: - rand_int = _random.getrandbits(gl) + rand_int = grammar.rng.getrandbits(gl) rand_bitstring = _int2ba(rand_int, gl) rand_genotype = _representation.Genotype(rand_bitstring) except Exception: diff --git a/alogos/systems/whge/mutation.py b/alogos/systems/whge/mutation.py index 02611c4..d54442f 100644 --- a/alogos/systems/whge/mutation.py +++ b/alogos/systems/whge/mutation.py @@ -1,7 +1,5 @@ """Mutation functions for WHGE.""" -import random as _random - from ..._utilities.parametrization import get_given_or_default as _get_given_or_default from . import default_parameters as _dp from . import representation as _representation @@ -83,7 +81,7 @@ def bit_flip_by_probability(grammar, genotype, parameters=None): # Mutation: For each bit decide randomly whether it is flipped data = genotype.data.copy() for i in range(len(data)): - if _random.random() < probability: + if grammar.rng.random() < probability: data[i] = not data[i] return _representation.Genotype(data) @@ -103,7 +101,7 @@ def bit_flip_by_count(grammar, genotype, parameters=None): if flip_count > num_pos: positions = range(num_pos) else: - positions = _random.sample(range(num_pos), flip_count) + positions = grammar.rng.sample(range(num_pos), flip_count) for i in positions: data[i] = not data[i] return _representation.Genotype(data) diff --git a/alogos/systems/whge/neighborhood.py b/alogos/systems/whge/neighborhood.py index 0cb6419..60b6fba 100644 --- a/alogos/systems/whge/neighborhood.py +++ b/alogos/systems/whge/neighborhood.py @@ -6,7 +6,7 @@ from . import representation as _representation -def bit_flip(grammar, genotype, parameters=None): +def bit_flip(grammar, genotype, parameters): """Generate nearby genotypes by flipping n bits. Parameters @@ -44,7 +44,7 @@ def bit_flip(grammar, genotype, parameters=None): # Generate combinations combinations = _shared.neighborhood.generate_combinations( - num_choices_per_pos, distance, max_size + num_choices_per_pos, distance, max_size, parameters ) for comb in combinations: print(" ", comb)