From 6140674bda17b5eaf4f60e8e06cb971eeb324954 Mon Sep 17 00:00:00 2001 From: Sebastian Musslick Date: Mon, 3 Jul 2023 14:26:15 -0400 Subject: [PATCH 1/3] aligned input argument with that of other experimentalists --- src/autora/experimentalist/sampler/random_sampler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autora/experimentalist/sampler/random_sampler.py b/src/autora/experimentalist/sampler/random_sampler.py index 5fa09809..7e28d2c3 100644 --- a/src/autora/experimentalist/sampler/random_sampler.py +++ b/src/autora/experimentalist/sampler/random_sampler.py @@ -4,7 +4,7 @@ from autora.utils.deprecation import deprecated_alias -def random_sample(conditions: Union[Iterable, Sequence], n: int = 1): +def random_sample(conditions: Union[Iterable, Sequence], num_samples: int = 1): """ Uniform random sampling without replacement from a pool of conditions. Args: @@ -18,7 +18,7 @@ def random_sample(conditions: Union[Iterable, Sequence], n: int = 1): if isinstance(conditions, Iterable): conditions = list(conditions) random.shuffle(conditions) - samples = conditions[0:n] + samples = conditions[0:num_samples] return samples From 081cc379dbda63ba13c876f150144e1cd71317bf Mon Sep 17 00:00:00 2001 From: Sebastian Musslick Date: Mon, 3 Jul 2023 14:30:38 -0400 Subject: [PATCH 2/3] aligned first input argument with that of grid pooler --- .../experimentalist/pooler/random_pooler.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/autora/experimentalist/pooler/random_pooler.py b/src/autora/experimentalist/pooler/random_pooler.py index 3e3db4d4..78ad104e 100644 --- a/src/autora/experimentalist/pooler/random_pooler.py +++ b/src/autora/experimentalist/pooler/random_pooler.py @@ -1,38 +1,48 @@ import random +from typing import Iterable, List, Tuple import numpy as np from autora.utils.deprecation import deprecated_alias +from autora.variable import IV -def random_pool(*args, n: int = 1, duplicates: bool = True): +def random_pool( + ivs: List[IV], num_samples: int = 1, duplicates: bool = True +) -> Iterable: """ Creates combinations from lists of discrete values using random selection. Args: - *args: m lists of discrete values. One value will be sampled from each list. + ivs: List of independent variables n: Number of samples to sample duplicates: Boolean if duplicate value are allowed. """ - l_samples = [] + l_samples: List[Tuple] = [] # Create list of pools of values sample from - pools = [tuple(pool) for pool in args] + l_iv_values = [] + for iv in ivs: + assert iv.allowed_values is not None, ( + f"gridsearch_pool only supports independent variables with discrete allowed values, " + f"but allowed_values is None on {iv=} " + ) + l_iv_values.append(iv.allowed_values) # Check to ensure infinite search won't occur if duplicates not allowed if not duplicates: - l_pool_len = [len(set(s)) for s in pools] + l_pool_len = [len(set(s)) for s in l_iv_values] n_combinations = np.product(l_pool_len) try: - assert n <= n_combinations + assert num_samples <= n_combinations except AssertionError: raise AssertionError( - f"Number to sample n({n}) is larger than the number " + f"Number to sample n({num_samples}) is larger than the number " f"of unique combinations({n_combinations})." ) # Random sample from the pools until n is met - while len(l_samples) < n: - l_samples.append(tuple(map(random.choice, pools))) + while len(l_samples) < num_samples: + l_samples.append(tuple(map(random.choice, l_iv_values))) if not duplicates: l_samples = [*set(l_samples)] From 50dcee7f1efb16c5e2d7ad4d99abf9bf9d0bf3d4 Mon Sep 17 00:00:00 2001 From: Sebastian Musslick Date: Mon, 3 Jul 2023 14:31:05 -0400 Subject: [PATCH 3/3] added test for pooler --- tests/test_experimentalist_random.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/test_experimentalist_random.py b/tests/test_experimentalist_random.py index 0cac76e9..a81ad483 100644 --- a/tests/test_experimentalist_random.py +++ b/tests/test_experimentalist_random.py @@ -5,6 +5,7 @@ from autora.experimentalist.pipeline import make_pipeline from autora.experimentalist.pooler.grid import grid_pool +from autora.experimentalist.pooler.random_pooler import random_pool from autora.experimentalist.sampler.random_sampler import random_sample from autora.variable import DV, IV, ValueType, VariableCollection @@ -13,7 +14,24 @@ def weber_filter(values): return filter(lambda s: s[0] <= s[1], values) -def test_random_experimentalist(metadata): +def test_random_pooler_experimentalist(metadata): + """ + Tests the implementation of a random pooler. + """ + num_samples = 10 + + conditions = random_pool(metadata.independent_variables, num_samples=num_samples) + + conditions = np.array(list(conditions)) + + assert conditions.shape[0] == num_samples + assert conditions.shape[1] == len(metadata.independent_variables) + for condition in conditions: + for idx, value in enumerate(condition): + assert value in metadata.independent_variables[idx].allowed_values + + +def test_random_sampler_experimentalist(metadata): """ Tests the implementation of the experimentalist pipeline with an exhaustive pool of discrete values, Weber filter, random selector. Tests two different implementations of the pool function @@ -26,7 +44,7 @@ def test_random_experimentalist(metadata): # ---Implementation 1 - Pool using Callable via partial function---- # Set up pipeline functions with partial pooler_callable = partial(grid_pool, ivs=metadata.independent_variables) - sampler = partial(random_sample, n=n_trials) + sampler = partial(random_sample, num_samples=n_trials) pipeline_random_samp = make_pipeline( [pooler_callable, weber_filter, sampler], ) @@ -64,7 +82,7 @@ def test_random_experimentalist_generator(metadata): n_trials = 25 # Number of trails for sampler to select pooler_generator = grid_pool(metadata.independent_variables) - sampler = partial(random_sample, n=n_trials) + sampler = partial(random_sample, num_samples=n_trials) pipeline_random_samp_poolgen = make_pipeline( [pooler_generator, weber_filter, sampler] )