gradient_free_optimizers/optimizers/global_opt/pattern_search.py
# Author: Simon Blanke
# Email: simon.blanke@yahoo.com
# License: MIT License
import random
import numpy as np
from ..local_opt import HillClimbingOptimizer
def max_list_idx(list_):
max_item = max(list_)
max_item_idx = [i for i, j in enumerate(list_) if j == max_item]
return max_item_idx[-1:][0]
class PatternSearch(HillClimbingOptimizer):
name = "Pattern Search"
_name_ = "pattern_search"
__name__ = "PatternSearch"
optimizer_type = "global"
computationally_expensive = False
def __init__(
self, *args, n_positions=4, pattern_size=0.25, reduction=0.9, **kwargs
):
super().__init__(*args, **kwargs)
self.n_positions = n_positions
self.pattern_size = pattern_size
self.reduction = reduction
self.n_positions_ = min(n_positions, self.conv.n_dimensions)
self.pattern_size_tmp = pattern_size
self.pattern_pos_l = []
def generate_pattern(self, current_position):
pattern_pos_l = []
n_valid_pos = len(self.positions_valid)
n_pattern_pos = int(self.n_positions_ * 2)
n_pos_min = min(n_valid_pos, n_pattern_pos)
best_in_recent_pos = any(
np.array_equal(np.array(self.pos_best), pos)
for pos in self.positions_valid[n_pos_min:]
)
if best_in_recent_pos:
self.pattern_size_tmp *= self.reduction
pattern_size = self.pattern_size_tmp
for idx, dim_size in enumerate(self.conv.dim_sizes):
pos_pattern_p = np.array(current_position)
pos_pattern_n = np.array(current_position)
pos_pattern_p[idx] += pattern_size * dim_size
pos_pattern_n[idx] -= pattern_size * dim_size
pos_pattern_p = self.conv2pos(pos_pattern_p)
pos_pattern_n = self.conv2pos(pos_pattern_n)
pattern_pos_l.append(pos_pattern_p)
pattern_pos_l.append(pos_pattern_n)
self.pattern_pos_l = list(random.sample(pattern_pos_l, self.n_positions_))
@HillClimbingOptimizer.track_new_pos
@HillClimbingOptimizer.random_iteration
def iterate(self):
while True:
pos_new = self.pattern_pos_l[0]
self.pattern_pos_l.pop(0)
if self.conv.not_in_constraint(pos_new):
return pos_new
return self.move_climb(pos_new)
def finish_initialization(self):
self.generate_pattern(self.pos_current)
self.search_state = "iter"
@HillClimbingOptimizer.track_new_score
def evaluate(self, score_new):
super(HillClimbingOptimizer, self).evaluate(score_new)
if len(self.scores_valid) == 0:
return
modZero = self.nth_trial % int(self.n_positions_ * 2) == 0
if modZero or len(self.pattern_pos_l) == 0:
if self.search_state == "iter":
self.generate_pattern(self.pos_current)
score_new_list_temp = self.scores_valid[-self.n_positions_ :]
pos_new_list_temp = self.positions_valid[-self.n_positions_ :]
idx = max_list_idx(score_new_list_temp)
score = score_new_list_temp[idx]
pos = pos_new_list_temp[idx]
self._eval2current(pos, score)
self._eval2best(pos, score)