File indexing completed on 2026-08-12 08:24:54
0001
0002 """Example demonstrating BaseOptimizer and AxOptimizer integration with DTLZ2.
0003
0004 This script shows how to:
0005 1. Load optimizer configuration from YAML file
0006 2. Define a search space using the new BaseOptimizer interface
0007 3. Create an AxOptimizer instance
0008 4. Run multi-objective optimization on DTLZ2 benchmark
0009 5. Serialize and deserialize optimizer state
0010
0011 DTLZ2 is a multi-objective test problem with a known Pareto front.
0012 For 2 objectives and M variables, the optimal solutions lie on the unit sphere.
0013
0014 Project: AID2E v0.0.0 - AI assisted Detector Design for EIC
0015 Homepage: https://aid2e.github.io/aid2e-framework
0016 Repository: https://github.com/aid2e/AID2E-framework.git
0017 """
0018
0019 import os
0020 import json
0021 import yaml
0022 import numpy as np
0023 from pathlib import Path
0024 from aid2e.optimizers import BaseOptimizer, SearchSpace, AxOptimizer, AxOptimizerConfig
0025 from aid2e.utilities.configurations import OptimizerConfiguration
0026
0027
0028 def dtlz2(x_dict, n_objectives=2):
0029 """DTLZ2 multi-objective test problem.
0030
0031 Args:
0032 x_dict: Dictionary of parameters (x1, x2, ..., x10)
0033 n_objectives: Number of objectives (default: 2)
0034
0035 Returns:
0036 Dictionary with objective values {f1, f2, ...}
0037
0038 Notes:
0039 For 2 objectives, the Pareto front lies on the unit circle.
0040 Optimal solutions have g(x) = 0, where g is the sum of squared
0041 deviations from 0.5 for decision variables after the first M-1.
0042 """
0043
0044 x = np.array([x_dict[f'x{i+1}'] for i in range(len(x_dict))])
0045 k = len(x) - n_objectives + 1
0046
0047
0048 g = np.sum((x[n_objectives-1:] - 0.5) ** 2)
0049
0050
0051 objectives = {}
0052 for i in range(n_objectives):
0053 f = 1.0 + g
0054 for j in range(n_objectives - i - 1):
0055 f *= np.cos(x[j] * np.pi / 2.0)
0056 if i > 0:
0057 f *= np.sin(x[n_objectives - i - 1] * np.pi / 2.0)
0058 objectives[f'f{i+1}'] = f
0059
0060 return objectives
0061
0062
0063 def main():
0064 print("=" * 70)
0065 print("AID2E BaseOptimizer + AxOptimizer: DTLZ2 Example")
0066 print("=" * 70)
0067
0068
0069 print("\n1. Loading configuration from YAML file...")
0070
0071 config_path = Path(__file__).parent / "ax_dtlz2_config.json"
0072 with open(config_path, 'r') as f:
0073 config_data = json.load(f)
0074
0075
0076
0077
0078
0079 print(f" Config file: {config_path.name}")
0080 print(f" Problem: {config_data['name']}")
0081
0082
0083 print("\n2. Parsing optimization configuration...")
0084 optimizer_payload = config_data.get("optimizer", config_data)
0085 opt_config = OptimizerConfiguration(**optimizer_payload)
0086 objective_names = config_data.get("objectives", [])
0087 n_iterations = config_data.get("n_iterations", 30)
0088
0089 print(f" Optimizer: {opt_config.name}")
0090 print(f" Strategy: {opt_config.parameters.get('initialization_strategy', 'sobol')}")
0091 print(f" Generator: {opt_config.parameters.get('generator', 'BOTORCH_MODULAR')}")
0092
0093
0094 print("\n3. Creating AxOptimizerConfig...")
0095 optimizer_params = opt_config.parameters
0096 ax_config = AxOptimizerConfig(
0097 initialization_strategy=optimizer_params.get('initialization_strategy', 'sobol'),
0098 generator=optimizer_params.get('generator', 'BOTORCH_MODULAR'),
0099 generator_kwargs=optimizer_params.get('generator_kwargs', {}),
0100 generator_gen_kwargs=optimizer_params.get('generator_gen_kwargs', {}),
0101 objective_thresholds=optimizer_params.get('objective_thresholds'),
0102 n_initial_samples=optimizer_params.get('n_initial_samples', 10),
0103 n_iterations=n_iterations,
0104 batch_size=optimizer_params.get('batch_size', 3),
0105 seed=optimizer_params.get('seed', 42)
0106 )
0107 print(f" Initial samples: {ax_config.n_initial_samples}")
0108 print(f" Total iterations: {ax_config.n_iterations}")
0109 print(f" Batch size: {ax_config.batch_size}")
0110
0111
0112 print("\n4. Defining search space from configuration...")
0113 search_space_params = {}
0114 parameters = config_data.get('parameters', {})
0115 for param_name, param_config in parameters.items():
0116 search_space_params[param_name] = {
0117 "type": "range",
0118 "bounds": param_config["bounds"]
0119 }
0120
0121 search_space = SearchSpace(parameters=search_space_params)
0122 print(f" Parameters: {list(search_space.parameters.keys())}")
0123 print(f" Total dimensions: {len(search_space.parameters)}")
0124
0125
0126 print("\n5. Creating AxOptimizer...")
0127 optimizer = AxOptimizer(
0128 search_space=search_space,
0129 config=ax_config,
0130 objective_names=objective_names,
0131 seed=ax_config.seed
0132 )
0133 print(f" Optimizer: {optimizer}")
0134 print(f" Objectives: {objective_names}")
0135 print(f" Inherits from BaseOptimizer: {isinstance(optimizer, BaseOptimizer)}")
0136
0137
0138 print("\n6. Running multi-objective optimization on DTLZ2...")
0139 n_iterations = 5
0140
0141 for iteration in range(n_iterations):
0142 print(f"\n Iteration {iteration + 1}/{n_iterations}:")
0143
0144
0145 candidates = optimizer.suggest_candidates(n_candidates=ax_config.batch_size)
0146 print(f" - Suggested {len(candidates)} candidates")
0147
0148
0149 trial_start_idx = len(optimizer.experiment.trials) - len(candidates)
0150
0151
0152 for idx, candidate in enumerate(candidates):
0153
0154 objectives = dtlz2(candidate, n_objectives=len(objective_names))
0155
0156
0157 trial_idx = trial_start_idx + idx
0158 optimizer.update_with_results(
0159 trial_index=trial_idx,
0160 parameters=candidate,
0161 metrics=objectives
0162 )
0163
0164
0165 x_vals = [f"{candidate[f'x{i+1}']:.3f}" for i in range(3)]
0166 obj_vals = [f"{objectives[obj]:.4f}" for obj in objective_names]
0167 print(f" Trial {trial_idx}: x=[{', '.join(x_vals)}, ...] → {dict(zip(objective_names, obj_vals))}")
0168
0169
0170 print("\n7. Retrieving Pareto front...")
0171 pareto_front = optimizer.get_pareto_front()
0172 print(f" Pareto front size: {len(pareto_front)}")
0173
0174 if pareto_front:
0175 print("\n Pareto-optimal solutions:")
0176 for i, trial in enumerate(pareto_front[:5]):
0177 obj_vals = [f"{trial.metrics[obj]:.4f}" for obj in objective_names]
0178 print(f" Solution {i+1}: {dict(zip(objective_names, obj_vals))}")
0179
0180 if len(pareto_front) > 5:
0181 print(f" ... and {len(pareto_front) - 5} more solutions")
0182
0183
0184 print("\n8. Best trial (from Pareto front):")
0185 best_trial = optimizer.get_best_trial()
0186 if best_trial:
0187 print(f" Objectives: {best_trial.metrics}")
0188 print(f" (For DTLZ2, optimal Pareto front is on unit sphere)")
0189
0190
0191 print(f"\n9. Total trials evaluated: {len(optimizer.get_trials())}")
0192
0193
0194 print("\n10. Testing state serialization...")
0195 state = optimizer.serialize_state()
0196 print(f" Serialized state has {len(state['trials'])} trials")
0197
0198
0199 optimizer2 = AxOptimizer(
0200 search_space=search_space,
0201 config=ax_config,
0202 objective_names=objective_names,
0203 seed=ax_config.seed
0204 )
0205 optimizer2.load_state(state)
0206 print(f" Loaded state: {len(optimizer2.get_trials())} trials restored")
0207
0208 pareto_front2 = optimizer2.get_pareto_front()
0209 print(f" Pareto front after reload: {len(pareto_front2)} solutions")
0210
0211 print("\n" + "=" * 70)
0212 print("DTLZ2 multi-objective optimization completed successfully!")
0213 print("=" * 70)
0214
0215
0216 if __name__ == "__main__":
0217 main()