Skip to content

Commit

Permalink
Improved APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
jbussemaker committed Sep 1, 2023
1 parent c46cf05 commit 9a2b395
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 23 deletions.
3 changes: 2 additions & 1 deletion sb_arch_opt/algo/arch_sbo/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
get_sbo_termination = lambda *_, **__: None


__all__ = ['get_arch_sbo_rbf', 'get_arch_sbo_gp', 'HAS_ARCH_SBO', 'get_sbo_termination', 'get_sbo']
__all__ = ['get_arch_sbo_rbf', 'get_arch_sbo_gp', 'HAS_ARCH_SBO', 'get_sbo_termination', 'get_sbo',
'ConstraintAggregation']


def get_arch_sbo_rbf(init_size: int = 100, results_folder=None, **kwargs) -> InfillAlgorithm:
Expand Down
11 changes: 9 additions & 2 deletions sb_arch_opt/algo/pymoo_interface/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,12 @@ def __init__(self, *args, **kwargs):

self._init_sampling = self.initialization.sampling

def set_doe_size(self, problem, doe_size: int, **kwargs):
"""Set the DOE size, also if the algo is already initialized with a prior population"""
def set_doe_size(self, problem, doe_size: int, **kwargs) -> bool:
"""
Set the DOE size, also if the algo is already initialized with a prior population.
Returns whether the DOE size was increased.
"""
was_increased = False
self.pop_size = doe_size
self._set_termination()

Expand All @@ -126,9 +130,12 @@ def set_doe_size(self, problem, doe_size: int, **kwargs):
add_pop = self.repair(problem, add_pop)

self.initialization.sampling = Population.merge(init_pop, add_pop)
was_increased = True

log.info(f'New DOE size: {len(self.initialization.sampling)}')

return was_increased

def _set_termination(self):
self.termination = MaximumFunctionCallTermination(n_max_evals=self.pop_size)

Expand Down
47 changes: 30 additions & 17 deletions sb_arch_opt/algo/pymoo_interface/storage_restart.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from sb_arch_opt.sampling import LargeDuplicateElimination

__all__ = ['load_from_previous_results', 'initialize_from_previous_results', 'ResultsStorageCallback',
'ArchOptEvaluator']
'ArchOptEvaluator', 'set_initial_population']

log = logging.getLogger('sb_arch_opt.pymoo')

Expand Down Expand Up @@ -76,16 +76,7 @@ def load_from_previous_results(problem: ArchOptProblemBase, result_folder: str,
return

# Set evaluated flags
def _set_eval(ind: Individual):
nonlocal n_evaluated
# Assume evaluated but failed points have Inf as output values
is_eval = ~np.all(np.isnan(ind.get('F')))
if is_eval:
ind.evaluated.update({'X', 'F', 'G', 'H'})
n_evaluated += 1

n_evaluated = 0
population.apply(_set_eval)
n_evaluated = ArchOptEvaluator.pop_set_eval_flags(population)
log.info(f'Evaluation status: {n_evaluated} of {len(population)} ({(n_evaluated/len(population))*100:.1f}%) '
f'are already evaluated')

Expand Down Expand Up @@ -312,7 +303,10 @@ def eval_batch_post(self, problem, pop: Population, batch_pop: Population, evalu

self._apply_extreme_barrier(batch_pop, evaluate_values_of=evaluate_values_of)
intermediate_pop = self._normalize_pop(pop, evaluate_values_of, evaluated_pop=self._evaluated_pop)
self._store_intermediate(problem, intermediate_pop)

if self.results_folder is not None:
self._store_intermediate(problem, intermediate_pop)
return intermediate_pop

def _apply_extreme_barrier(self, pop: Population, evaluate_values_of: list = None):
if evaluate_values_of is None:
Expand Down Expand Up @@ -378,8 +372,10 @@ def store_pop(self, pop: Population, cumulative=False):
csv_path = os.path.join(self.results_folder, f'pymoo_population{cumulative_str}.csv')
self.get_pop_as_df(pop).to_csv(csv_path)

@staticmethod
def get_pop_as_df(pop: Population) -> pd.DataFrame:
@classmethod
def get_pop_as_df(cls, pop: Population) -> pd.DataFrame:
pop = cls._normalize_pop(pop, ['F', 'G', 'H'])

cols = []
all_data = []
for symbol in ['x', 'f', 'g', 'h']:
Expand All @@ -389,8 +385,8 @@ def get_pop_as_df(pop: Population) -> pd.DataFrame:

return pd.DataFrame(columns=cols, data=np.column_stack(all_data))

@staticmethod
def get_pop_from_df(df: pd.DataFrame):
@classmethod
def get_pop_from_df(cls, df: pd.DataFrame):
symbols = list(set(col[0] for col in df.columns))

def _get_symbol_data(symbol):
Expand All @@ -402,7 +398,24 @@ def _get_symbol_data(symbol):
return symbol_data

data = {sym.upper(): _get_symbol_data(sym) for sym in symbols}
return Population.new(**data)
pop = Population.new(**data)
cls.pop_set_eval_flags(pop)
return pop

@staticmethod
def pop_set_eval_flags(pop: Population):
# Set evaluated flags
def _set_eval(ind: Individual):
nonlocal n_evaluated
# Assume evaluated but failed points have Inf as output values
is_eval = ~np.all(np.isnan(ind.get('F')))
if is_eval:
ind.evaluated.update({'X', 'F', 'G', 'H'})
n_evaluated += 1

n_evaluated = 0
pop.apply(_set_eval)
return n_evaluated

def _store_intermediate_problem(self, problem):
if isinstance(problem, ArchOptProblemBase):
Expand Down
7 changes: 4 additions & 3 deletions sb_arch_opt/tests/algo/test_pymoo.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,15 @@ def test_doe_algo(problem: ArchOptProblemBase):

doe_algo2 = get_doe_algo(doe_size=100, results_folder=tmp_folder)
initialize_from_previous_results(doe_algo2, problem, tmp_folder)
doe_algo2.set_doe_size(problem, doe_size=200)
assert doe_algo2.set_doe_size(problem, doe_size=200)
doe_algo2.setup(problem)
doe_algo2.run()
assert doe_algo2.evaluator.n_eval == 200
assert len(doe_algo2.pop) == 200

doe_algo3 = get_doe_algo(doe_size=200, results_folder=tmp_folder)
initialize_from_previous_results(doe_algo3, problem, tmp_folder)
doe_algo3.set_doe_size(problem, doe_size=150)
assert not doe_algo3.set_doe_size(problem, doe_size=150)
doe_algo3.setup(problem)
doe_algo3.run()
assert doe_algo3.evaluator.n_eval == 200
Expand Down Expand Up @@ -370,7 +370,8 @@ def test_partial_doe_restart_ask_tell():

evaluator = doe_algo.evaluator
assert isinstance(evaluator, ArchOptEvaluator)
pop_to_eval = evaluator.eval_pre(pop)
for _ in range(5):
pop_to_eval = evaluator.eval_pre(pop)

for batch_pop in evaluator.iter_pop_batch(problem, pop_to_eval):
out = problem.evaluate(batch_pop.get('X'), return_as_dictionary=True)
Expand Down

0 comments on commit 9a2b395

Please sign in to comment.