xopt 2.4.2__tar.gz → 2.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xopt-2.4.2 → xopt-2.4.4}/PKG-INFO +1 -1
- {xopt-2.4.2 → xopt-2.4.4}/xopt/_version.py +2 -2
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bax/visualize.py +1 -1
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bayesian_generator.py +63 -26
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/expected_improvement.py +3 -5
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/mggpo.py +1 -1
- xopt-2.4.4/xopt/generators/bayesian/mobo.py +135 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/multi_fidelity.py +1 -1
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/time_dependent.py +2 -2
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/turbo.py +32 -13
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/utils.py +7 -3
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/ga/cnsga.py +4 -2
- {xopt-2.4.2 → xopt-2.4.4}/xopt/numerical_optimizer.py +5 -2
- xopt-2.4.4/xopt/resources/test_functions/zdt.py +51 -0
- xopt-2.4.4/xopt/tests/generators/bayesian/test_mobo.py +249 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_turbo.py +36 -3
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_numerical_optimizer.py +4 -1
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_vocs.py +34 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/vocs.py +16 -2
- {xopt-2.4.2 → xopt-2.4.4}/xopt.egg-info/PKG-INFO +1 -1
- {xopt-2.4.2 → xopt-2.4.4}/xopt.egg-info/SOURCES.txt +1 -0
- xopt-2.4.2/xopt/generators/bayesian/mobo.py +0 -66
- xopt-2.4.2/xopt/tests/generators/bayesian/test_mobo.py +0 -96
- {xopt-2.4.2 → xopt-2.4.4}/.flake8 +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/.pre-commit-config.yaml +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/LICENSE +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/MANIFEST.in +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/README.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/benchmarks/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/benchmarks/test_mo.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/algorithms.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/evaluator.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/generators/bayesian.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/generators/genetic.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/generators/scipy.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/generators.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/vocs.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/api/xopt.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/assets/Xopt-logo.png +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/assets/xopt_overview.pdf +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/index.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/installation.md +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/overrides/main.html +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/docs/stylesheets/extra.css +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/environment.yml +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/mkdocs.yml +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/pyproject.toml +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/scripts/execute_notebooks.bash +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/scripts/run_benchmarks.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/scripts/run_examples.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/scripts/run_tests.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/setup.cfg +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/asynchronous.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/base.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/errors.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/evaluator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/base_model.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bax/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bax/acquisition.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bax/algorithms.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bax_generator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/bayesian_exploration.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/constrained_acquisition.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/hessian_kernel.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/heteroskedastic.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/identity.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/log_acquisition_function.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/multi_fidelity.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/custom_botorch/proximal.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/models/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/models/prior_mean.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/models/standard.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/models/time_dependent.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/objectives.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/upper_confidence_bound.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/bayesian/visualize.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/es/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/es/extremumseeking.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/ga/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/ga/deap_creator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/ga/deap_fitness_with_constraints.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/ga/fitness_with_constraints.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/random.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/rcds/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/rcds/rcds.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/scipy/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/scipy/latin_hypercube.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/scipy/neldermead.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/generators/utils.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/log.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/mpi/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/mpi/run.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/pydantic.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/benchmarking.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/ackley_20.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/haverly_pooling.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/modified_tnk.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/multi_objective.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/problem.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/rosenbrock.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/sinusoid_1d.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/sphere_100.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/sphere_20.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/sphere_50.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/test_functions/tnk.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/resources/testing.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/__init__.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_bax.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_bayesian_exploration.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_bayesian_generator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_constraints.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_custom_model.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_expected_improvement.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_hessian_kernel.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_high_level.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_mggpo.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_model_constructor.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_multi_fidelity.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_time_dependent_bo.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_upper_confidence_bound.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/bayesian/test_utils.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/ga/test_cnsga.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/test_extremum_seeking.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/test_latin_hypercube.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/test_neldermead.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/test_random.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/generators/test_rcds.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_evaluator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_generator.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_io.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_mpi.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_perf.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_pydantic.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_utils.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/tests/test_xopt.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt/utils.py +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt.egg-info/dependency_links.txt +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt.egg-info/requires.txt +0 -0
- {xopt-2.4.2 → xopt-2.4.4}/xopt.egg-info/top_level.txt +0 -0
|
@@ -103,7 +103,7 @@ def visualize_virtual_objective(
|
|
|
103
103
|
bounds = generator._get_optimization_bounds()
|
|
104
104
|
kwargs = kwargs if kwargs else {}
|
|
105
105
|
objective_values = generator.algorithm.evaluate_virtual_objective(
|
|
106
|
-
bax_model, x, bounds, tkwargs=generator.
|
|
106
|
+
bax_model, x, bounds, tkwargs=generator.tkwargs, n_samples=n_samples, **kwargs
|
|
107
107
|
)
|
|
108
108
|
|
|
109
109
|
# get sample stats
|
|
@@ -4,13 +4,14 @@ import time
|
|
|
4
4
|
import warnings
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from copy import deepcopy
|
|
7
|
-
from typing import Dict, List, Optional
|
|
7
|
+
from typing import Dict, List, Optional, Union
|
|
8
8
|
|
|
9
9
|
import pandas as pd
|
|
10
10
|
import torch
|
|
11
11
|
from botorch.acquisition import FixedFeatureAcquisitionFunction, qUpperConfidenceBound
|
|
12
12
|
from botorch.models.model import Model
|
|
13
13
|
from botorch.sampling import get_sampler
|
|
14
|
+
from botorch.utils.multi_objective import is_non_dominated
|
|
14
15
|
from botorch.utils.multi_objective.box_decompositions import DominatedPartitioning
|
|
15
16
|
from gpytorch import Module
|
|
16
17
|
from pydantic import Field, field_validator, PositiveInt, SerializeAsAny
|
|
@@ -384,7 +385,7 @@ class BayesianGenerator(Generator, ABC):
|
|
|
384
385
|
self.vocs.output_names,
|
|
385
386
|
data,
|
|
386
387
|
{name: variable_bounds[name] for name in self.model_input_names},
|
|
387
|
-
**self.
|
|
388
|
+
**self.tkwargs,
|
|
388
389
|
)
|
|
389
390
|
|
|
390
391
|
if update_internal:
|
|
@@ -407,8 +408,18 @@ class BayesianGenerator(Generator, ABC):
|
|
|
407
408
|
# get acquisition function
|
|
408
409
|
acq_funct = self.get_acquisition(model)
|
|
409
410
|
|
|
410
|
-
# get candidates
|
|
411
|
-
|
|
411
|
+
# get initial candidates to start acquisition function optimization
|
|
412
|
+
initial_points = self._get_initial_conditions(n_candidates)
|
|
413
|
+
|
|
414
|
+
# get candidates -- grid optimizer does not support batch_initial_conditions
|
|
415
|
+
if isinstance(self.numerical_optimizer, GridOptimizer):
|
|
416
|
+
candidates = self.numerical_optimizer.optimize(
|
|
417
|
+
acq_funct, bounds, n_candidates
|
|
418
|
+
)
|
|
419
|
+
else:
|
|
420
|
+
candidates = self.numerical_optimizer.optimize(
|
|
421
|
+
acq_funct, bounds, n_candidates, batch_initial_conditions=initial_points
|
|
422
|
+
)
|
|
412
423
|
return candidates
|
|
413
424
|
|
|
414
425
|
def get_training_data(self, data: pd.DataFrame) -> pd.DataFrame:
|
|
@@ -456,7 +467,7 @@ class BayesianGenerator(Generator, ABC):
|
|
|
456
467
|
input names (variables), and the resulting tensor is configured with the data
|
|
457
468
|
type and device settings from the generator.
|
|
458
469
|
"""
|
|
459
|
-
return torch.tensor(data[self.model_input_names].to_numpy(), **self.
|
|
470
|
+
return torch.tensor(data[self.model_input_names].to_numpy(), **self.tkwargs)
|
|
460
471
|
|
|
461
472
|
def get_acquisition(self, model):
|
|
462
473
|
"""
|
|
@@ -536,6 +547,11 @@ class BayesianGenerator(Generator, ABC):
|
|
|
536
547
|
"""displays the GP models"""
|
|
537
548
|
return visualize_generator_model(self, **kwargs)
|
|
538
549
|
|
|
550
|
+
def _get_initial_conditions(self, n_candidates=1) -> Union[Tensor, None]:
|
|
551
|
+
"""overwrite if algorithm should specifiy initial candidates for optimizing
|
|
552
|
+
the acquisition function"""
|
|
553
|
+
return None
|
|
554
|
+
|
|
539
555
|
def _process_candidates(self, candidates: Tensor):
|
|
540
556
|
"""process pytorch candidates from optimizing the acquisition function"""
|
|
541
557
|
logger.debug(f"Best candidate from optimize {candidates}")
|
|
@@ -581,7 +597,7 @@ class BayesianGenerator(Generator, ABC):
|
|
|
581
597
|
|
|
582
598
|
return self.custom_objective
|
|
583
599
|
else:
|
|
584
|
-
return create_mc_objective(self.vocs, self.
|
|
600
|
+
return create_mc_objective(self.vocs, self.tkwargs)
|
|
585
601
|
|
|
586
602
|
def _get_constraint_callables(self):
|
|
587
603
|
"""return constratint callable determined by vocs"""
|
|
@@ -591,7 +607,7 @@ class BayesianGenerator(Generator, ABC):
|
|
|
591
607
|
return constraint_callables
|
|
592
608
|
|
|
593
609
|
@property
|
|
594
|
-
def
|
|
610
|
+
def tkwargs(self):
|
|
595
611
|
# set device and data type for generator
|
|
596
612
|
device = "cpu"
|
|
597
613
|
if self.use_cuda:
|
|
@@ -627,7 +643,7 @@ class BayesianGenerator(Generator, ABC):
|
|
|
627
643
|
|
|
628
644
|
def _get_bounds(self):
|
|
629
645
|
"""convert bounds from vocs to torch tensors"""
|
|
630
|
-
return torch.tensor(self.vocs.bounds, **self.
|
|
646
|
+
return torch.tensor(self.vocs.bounds, **self.tkwargs)
|
|
631
647
|
|
|
632
648
|
def _get_optimization_bounds(self):
|
|
633
649
|
"""
|
|
@@ -720,7 +736,7 @@ class BayesianGenerator(Generator, ABC):
|
|
|
720
736
|
"from, add data first to use during BO"
|
|
721
737
|
)
|
|
722
738
|
last_point = torch.tensor(
|
|
723
|
-
self.data[self.vocs.variable_names].iloc[-1].to_numpy(), **self.
|
|
739
|
+
self.data[self.vocs.variable_names].iloc[-1].to_numpy(), **self.tkwargs
|
|
724
740
|
)
|
|
725
741
|
|
|
726
742
|
# bound lengths based on vocs for normalization
|
|
@@ -728,8 +744,8 @@ class BayesianGenerator(Generator, ABC):
|
|
|
728
744
|
|
|
729
745
|
# get maximum travel distances
|
|
730
746
|
max_travel_distances = torch.tensor(
|
|
731
|
-
self.max_travel_distances, **self.
|
|
732
|
-
) * torch.tensor(lengths, **self.
|
|
747
|
+
self.max_travel_distances, **self.tkwargs
|
|
748
|
+
) * torch.tensor(lengths, **self.tkwargs)
|
|
733
749
|
max_travel_bounds = torch.stack(
|
|
734
750
|
(last_point - max_travel_distances, last_point + max_travel_distances)
|
|
735
751
|
)
|
|
@@ -774,33 +790,54 @@ class MultiObjectiveBayesianGenerator(BayesianGenerator, ABC):
|
|
|
774
790
|
supported"
|
|
775
791
|
)
|
|
776
792
|
|
|
777
|
-
return torch.tensor(pt, **self.
|
|
793
|
+
return torch.tensor(pt, **self.tkwargs)
|
|
778
794
|
|
|
779
|
-
def
|
|
780
|
-
"""
|
|
781
|
-
|
|
782
|
-
|
|
795
|
+
def _get_scaled_data(self):
|
|
796
|
+
"""get scaled input/objective data for use with botorch logic which assumes
|
|
797
|
+
maximization for each objective"""
|
|
798
|
+
var_df, obj_df, _, _ = self.vocs.extract_data(
|
|
799
|
+
self.data, return_valid=True, return_raw=True
|
|
783
800
|
)
|
|
784
801
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
802
|
+
variable_data = torch.tensor(var_df[self.vocs.variable_names].to_numpy())
|
|
803
|
+
objective_data = torch.tensor(obj_df[self.vocs.objective_names].to_numpy())
|
|
804
|
+
weights = set_botorch_weights(self.vocs).to(**self.tkwargs)[
|
|
805
|
+
: self.vocs.n_objectives
|
|
806
|
+
]
|
|
807
|
+
return variable_data, objective_data * weights, weights
|
|
790
808
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
weights = set_botorch_weights(self.vocs).to(**self._tkwargs)
|
|
794
|
-
objective_data = objective_data * weights
|
|
809
|
+
def calculate_hypervolume(self):
|
|
810
|
+
"""compute hypervolume given data"""
|
|
795
811
|
|
|
796
812
|
# compute hypervolume
|
|
797
813
|
bd = DominatedPartitioning(
|
|
798
|
-
ref_point=self.torch_reference_point, Y=
|
|
814
|
+
ref_point=self.torch_reference_point, Y=self._get_scaled_data()[1]
|
|
799
815
|
)
|
|
800
816
|
volume = bd.compute_hypervolume().item()
|
|
801
817
|
|
|
802
818
|
return volume
|
|
803
819
|
|
|
820
|
+
def get_pareto_front(self):
|
|
821
|
+
"""compute the pareto front x/y values given data"""
|
|
822
|
+
variable_data, objective_data, weights = self._get_scaled_data()
|
|
823
|
+
obj_data = torch.vstack(
|
|
824
|
+
(self.torch_reference_point.unsqueeze(0), objective_data)
|
|
825
|
+
)
|
|
826
|
+
var_data = torch.vstack(
|
|
827
|
+
(
|
|
828
|
+
torch.full_like(variable_data[0], float("Nan")).unsqueeze(0),
|
|
829
|
+
variable_data,
|
|
830
|
+
)
|
|
831
|
+
)
|
|
832
|
+
non_dominated = is_non_dominated(obj_data)
|
|
833
|
+
|
|
834
|
+
# note need to undo weights for real number output
|
|
835
|
+
# only return values if non nan values exist
|
|
836
|
+
if torch.all(torch.isnan(var_data[non_dominated])):
|
|
837
|
+
return None, None
|
|
838
|
+
else:
|
|
839
|
+
return var_data[non_dominated], obj_data[non_dominated] / weights
|
|
840
|
+
|
|
804
841
|
|
|
805
842
|
def formatted_base_docstring():
|
|
806
843
|
return "\nBase Generator\n---------------\n" + BayesianGenerator.__doc__
|
|
@@ -40,7 +40,7 @@ class ExpectedImprovementGenerator(BayesianGenerator):
|
|
|
40
40
|
# analytic acquisition function for single candidate generation with
|
|
41
41
|
# basic objective
|
|
42
42
|
# note that the analytic version cannot handle custom objectives
|
|
43
|
-
weights = set_botorch_weights(self.vocs).to(**self.
|
|
43
|
+
weights = set_botorch_weights(self.vocs).to(**self.tkwargs)
|
|
44
44
|
posterior_transform = ScalarizedPosteriorTransform(weights)
|
|
45
45
|
acq = ExpectedImprovement(
|
|
46
46
|
model, best_f=best_f, posterior_transform=posterior_transform
|
|
@@ -52,14 +52,12 @@ class ExpectedImprovementGenerator(BayesianGenerator):
|
|
|
52
52
|
"""get best function value for EI based on the objective"""
|
|
53
53
|
if isinstance(objective, CustomXoptObjective):
|
|
54
54
|
best_f = objective(
|
|
55
|
-
torch.tensor(
|
|
56
|
-
self.vocs.observable_data(data).to_numpy(), **self._tkwargs
|
|
57
|
-
)
|
|
55
|
+
torch.tensor(self.vocs.observable_data(data).to_numpy(), **self.tkwargs)
|
|
58
56
|
).max()
|
|
59
57
|
else:
|
|
60
58
|
# analytic acquisition function for single candidate generation
|
|
61
59
|
best_f = -torch.tensor(
|
|
62
|
-
self.vocs.objective_data(data).min().values, **self.
|
|
60
|
+
self.vocs.objective_data(data).min().values, **self.tkwargs
|
|
63
61
|
)
|
|
64
62
|
|
|
65
63
|
return best_f
|
|
@@ -32,7 +32,7 @@ class MGGPOGenerator(MultiObjectiveBayesianGenerator):
|
|
|
32
32
|
ga_candidates = self.ga_generator.generate(n_candidates * 10)
|
|
33
33
|
ga_candidates = pd.DataFrame(ga_candidates)[self.vocs.variable_names].to_numpy()
|
|
34
34
|
ga_candidates = torch.unique(
|
|
35
|
-
torch.tensor(ga_candidates, **self.
|
|
35
|
+
torch.tensor(ga_candidates, **self.tkwargs), dim=0
|
|
36
36
|
).reshape(-1, 1, self.vocs.n_variables)
|
|
37
37
|
|
|
38
38
|
if ga_candidates.shape[0] < n_candidates:
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
from botorch.acquisition import FixedFeatureAcquisitionFunction
|
|
5
|
+
from botorch.acquisition.multi_objective import qNoisyExpectedHypervolumeImprovement
|
|
6
|
+
from botorch.acquisition.multi_objective.logei import (
|
|
7
|
+
qLogNoisyExpectedHypervolumeImprovement,
|
|
8
|
+
)
|
|
9
|
+
from botorch.utils import draw_sobol_samples
|
|
10
|
+
from pydantic import Field
|
|
11
|
+
from torch import Tensor
|
|
12
|
+
|
|
13
|
+
from xopt.generators.bayesian.bayesian_generator import MultiObjectiveBayesianGenerator
|
|
14
|
+
|
|
15
|
+
from xopt.generators.bayesian.objectives import create_mobo_objective
|
|
16
|
+
from xopt.numerical_optimizer import LBFGSOptimizer
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MOBOGenerator(MultiObjectiveBayesianGenerator):
|
|
20
|
+
name = "mobo"
|
|
21
|
+
supports_batch_generation: bool = True
|
|
22
|
+
use_pf_as_initial_points: bool = Field(
|
|
23
|
+
False,
|
|
24
|
+
description="flag to specify if pareto front points are to be used during "
|
|
25
|
+
"optimization of the acquisition function",
|
|
26
|
+
)
|
|
27
|
+
__doc__ = """Implements Multi-Objective Bayesian Optimization using the Expected
|
|
28
|
+
Hypervolume Improvement acquisition function"""
|
|
29
|
+
|
|
30
|
+
def _get_objective(self):
|
|
31
|
+
return create_mobo_objective(self.vocs)
|
|
32
|
+
|
|
33
|
+
def get_acquisition(self, model):
|
|
34
|
+
"""
|
|
35
|
+
Returns a function that can be used to evaluate the acquisition function
|
|
36
|
+
"""
|
|
37
|
+
if model is None:
|
|
38
|
+
raise ValueError("model cannot be None")
|
|
39
|
+
|
|
40
|
+
# get base acquisition function
|
|
41
|
+
acq = self._get_acquisition(model)
|
|
42
|
+
|
|
43
|
+
# apply fixed features if specified in the generator
|
|
44
|
+
if self.fixed_features is not None:
|
|
45
|
+
# get input dim
|
|
46
|
+
dim = len(self.model_input_names)
|
|
47
|
+
columns = []
|
|
48
|
+
values = []
|
|
49
|
+
for name, value in self.fixed_features.items():
|
|
50
|
+
columns += [self.model_input_names.index(name)]
|
|
51
|
+
values += [value]
|
|
52
|
+
|
|
53
|
+
acq = FixedFeatureAcquisitionFunction(
|
|
54
|
+
acq_function=acq, d=dim, columns=columns, values=values
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
return acq
|
|
58
|
+
|
|
59
|
+
def _get_acquisition(self, model):
|
|
60
|
+
inputs = self.get_input_data(self.data)
|
|
61
|
+
sampler = self._get_sampler(model)
|
|
62
|
+
|
|
63
|
+
if self.log_transform_acquisition_function:
|
|
64
|
+
acqclass = qLogNoisyExpectedHypervolumeImprovement
|
|
65
|
+
else:
|
|
66
|
+
acqclass = qNoisyExpectedHypervolumeImprovement
|
|
67
|
+
|
|
68
|
+
acq = acqclass(
|
|
69
|
+
model,
|
|
70
|
+
X_baseline=inputs,
|
|
71
|
+
constraints=self._get_constraint_callables(),
|
|
72
|
+
ref_point=self.torch_reference_point,
|
|
73
|
+
sampler=sampler,
|
|
74
|
+
objective=self._get_objective(),
|
|
75
|
+
cache_root=False,
|
|
76
|
+
prune_baseline=True,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return acq
|
|
80
|
+
|
|
81
|
+
def _get_initial_conditions(self, n_candidates=1) -> Union[Tensor, None]:
|
|
82
|
+
"""
|
|
83
|
+
generate initial candidates for optimizing the acquisition function based on
|
|
84
|
+
the pareto front
|
|
85
|
+
|
|
86
|
+
If `use_pf_as_initial_points` flag is set to true then the current
|
|
87
|
+
Pareto-optimal set is used as initial points for optimizing the acquisition
|
|
88
|
+
function instead of randomly selected points (random points fill in the set
|
|
89
|
+
if `num_restarts` is greater than the number of points in the Pareto set).
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
A `num_restarts x q x d` tensor of initial conditions.
|
|
93
|
+
|
|
94
|
+
"""
|
|
95
|
+
if self.use_pf_as_initial_points:
|
|
96
|
+
if isinstance(self.numerical_optimizer, LBFGSOptimizer):
|
|
97
|
+
bounds = self._get_optimization_bounds()
|
|
98
|
+
num_restarts = self.numerical_optimizer.n_restarts
|
|
99
|
+
|
|
100
|
+
pf_locations, _ = self.get_pareto_front()
|
|
101
|
+
|
|
102
|
+
# if there is no pareto front just return None to revert back to
|
|
103
|
+
# default behavior
|
|
104
|
+
if pf_locations is None:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
initial_points = torch.clone(pf_locations)
|
|
108
|
+
|
|
109
|
+
# add the q dimension
|
|
110
|
+
initial_points = initial_points.unsqueeze(1)
|
|
111
|
+
initial_points = initial_points.expand([-1, n_candidates, -1])
|
|
112
|
+
|
|
113
|
+
# initial_points must equal the number of restarts
|
|
114
|
+
if len(initial_points) < num_restarts:
|
|
115
|
+
# add random points to the list inside the bounds
|
|
116
|
+
sobol_samples = draw_sobol_samples(
|
|
117
|
+
bounds, num_restarts - len(initial_points), n_candidates
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
initial_points = torch.cat([initial_points, sobol_samples])
|
|
121
|
+
elif len(initial_points) > num_restarts:
|
|
122
|
+
# if there are too many select the first `num_restarts` points at
|
|
123
|
+
# random
|
|
124
|
+
initial_points = initial_points[
|
|
125
|
+
torch.randperm(len(initial_points))
|
|
126
|
+
][:num_restarts]
|
|
127
|
+
|
|
128
|
+
return initial_points
|
|
129
|
+
else:
|
|
130
|
+
raise RuntimeWarning(
|
|
131
|
+
"cannot use PF as initial optimization points "
|
|
132
|
+
"for non-LBFGS optimizers, ignoring flag"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return None
|
|
@@ -155,7 +155,7 @@ class MultiFidelityGenerator(MOBOGenerator):
|
|
|
155
155
|
"""select the best point at the maximum fidelity"""
|
|
156
156
|
|
|
157
157
|
# define single objective based on vocs
|
|
158
|
-
weights = torch.zeros(self.vocs.n_outputs, **self.
|
|
158
|
+
weights = torch.zeros(self.vocs.n_outputs, **self.tkwargs)
|
|
159
159
|
for idx, ele in enumerate(self.vocs.objective_names):
|
|
160
160
|
if self.vocs.objectives[ele] == "MINIMIZE":
|
|
161
161
|
weights[idx] = -1.0
|
|
@@ -95,7 +95,7 @@ class TimeDependentBayesianGenerator(BayesianGenerator, ABC):
|
|
|
95
95
|
type and device settings from the generator.
|
|
96
96
|
"""
|
|
97
97
|
return torch.tensor(
|
|
98
|
-
data[self.model_input_names + ["time"]].to_numpy(), **self.
|
|
98
|
+
data[self.model_input_names + ["time"]].to_numpy(), **self.tkwargs
|
|
99
99
|
)
|
|
100
100
|
|
|
101
101
|
def get_acquisition(self, model):
|
|
@@ -103,7 +103,7 @@ class TimeDependentBayesianGenerator(BayesianGenerator, ABC):
|
|
|
103
103
|
|
|
104
104
|
# identify which column has the `time` attribute
|
|
105
105
|
column = [-1]
|
|
106
|
-
value = torch.tensor(self.target_prediction_time, **self.
|
|
106
|
+
value = torch.tensor(self.target_prediction_time, **self.tkwargs).unsqueeze(0)
|
|
107
107
|
fixed_acq = FixedFeatureAcquisitionFunction(
|
|
108
108
|
acq, self.vocs.n_variables + 1, column, value
|
|
109
109
|
)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import math
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import Dict, Optional
|
|
4
|
+
from typing import Dict, Optional
|
|
5
5
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
import torch
|
|
8
8
|
from pydantic import ConfigDict, Field, PositiveFloat, PositiveInt
|
|
9
|
+
from torch import Tensor
|
|
9
10
|
|
|
10
11
|
from xopt.pydantic import XoptBaseModel
|
|
11
12
|
from xopt.vocs import VOCS
|
|
@@ -51,7 +52,6 @@ class TurboController(XoptBaseModel, ABC):
|
|
|
51
52
|
restrict_model_data: Optional[bool] = Field(
|
|
52
53
|
True, description="flag to restrict model data to within the trust region"
|
|
53
54
|
)
|
|
54
|
-
tkwargs: Dict[str, Union[torch.dtype, str]] = Field({"dtype": torch.double})
|
|
55
55
|
|
|
56
56
|
model_config = ConfigDict(validate_assignment=True, arbitrary_types_allowed=True)
|
|
57
57
|
|
|
@@ -79,9 +79,24 @@ class TurboController(XoptBaseModel, ABC):
|
|
|
79
79
|
)
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
-
def get_trust_region(self, generator):
|
|
82
|
+
def get_trust_region(self, generator) -> Tensor:
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
Return the trust region based on the generator. The trust region is a
|
|
86
|
+
rectangular region around a center point. The sides of the trust region are
|
|
87
|
+
given by the `length` parameter and are scaled according to the generator
|
|
88
|
+
model lengthscales (if available).
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
generator : BayesianGenerator
|
|
93
|
+
Generator object used to supply the model and datatypes for the returned
|
|
94
|
+
trust region.
|
|
95
|
+
|
|
96
|
+
"""
|
|
97
|
+
|
|
83
98
|
model = generator.model
|
|
84
|
-
bounds = torch.tensor(self.vocs.bounds, **
|
|
99
|
+
bounds = torch.tensor(self.vocs.bounds, **generator.tkwargs)
|
|
85
100
|
|
|
86
101
|
if self.center_x is not None:
|
|
87
102
|
# get bounds width
|
|
@@ -89,18 +104,22 @@ class TurboController(XoptBaseModel, ABC):
|
|
|
89
104
|
|
|
90
105
|
# Scale the TR to be proportional to the lengthscales of the objective model
|
|
91
106
|
x_center = torch.tensor(
|
|
92
|
-
[self.center_x[ele] for ele in self.vocs.variable_names],
|
|
107
|
+
[self.center_x[ele] for ele in self.vocs.variable_names],
|
|
108
|
+
**generator.tkwargs,
|
|
93
109
|
).unsqueeze(dim=0)
|
|
94
110
|
|
|
95
|
-
if
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
].covar_module.base_kernel.lengthscale.detach()
|
|
111
|
+
# default weights are 1 (if there is no model or a model without
|
|
112
|
+
# lengthscales)
|
|
113
|
+
weights = 1.0
|
|
99
114
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
if model is not None:
|
|
116
|
+
if model.models[0].covar_module.base_kernel.lengthscale is not None:
|
|
117
|
+
lengthscales = model.models[
|
|
118
|
+
0
|
|
119
|
+
].covar_module.base_kernel.lengthscale.detach()
|
|
120
|
+
|
|
121
|
+
# calculate the ratios of lengthscales for each axis
|
|
122
|
+
weights = lengthscales / torch.prod(lengthscales) ** (1 / self.dim)
|
|
104
123
|
|
|
105
124
|
# calculate the tr bounding box
|
|
106
125
|
tr_lb = torch.clamp(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
1
2
|
from typing import List
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
@@ -206,16 +207,19 @@ def validate_turbo_controller_base(value, available_controller_types, info):
|
|
|
206
207
|
f"{available_controller_types.keys()}"
|
|
207
208
|
)
|
|
208
209
|
elif isinstance(value, dict):
|
|
210
|
+
value_copy = deepcopy(value)
|
|
209
211
|
# create turbo controller from dict input
|
|
210
212
|
if "name" not in value:
|
|
211
213
|
raise ValueError("turbo input dict needs to have a `name` attribute")
|
|
212
|
-
name =
|
|
214
|
+
name = value_copy.pop("name")
|
|
213
215
|
if name in available_controller_types:
|
|
214
216
|
# pop unnecessary elements
|
|
215
217
|
for ele in ["dim"]:
|
|
216
|
-
|
|
218
|
+
value_copy.pop(ele, None)
|
|
217
219
|
|
|
218
|
-
value = available_controller_types[name](
|
|
220
|
+
value = available_controller_types[name](
|
|
221
|
+
vocs=info.data["vocs"], **value_copy
|
|
222
|
+
)
|
|
219
223
|
else:
|
|
220
224
|
raise ValueError(
|
|
221
225
|
f"{value} not found, available values are "
|
|
@@ -123,7 +123,8 @@ class CNSGAGenerator(Generator):
|
|
|
123
123
|
return
|
|
124
124
|
|
|
125
125
|
if filename is None:
|
|
126
|
-
|
|
126
|
+
timestamp = xopt.utils.isotime(include_microseconds=True).replace(":", "_")
|
|
127
|
+
filename = f"{self.name}_offspring_{timestamp}.csv"
|
|
127
128
|
filename = os.path.join(self.output_path, filename)
|
|
128
129
|
|
|
129
130
|
self._offspring.to_csv(filename, index_label="xopt_index")
|
|
@@ -139,7 +140,8 @@ class CNSGAGenerator(Generator):
|
|
|
139
140
|
return
|
|
140
141
|
|
|
141
142
|
if filename is None:
|
|
142
|
-
|
|
143
|
+
timestamp = xopt.utils.isotime(include_microseconds=True).replace(":", "_")
|
|
144
|
+
filename = f"{self.name}_population_{timestamp}.csv"
|
|
143
145
|
filename = os.path.join(self.output_path, filename)
|
|
144
146
|
|
|
145
147
|
self.population.to_csv(filename, index_label="xopt_index")
|
|
@@ -15,7 +15,9 @@ class NumericalOptimizer(XoptBaseModel, ABC):
|
|
|
15
15
|
model_config = ConfigDict(extra="forbid")
|
|
16
16
|
|
|
17
17
|
@abstractmethod
|
|
18
|
-
def optimize(
|
|
18
|
+
def optimize(
|
|
19
|
+
self, function: AcquisitionFunction, bounds: Tensor, n_candidates=1, **kwargs
|
|
20
|
+
):
|
|
19
21
|
"""optimize a function to produce a number of candidate points that
|
|
20
22
|
minimize the function"""
|
|
21
23
|
pass
|
|
@@ -33,7 +35,7 @@ class LBFGSOptimizer(NumericalOptimizer):
|
|
|
33
35
|
|
|
34
36
|
model_config = ConfigDict(validate_assignment=True)
|
|
35
37
|
|
|
36
|
-
def optimize(self, function, bounds, n_candidates=1):
|
|
38
|
+
def optimize(self, function, bounds, n_candidates=1, **kwargs):
|
|
37
39
|
assert isinstance(bounds, Tensor)
|
|
38
40
|
if len(bounds) != 2:
|
|
39
41
|
raise ValueError("bounds must have the shape [2, ndim]")
|
|
@@ -46,6 +48,7 @@ class LBFGSOptimizer(NumericalOptimizer):
|
|
|
46
48
|
num_restarts=self.n_restarts,
|
|
47
49
|
timeout_sec=self.max_time,
|
|
48
50
|
options={"maxiter": self.max_iter},
|
|
51
|
+
**kwargs,
|
|
49
52
|
)
|
|
50
53
|
return candidates
|
|
51
54
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from xopt import VOCS
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def construct_zdt(n_dims, problem_index=1):
|
|
7
|
+
"""construct Xopt versions of the multiobjective ZDT test problems"""
|
|
8
|
+
vocs = VOCS(
|
|
9
|
+
variables={f"x{i + 1}": [0, 1] for i in range(n_dims)},
|
|
10
|
+
objectives={"f1": "MINIMIZE", "f2": "MINIMIZE"},
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
if problem_index == 1:
|
|
14
|
+
# ZDT1
|
|
15
|
+
def evaluate(input_dict):
|
|
16
|
+
x = np.array([input_dict[f"x{i + 1}"] for i in range(n_dims)])
|
|
17
|
+
|
|
18
|
+
f1 = x[0]
|
|
19
|
+
g = 1 + (9 / (n_dims - 1)) * np.sum(x[1:])
|
|
20
|
+
h = 1 - np.sqrt(f1 / g)
|
|
21
|
+
f2 = g * h
|
|
22
|
+
|
|
23
|
+
return {"f1": f1, "f2": f2, "g": g}
|
|
24
|
+
elif problem_index == 2:
|
|
25
|
+
|
|
26
|
+
def evaluate(input_dict):
|
|
27
|
+
x = np.array([input_dict[f"x{i + 1}"] for i in range(n_dims)])
|
|
28
|
+
|
|
29
|
+
f1 = x[0]
|
|
30
|
+
g = 1 + (9 / (n_dims - 1)) * np.sum(x[1:])
|
|
31
|
+
h = 1 - (f1 / g) ** 2
|
|
32
|
+
f2 = g * h
|
|
33
|
+
|
|
34
|
+
return {"f1": f1, "f2": f2, "g": g}
|
|
35
|
+
elif problem_index == 3:
|
|
36
|
+
|
|
37
|
+
def evaluate(input_dict):
|
|
38
|
+
x = np.array([input_dict[f"x{i + 1}"] for i in range(n_dims)])
|
|
39
|
+
|
|
40
|
+
f1 = x[0]
|
|
41
|
+
g = 1 + (9 / (n_dims - 1)) * np.sum(x[1:])
|
|
42
|
+
h = 1 - np.sqrt(f1 / g) - (f1 / g) * np.sin(10 * np.pi * f1)
|
|
43
|
+
f2 = g * h
|
|
44
|
+
|
|
45
|
+
return {"f1": f1, "f2": f2, "g": g}
|
|
46
|
+
else:
|
|
47
|
+
raise NotImplementedError()
|
|
48
|
+
|
|
49
|
+
reference_point = {"f1": 1.0, "f2": 1.0}
|
|
50
|
+
|
|
51
|
+
return vocs, evaluate, reference_point
|