Skip to content

Commit

Permalink
Merge pull request #139 from perib/new_search_space_def
Browse files Browse the repository at this point in the history
New search space def
  • Loading branch information
jay-m-dev committed Jul 11, 2024
2 parents a1592e6 + 76d3989 commit 93e709a
Show file tree
Hide file tree
Showing 22 changed files with 681 additions and 351 deletions.
507 changes: 481 additions & 26 deletions Tutorial/Example_Search_Spaces/imputation.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def calculate_version():
''',
zip_safe=True,
install_requires=['numpy>=1.26.4',
install_requires=['numpy==1.26.4',
'scipy>=1.3.1',
'scikit-learn>=1.3.0',
'update_checker>=0.16',
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ commands = flake8 tpot2
basepython = python3.10
deps =
-r{toxinidir}/requirements_dev.txt
commands = mypy tpot2
commands = mypy tpot2
19 changes: 10 additions & 9 deletions tpot2/evolvers/steady_state_evolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,17 +299,18 @@ def optimize(self):
eval_error = "INVALID"
else: #if future is not done

#check if the future has been running for too long, cancel the future
if time.time() - submitted_futures[completed_future]["time"] > self.max_eval_time_seconds*1.25:
completed_future.cancel()
if self.max_eval_time_seconds is not None:
#check if the future has been running for too long, cancel the future
if time.time() - submitted_futures[completed_future]["time"] > self.max_eval_time_seconds*1.25:
completed_future.cancel()

if self.verbose >= 4:
print(f'WARNING AN INDIVIDUAL TIMED OUT (Fallback): \n {submitted_futures[completed_future]} \n')
if self.verbose >= 4:
print(f'WARNING AN INDIVIDUAL TIMED OUT (Fallback): \n {submitted_futures[completed_future]} \n')

scores = [np.nan for _ in range(len(self.objective_names))]
eval_error = "TIMEOUT"
else:
continue #otherwise, continue to next future
scores = [np.nan for _ in range(len(self.objective_names))]
eval_error = "TIMEOUT"
else:
continue #otherwise, continue to next future



Expand Down
22 changes: 15 additions & 7 deletions tpot2/search_spaces/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,31 @@




class SklearnIndividual(tpot2.BaseIndividual):

def __init_subclass__(cls):
cls.crossover = cls.validate_same_type(cls.crossover)


def __init__(self,) -> None:
super().__init__()

def mutate(self, rng=None):
return

@final
def crossover(self, other, rng=None, **kwargs):
if not isinstance(other, type(self)):
return False
return self._crossover(other, rng=rng, **kwargs)
return

@abstractmethod
def _crossover(self, other, rng=None):
return
@final
def validate_same_type(func):

def wrapper(self, other, rng=None, **kwargs):
if not isinstance(other, type(self)):
return False
return func(self, other, rng=None, **kwargs)

return wrapper

def export_pipeline(self) -> BaseEstimator:
return
Expand Down
2 changes: 1 addition & 1 deletion tpot2/search_spaces/nodes/estimator_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def mutate(self, rng=None):
self.check_hyperparameters_for_None()
return True

def _crossover(self, other, rng=None):
def crossover(self, other, rng=None):
if isinstance(self.space, dict):
return False

Expand Down
59 changes: 0 additions & 59 deletions tpot2/search_spaces/nodes/estimator_node_custom_sampler.py

This file was deleted.

70 changes: 0 additions & 70 deletions tpot2/search_spaces/nodes/estimator_node_simple.py

This file was deleted.

2 changes: 1 addition & 1 deletion tpot2/search_spaces/nodes/fss_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def mutate(self, rng=None):
self.sel_subset = self.subset_dict[self.selected_subset_name]


def _crossover(self, other, rng=None):
def crossover(self, other, rng=None):
self.selected_subset_name = other.selected_subset_name
self.sel_subset = other.sel_subset

Expand Down
10 changes: 5 additions & 5 deletions tpot2/search_spaces/nodes/genetic_feature_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ def __init__( self,
start_p=0.2,
mutation_rate = 0.5,
crossover_rate = 0.5,
mutation_rate_rate = 0,
crossover_rate_rate = 0,
rng=None,
):

self.start_p = start_p
self.mutation_rate = mutation_rate
self.crossover_rate = crossover_rate
self.mutation_rate_rate = mutation_rate_rate
self.crossover_rate_rate = crossover_rate_rate
self.mutation_rate_rate = 0
self.crossover_rate_rate = 0



rng = np.random.default_rng(rng)

Expand Down Expand Up @@ -69,7 +69,7 @@ def mutate(self, rng=None):

return rng.choice(self.mutation_list)(rng)

def _crossover(self, other, rng=None):
def crossover(self, other, rng=None):
rng = np.random.default_rng(rng)

if rng.uniform() < self.crossover_rate_rate:
Expand Down
2 changes: 1 addition & 1 deletion tpot2/search_spaces/pipelines/choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def _mutate_select_new_node(self, rng=None):
def _mutate_node(self, rng=None):
return self.node.mutate(rng)

def _crossover(self, other, rng=None):
def crossover(self, other, rng=None):
return self.node.crossover(other.node, rng)

def export_pipeline(self):
Expand Down
28 changes: 14 additions & 14 deletions tpot2/search_spaces/pipelines/dynamic_linear.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, search_space : SklearnIndividualGenerator, max_length: int ,
self.pipeline = self._generate_pipeline(rng)

def _generate_pipeline(self, rng=None):
rng = np.random.default_rng()
rng = np.random.default_rng(rng)
pipeline = []
length = rng.integers(self.min_length, self.max_length)
length = min(length, 3)
Expand All @@ -37,7 +37,7 @@ def _generate_pipeline(self, rng=None):


def mutate(self, rng=None):
rng = np.random.default_rng()
rng = np.random.default_rng(rng)
options = []
if len(self.pipeline) > self.min_length:
options.append(self._mutate_remove_node)
Expand All @@ -48,28 +48,28 @@ def mutate(self, rng=None):
return rng.choice(options)(rng)

def _mutate_add_node(self, rng=None):
rng = np.random.default_rng()
rng = np.random.default_rng(rng)
new_node = self.search_space.generate(rng)
idx = rng.integers(len(self.pipeline))
self.pipeline.insert(idx, new_node)

def _mutate_remove_node(self, rng=None):
rng = np.random.default_rng()
rng = np.random.default_rng(rng)
idx = rng.integers(len(self.pipeline))
self.pipeline.pop(idx)

def _mutate_step(self, rng=None):
#choose a random step in the pipeline and mutate it
rng = np.random.default_rng()
rng = np.random.default_rng(rng)
step = rng.choice(self.pipeline)
return step.mutate(rng)


def _crossover(self, other, rng=None):
def crossover(self, other, rng=None):
#swap a random step in the pipeline with the corresponding step in the other pipeline

rng = np.random.default_rng()
cx_funcs = [self._crossover_swap_random_steps, self._crossover_inner_step]
rng = np.random.default_rng(rng)
cx_funcs = [self._crossover_swap_multiple_nodes, self._crossover_node]

rng.shuffle(cx_funcs)
for cx_func in cx_funcs:
Expand All @@ -78,8 +78,8 @@ def _crossover(self, other, rng=None):

return False

def _crossover_swap_random_steps(self, other, rng):
rng = np.random.default_rng()
def _crossover_swap_multiple_nodes(self, other, rng):
rng = np.random.default_rng(rng)

max_steps = int(min(len(self.pipeline), len(other.pipeline))/2)
max_steps = max(max_steps, 1)
Expand All @@ -99,21 +99,21 @@ def _crossover_swap_random_steps(self, other, rng):

return True

def _crossover_swap_step(self, other, rng):
def _crossover_swap_node(self, other, rng):
if len(self.pipeline) != len(other.pipeline):
return False

if len(self.pipeline) < 2:
return False

rng = np.random.default_rng()
rng = np.random.default_rng(rng)
idx = rng.integers(1,len(self.pipeline))

self.pipeline[idx], other.pipeline[idx] = other.pipeline[idx], self.pipeline[idx]
return True

def _crossover_inner_step(self, other, rng):
rng = np.random.default_rng()
def _crossover_node(self, other, rng):
rng = np.random.default_rng(rng)

pipeline1_indexes= list(range(len(self.pipeline)))
pipeline2_indexes= list(range(len(other.pipeline)))
Expand Down
Loading

0 comments on commit 93e709a

Please sign in to comment.