Skip to content

Commit d36bc42

Browse files
committed
Random local search over a set of permissible values
1 parent 35ce5ae commit d36bc42

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

cgp/local_search/random_search.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import numpy as np
2+
from typing import Callable
3+
4+
5+
class RandomLocalSearch:
6+
def __init__(
7+
self,
8+
objective: Callable,
9+
seed: int,
10+
n_steps: int = 10, # todo: should this be dependent on the number of parameters?
11+
permissible_values: np.ndarray = np.logspace(start=0, stop=7, num=8, base=2, dtype=int),
12+
) -> None:
13+
self.objective = objective
14+
self.seed = seed
15+
self.n_steps = n_steps
16+
self.permissible_values = permissible_values
17+
18+
def __call__(self, ind) -> None:
19+
rng = np.random.RandomState(self.seed)
20+
21+
params_values, params_names = ind.parameters_to_numpy_array(only_active_nodes=True)
22+
23+
if len(params_values) > 0:
24+
for _ in range(self.n_steps):
25+
# sample a new set of parameter values randomly
26+
params_sampled = [rng.choice(self.permissible_values) for param_value in params_values]
27+
# write the parameters into a clone of individual
28+
new_ind = ind.clone()
29+
new_ind.update_parameters_from_numpy_array(params=params_sampled, params_names=params_names)
30+
# evaluate fitness
31+
self.objective(new_ind)
32+
# if fitness improved: replace parameter values and fitness
33+
if new_ind.fitness >= ind.fitness: # todo: should this be >= or > ??
34+
ind.update_parameters_from_numpy_array(params=params_sampled, params_names=params_names)
35+
ind.fitness = new_ind.fitness
36+
37+
38+
if __name__ == "__main__":
39+
import cgp
40+
41+
def objective(ind):
42+
params_values, _ = ind.parameters_to_numpy_array(only_active_nodes=True)
43+
ind.fitness = np.sum(params_values)
44+
return ind
45+
46+
seed = 12345
47+
genome = cgp.Genome(primitives=(cgp.Add, cgp.Sub, cgp.Mul, cgp.Parameter), n_inputs=1)
48+
genome.randomize(rng=np.random.RandomState(seed=seed))
49+
ind = cgp.IndividualSingleGenome(genome=genome)
50+
51+
objective(ind)
52+
print(f'Node parameters before local search {ind.parameters_to_numpy_array(only_active_nodes=True)} \n')
53+
54+
rls = RandomLocalSearch(objective=objective, seed=seed, n_steps=1000)
55+
rls(ind)
56+
print(f'Node parameters after local search {ind.parameters_to_numpy_array(only_active_nodes=True)} \n')

cgp/local_search/sympy_simplification_options.py

Whitespace-only changes.

cgp/node.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def __init_subclass__(cls: Type["OperatorNode"]) -> None:
178178
node_validation.check_to_func(cls)
179179
node_validation.check_to_numpy(cls)
180180
node_validation.check_to_torch(cls)
181-
node_validation.check_to_sympy(cls)
181+
#node_validation.check_to_sympy(cls)
182182

183183
@classmethod
184184
def _extract_input_names_from_def_output(self, cls: Type["OperatorNode"]) -> None:

cgp/node_validation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def check_to_func(cls: Type["OperatorNode"]) -> None:
5050
genome = _create_genome(cls)
5151

5252
f = CartesianGraph(genome).to_func()
53-
x = 1.0
53+
x = 1
5454
f(x)
5555

5656

0 commit comments

Comments
 (0)