Skip to content

Commit

Permalink
Initial attempt at running surrogate assisted optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
light-weaver committed Dec 12, 2024
1 parent 1f10e22 commit 9ca6cd2
Show file tree
Hide file tree
Showing 10 changed files with 15,868 additions and 40 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,7 @@ reference_solutions.json


# Scratch
scratch/
scratch/

# Surrogate models
surrogatemodels/
801 changes: 801 additions & 0 deletions datasets/MetallApplication/charpydata.csv

Large diffs are not rendered by default.

306 changes: 306 additions & 0 deletions datasets/MetallApplication/elondata.csv

Large diffs are not rendered by default.

547 changes: 547 additions & 0 deletions datasets/MetallApplication/utsdata.csv

Large diffs are not rendered by default.

609 changes: 609 additions & 0 deletions datasets/MetallApplication/ysdata.csv

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions desdeo/emo/operators/evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import polars as pl

from desdeo.problem import PolarsEvaluator, Problem
from desdeo.problem import Evaluator, Problem
from desdeo.tools.message import (
EvaluatorMessageTopics,
GenericMessage,
Expand Down Expand Up @@ -51,7 +51,7 @@ def __init__(
super().__init__(**kwargs)
self.problem = problem
# TODO(@light-weaver, @gialmisi): This can be so much more efficient.
self.evaluator = lambda x: PolarsEvaluator(problem)._polars_evaluate_flat(
self.evaluator = lambda x: Evaluator(problem).evaluate(
{name.symbol: x[name.symbol].to_list() for name in problem.get_flattened_variables()}
)
self.variable_symbols = [name.symbol for name in problem.variables]
Expand Down
37 changes: 15 additions & 22 deletions desdeo/problem/simulator_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import joblib
import numpy as np
import polars as pl
#import skops.io as sio
# import skops.io as sio

from desdeo.problem import (
ObjectiveTypeEnum,
Expand All @@ -22,13 +22,12 @@
class EvaluatorError(Exception):
"""Error raised when exceptions are encountered in an Evaluator."""


class Evaluator:
"""A class for creating evaluators for simulator based and surrogate based objectives, constraints and extras."""

def __init__(
self,
problem: Problem,
params: dict[str, dict] | None = None,
surrogate_paths: dict[str, Path] | None = None
self, problem: Problem, params: dict[str, dict] | None = None, surrogate_paths: dict[str, Path] | None = None
):
"""Creating an evaluator for simulator based and surrogate based objectives, constraints and extras.
Expand All @@ -45,24 +44,20 @@ def __init__(
self.problem = problem
# store the symbol and min or max multiplier as well (symbol, min/max multiplier [1 | -1])
self.objective_mix_max_mult = [
(objective.symbol, -1 if objective.maximize else 1)
for objective in problem.objectives
(objective.symbol, -1 if objective.maximize else 1) for objective in problem.objectives
]
# Gather symbols objectives of different types into their own lists
self.analytical_symbols = [
obj.symbol for obj in list(
filter(lambda x: x.objective_type == ObjectiveTypeEnum.analytical, problem.objectives)
)
obj.symbol
for obj in list(filter(lambda x: x.objective_type == ObjectiveTypeEnum.analytical, problem.objectives))
]
self.simulator_symbols = [
obj.symbol for obj in list(
filter(lambda x: x.objective_type == ObjectiveTypeEnum.simulator, problem.objectives)
)
obj.symbol
for obj in list(filter(lambda x: x.objective_type == ObjectiveTypeEnum.simulator, problem.objectives))
]
self.surrogate_symbols = [
obj.symbol for obj in list(
filter(lambda x: x.objective_type == ObjectiveTypeEnum.surrogate, problem.objectives)
)
obj.symbol
for obj in list(filter(lambda x: x.objective_type == ObjectiveTypeEnum.surrogate, problem.objectives))
]
# Gather any constraints' symbols
if problem.constraints is not None:
Expand Down Expand Up @@ -138,9 +133,7 @@ def _evaluate_simulator(self, xs: dict[str, list[int | float]]) -> pl.DataFrame:
params = self.params.get(sim.name, {})
# call the simulator with the decision variable values and parameters as dicts
res = subprocess.run(
[sys.executable, sim.file, '-d', str(xs), '-p', str(params)],
capture_output=True,
text=True
[sys.executable, sim.file, "-d", str(xs), "-p", str(params)], capture_output=True, text=True
)
if res.returncode == 0:
# gather the simulation results (a dict) into the results dataframe
Expand Down Expand Up @@ -172,7 +165,7 @@ def _evaluate_surrogates(self, xs: dict[str, list[int | float]]) -> pl.DataFrame
uncertainty predictions, then they are set as NaN.
"""
res = pl.DataFrame()
var = np.array([value for _, value in xs.items()]).T # has to be transpose (at least for sklearn models)
var = np.array([value for _, value in xs.items()]).T # has to be transpose (at least for sklearn models)
for symbol in self.surrogates:
# get a list of args accepted by the model's predict function
accepted_args = getfullargspec(self.surrogates[symbol].predict).args
Expand Down Expand Up @@ -234,7 +227,7 @@ def _load_surrogates(self, surrogate_paths: dict[str, Path] | None = None):
else: # TODO: if there are unknown types they should be checked
self.surrogates[obj.symbol] = sio.load(file, unknown_types)
#raise EvaluatorError(f"Untrusted types found in the model of {obj.symbol}: {unknown_types}")"""
for con in self.problem.constraints:
for con in self.problem.constraints or []: # if there are no constraints, an empty list is used
if con.surrogates is not None:
with Path.open(f"{con.surrogates[0]}", "rb") as file:
self.surrogates[con.symbol] = joblib.load(file)
Expand All @@ -244,7 +237,7 @@ def _load_surrogates(self, surrogate_paths: dict[str, Path] | None = None):
else: # TODO: if there are unknown types they should be checked
self.surrogates[con.symbol] = sio.load(file, unknown_types)
#raise EvaluatorError(f"Untrusted types found in the model of {obj.symbol}: {unknown_types}")"""
for extra in self.problem.extra_funcs:
for extra in self.problem.extra_funcs or []: # if there are no extra functions, an empty list is used
if extra.surrogates is not None:
with Path.open(f"{extra.surrogates[0]}", "rb") as file:
self.surrogates[extra.symbol] = joblib.load(file)
Expand Down
Loading

0 comments on commit 9ca6cd2

Please sign in to comment.