Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-12 08:24:54

0001 #!/usr/bin/env python
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     # Convert dict to array
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     # g function (auxiliary function)
0048     g = np.sum((x[n_objectives-1:] - 0.5) ** 2)
0049     
0050     # Compute objectives
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     # 1. Load configuration from YAML file
0069     print("\n1. Loading configuration from YAML file...")
0070     # Note: You can also use JSON format - just change the extension:
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     # For YAML format:
0075     # config_path = Path(__file__).parent / "ax_dtlz2_config.yml"
0076     # with open(config_path, 'r') as f:
0077     #     config_data = yaml.safe_load(f)
0078     
0079     print(f"   Config file: {config_path.name}")
0080     print(f"   Problem: {config_data['name']}")
0081     
0082     # 2. Parse configuration using OptimizerConfiguration
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     # 3. Create AxOptimizerConfig from parsed parameters
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     # 4. Define search space from configuration parameters
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     # 5. Create AxOptimizer instance
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     # 6. Run multi-objective optimization loop
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         # Suggest candidates
0145         candidates = optimizer.suggest_candidates(n_candidates=ax_config.batch_size)
0146         print(f"   - Suggested {len(candidates)} candidates")
0147         
0148         # Get current trial count before adding results
0149         trial_start_idx = len(optimizer.experiment.trials) - len(candidates)
0150         
0151         # Evaluate candidates using DTLZ2
0152         for idx, candidate in enumerate(candidates):
0153             # Evaluate DTLZ2
0154             objectives = dtlz2(candidate, n_objectives=len(objective_names))
0155             
0156             # Update optimizer with results
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             # Display results
0165             x_vals = [f"{candidate[f'x{i+1}']:.3f}" for i in range(3)]  # Show first 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     # 7. Get Pareto front
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]):  # Show first 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     # 8. Get best trial (representative from Pareto front)
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     # 9. Get all trials
0191     print(f"\n9. Total trials evaluated: {len(optimizer.get_trials())}")
0192     
0193     # 10. Serialize and deserialize state
0194     print("\n10. Testing state serialization...")
0195     state = optimizer.serialize_state()
0196     print(f"    Serialized state has {len(state['trials'])} trials")
0197     
0198     # Create new optimizer and load state
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()