# ATOMIZER PROTOCOL ## The Single Source of Truth for Atomizer Operations **Version**: 1.0 **Date**: 2025-11-19 **Status**: ACTIVE - MUST BE FOLLOWED AT ALL TIMES --- ## CORE PRINCIPLE **Atomizer is INTELLIGENT and AUTONOMOUS. It discovers, analyzes, and configures everything automatically.** Human users should provide: 1. A CAD model (.prt) 2. A simulation setup (.sim) 3. An optimization goal (in natural language) Atomizer handles the rest. --- ## PROTOCOL: Study Creation Workflow ### Phase 1: Intelligent Benchmarking (MANDATORY) **Purpose**: Discover EVERYTHING about the simulation before running optimization. **Steps**: 1. **Extract ALL Expressions** - Use `NXParameterUpdater.get_all_expressions()` - Catalog all design variables - Identify bounds and units 2. **Solve ALL Solutions in .sim File** - Use `IntelligentSetup._solve_all_solutions()` - This runs NXOpen journal that: - Updates FEM from master .prt (CRITICAL for modal analysis) - Calls `SolveAllSolutions()` - Saves simulation to write output files - Returns: Number solved, failed, skipped, solution names 3. **Analyze ALL Result Files** - Scan for all .op2 files in model directory - Use pyNastran to read each .op2 - Catalog available results: - Eigenvalues (modal frequencies) - Displacements - Stresses (CQUAD4, CTRIA3, etc.) - Forces - Map each result type to its source solution 4. **Match Objectives to Available Results** - Parse user's optimization goal - Match requested objectives to discovered results - Example mappings: - "frequency" → eigenvalues → Solution_Normal_Modes - "displacement" → displacements → Solution_1 - "stress" → cquad4_stress → Solution_1 - Return recommended solution for optimization **Output**: Benchmark data structure ```json { "success": true, "expressions": {"param1": {"value": 10.0, "units": "mm"}, ...}, "solutions": {"num_solved": 2, "solution_names": ["Solution[Solution 1]", "Solution[Solution_Normal_Modes]"]}, "available_results": { "eigenvalues": {"file": "model_sim1-solution_normal_modes.op2", "count": 10, "solution": "Solution_Normal_Modes"}, "displacements": {"file": "model_sim1-solution_1.op2", "count": 613, "solution": "Solution_1"} }, "objective_mapping": { "objectives": { "frequency_error": { "solution": "Solution_Normal_Modes", "result_type": "eigenvalues", "extractor": "extract_first_frequency", "op2_file": Path("..."), "match_confidence": "HIGH" } }, "primary_solution": "Solution_Normal_Modes" } } ``` ### Phase 2: Study Structure Creation 1. Create directory structure: ``` studies/{study_name}/ ├── 1_setup/ │ ├── model/ # Model files (.prt, .sim, .fem, .fem_i) │ └── workflow_config.json ├── 2_results/ # Optimization database and history └── 3_reports/ # Human-readable markdown reports with graphs ``` 2. Copy model files to `1_setup/model/` 3. Save workflow configuration to `1_setup/workflow_config.json` ### Phase 3: Optimization Runner Generation **Purpose**: Generate a runner that is PERFECTLY configured based on benchmarking. **Critical Rules**: 1. **Solution Selection** - Use `benchmark_results['objective_mapping']['primary_solution']` - This is the solution that contains the required results - Generate code: `solver.run_simulation(sim_file, solution_name="{primary_solution}")` 2. **Extractor Generation** - Use extractors matched to available results - For eigenvalues: `extract_first_frequency` - For displacements: `extract_max_displacement` - For stresses: `extract_max_stress` 3. **Objective Function** - For target-matching objectives (e.g., "tune to 115 Hz"): ```python if 'target_frequency' in obj_config.get('extraction', {}).get('params', {}): target = obj_config['extraction']['params']['target_frequency'] objective_value = abs(results[result_name] - target) ``` - For minimization: `objective_value = results[result_name]` - For maximization: `objective_value = -results[result_name]` 4. **Incremental History Tracking** - Write JSON after each trial - File: `2_results/optimization_history_incremental.json` - Format: ```json [ { "trial_number": 0, "design_variables": {"param1": 10.5, ...}, "results": {"first_frequency": 115.2}, "objective": 0.2 }, ... ] ``` 5. **Report Generation** - Use `generate_markdown_report()` NOT plain text - Extract target values from workflow - Save to `3_reports/OPTIMIZATION_REPORT.md` - Include convergence plots, design space plots, sensitivity plots ### Phase 4: Configuration Report Generate `1_setup/CONFIGURATION_REPORT.md` documenting: - All discovered expressions - All solutions found and solved - Objective matching results - Recommended configuration - Any warnings or issues --- ## CRITICAL PROTOCOLS ### Protocol 1: NEVER Guess Solution Names **WRONG**: ```python result = solver.run_simulation(sim_file, solution_name="normal_modes") # WRONG - guessing! ``` **RIGHT**: ```python # Use benchmark data to get actual solution name recommended_solution = benchmark_results['objective_mapping']['primary_solution'] result = solver.run_simulation(sim_file, solution_name=recommended_solution) ``` **WHY**: Solution names in NX are user-defined and unpredictable. They might be "Solution 1", "SOLUTION_NORMAL_MODES", "Modal Analysis", etc. ALWAYS discover them via benchmarking. ### Protocol 2: ALWAYS Update FEM Before Solving **Why**: Modal analysis (eigenvalues) requires the FEM to match the current geometry. If geometry changed but FEM wasn't updated, eigenvalues will be wrong. **How**: The `_solve_all_solutions()` journal includes: ```python femPart.UpdateFemodel() # Updates FEM from master CAD ``` This is done automatically during benchmarking. ### Protocol 3: Target-Matching Objectives **User says**: "Tune frequency to exactly 115 Hz" **Workflow**: ```json { "objectives": [{ "name": "frequency_error", "goal": "minimize", "extraction": { "action": "extract_first_natural_frequency", "params": {"mode_number": 1, "target_frequency": 115.0} } }] } ``` **Generated objective function**: ```python if 'target_frequency' in obj_config.get('extraction', {}).get('params', {}): target = obj_config['extraction']['params']['target_frequency'] objective_value = abs(results[result_name] - target) print(f" Frequency: {results[result_name]:.4f} Hz, Target: {target} Hz, Error: {objective_value:.4f} Hz") ``` **WHY**: Optuna must minimize ERROR FROM TARGET, not minimize the raw frequency value. ### Protocol 4: Optimization Configuration (CRITICAL!) **MANDATORY**: Every study MUST have `1_setup/optimization_config.json`. **Purpose**: Full control over Optuna sampler, pruner, and optimization strategy. **Configuration File**: `{study_dir}/1_setup/optimization_config.json` ```json { "study_name": "my_optimization", "direction": "minimize", "sampler": { "type": "TPESampler", "params": { "n_startup_trials": 5, "n_ei_candidates": 24, "multivariate": true, "warn_independent_sampling": true } }, "pruner": { "type": "MedianPruner", "params": { "n_startup_trials": 5, "n_warmup_steps": 0 } }, "trials": { "n_trials": 50, "timeout": null, "catch": [] }, "optimization_notes": "TPE sampler with multivariate enabled for correlated parameters. n_startup_trials=5 means first 5 trials are random exploration, then TPE exploitation begins." } ``` **Sampler Configuration**: 1. **TPESampler** (RECOMMENDED - Tree-structured Parzen Estimator) - `n_startup_trials`: Number of random trials before TPE kicks in (default: 10) - `n_ei_candidates`: Number of candidate samples (default: 24) - `multivariate`: Use multivariate TPE for correlated parameters (default: false, **SHOULD BE TRUE**) - `warn_independent_sampling`: Warn when parameters sampled independently 2. **RandomSampler** (for baseline comparison only) ```json { "type": "RandomSampler", "params": {"seed": 42} } ``` 3. **CmaEsSampler** (for continuous optimization) ```json { "type": "CmaEsSampler", "params": { "n_startup_trials": 5, "restart_strategy": "ipop" } } ``` **Runner Implementation**: ```python # Load optimization configuration opt_config_file = Path(__file__).parent / "1_setup/optimization_config.json" with open(opt_config_file) as f: opt_config = json.load(f) # Create sampler from config sampler_type = opt_config['sampler']['type'] sampler_params = opt_config['sampler']['params'] if sampler_type == "TPESampler": sampler = optuna.samplers.TPESampler(**sampler_params) elif sampler_type == "CmaEsSampler": sampler = optuna.samplers.CmaEsSampler(**sampler_params) else: sampler = None # Use default # Create study with configured sampler study = optuna.create_study( study_name=opt_config['study_name'], storage=storage, load_if_exists=True, direction=opt_config['direction'], sampler=sampler # CRITICAL - do not omit! ) ``` **WHY THIS IS CRITICAL**: Without explicit sampler configuration, Optuna uses **random sampling**, which does NOT learn from previous trials! This is why your optimization wasn't converging. TPE with multivariate=True is essential for problems with correlated parameters (like diameter and thickness affecting frequency together). **Common Mistakes**: - ❌ Omitting `sampler` parameter in `create_study()` → Uses random search - ❌ Setting `multivariate=False` for correlated parameters → Poor convergence - ❌ Too high `n_startup_trials` → Wastes trials on random search - ❌ No configuration file → No reproducibility, no tuning ### Protocol 5: Folder Structure **ALWAYS** use this structure: - `1_setup/` - Model files, workflow_config.json, **optimization_config.json** - `2_results/` - Optimization database and incremental history - `3_reports/` - Markdown reports with graphs **NEVER** use: - `2_substudies/` ❌ - `results/` without parent folder ❌ - Reports in `2_results/` ❌ - Missing `optimization_config.json` ❌ ### Protocol 6: Intelligent Early Stopping **Purpose**: Stop optimization automatically when target is achieved with confidence, instead of wasting trials. **Implementation**: Use Optuna callbacks to monitor convergence. ```python class TargetAchievedCallback: """Stop when target is achieved with confidence.""" def __init__(self, target_value, tolerance, min_trials=10): self.target_value = target_value self.tolerance = tolerance self.min_trials = min_trials self.consecutive_successes = 0 self.required_consecutive = 3 # Need 3 consecutive successes to confirm def __call__(self, study, trial): # Need minimum trials for surrogate to learn if len(study.trials) < self.min_trials: return # Check if current trial achieved target if trial.value <= self.tolerance: self.consecutive_successes += 1 print(f" [STOPPING] Target achieved! ({self.consecutive_successes}/{self.required_consecutive} confirmations)") if self.consecutive_successes >= self.required_consecutive: print(f" [STOPPING] Confidence achieved - stopping optimization") study.stop() else: self.consecutive_successes = 0 # Usage in runner callback = TargetAchievedCallback(target_value=115.0, tolerance=0.1, min_trials=10) study.optimize(objective, n_trials=max_trials, callbacks=[callback]) ``` **WHY THIS IS CRITICAL**: - Bayesian optimization with TPE learns from trials - need minimum 10 trials before stopping - Require 3 consecutive successes to ensure it's not a lucky guess - This is **intelligent** stopping based on surrogate model confidence - Avoids wasting computational resources once target is reliably achieved **Parameters to Tune**: - `min_trials`: Minimum trials before considering stopping (default: 10 for TPE) - `required_consecutive`: Number of consecutive successes needed (default: 3) - `tolerance`: How close to target counts as "achieved" ### Protocol 7: Client-Friendly Reports **Purpose**: Reports must show ACTUAL METRICS first (what clients care about), not just technical objective values. **Report Structure** (in order of importance): 1. **Achieved Performance** (FIRST - what the client cares about) ```markdown ### Achieved Performance - **First Frequency**: 115.4780 Hz - Target: 115.0000 Hz - Error: 0.4780 Hz (0.42%) ``` 2. **Design Parameters** (SECOND - how to build it) ```markdown ### Design Parameters - **Inner Diameter**: 94.07 mm - **Plate Thickness**: 6.14 mm ``` 3. **Technical Details** (LAST - for engineers, in collapsible section) ```markdown
Technical Details (Objective Function) - **Objective Value (Error)**: 0.478014 Hz
``` **Graph Placement**: Graphs MUST be saved to `3_reports/` folder (same as markdown file) ```python def generate_markdown_report(history_file: Path, target_value: Optional[float] = None, tolerance: float = 0.1, reports_dir: Optional[Path] = None) -> str: # Graphs should be saved to 3_reports/ folder (same as markdown file) study_dir = history_file.parent.parent if reports_dir is None: reports_dir = study_dir / "3_reports" reports_dir.mkdir(parents=True, exist_ok=True) # Generate plots in reports folder convergence_plot = create_convergence_plot(history, target_value, reports_dir) design_space_plot = create_design_space_plot(history, reports_dir) sensitivity_plot = create_parameter_sensitivity_plot(history, reports_dir) ``` **Trial Tables**: Show ACTUAL RESULTS (frequency), not just objective ```markdown | Rank | Trial | First Frequency | Inner Diameter | Plate Thickness | |------|-------|-----------------|----------------|-----------------| | 1 | #45 | 115.48 | 94.07 | 6.14 | | 2 | #54 | 114.24 | 102.22 | 5.85 | | 3 | #31 | 113.99 | 85.78 | 6.38 | ``` **WHY THIS IS CRITICAL**: - Clients don't understand "objective = 0.478" - they need "Frequency = 115.48 Hz" - Showing target comparison immediately tells them if goal was achieved - Percentage error gives intuitive sense of accuracy - Design parameters tell them how to manufacture the part **NEVER**: - Show only objective values without explaining what they mean - Hide actual performance metrics in technical details - Put graphs in wrong folder (must be in 3_reports/ with markdown) ### Protocol 8: Adaptive Surrogate-Based Optimization **Purpose**: Intelligently transition from exploration to exploitation based on surrogate model confidence, not arbitrary trial counts. **State-of-the-Art Confidence Metrics**: 1. **Convergence Score** (40% weight) - Measures consistency of recent improvements - High score = surrogate reliably finding better regions 2. **Exploration Coverage** (30% weight) - How well parameter space has been explored - Calculated as spread relative to parameter bounds 3. **Prediction Stability** (30% weight) - Are recent trials clustered in promising regions? - High stability = surrogate has learned the landscape **Overall Confidence Formula**: ``` confidence = 0.4 * convergence + 0.3 * coverage + 0.3 * stability ``` **Phase Transition**: When confidence reaches 65%, automatically transition from exploration to exploitation. **Implementation**: ```python from optimization_engine.adaptive_surrogate import AdaptiveExploitationCallback # In optimization_config.json { "adaptive_strategy": { "enabled": true, "min_confidence_for_exploitation": 0.65, "min_trials_for_confidence": 15, "target_confidence_metrics": { "convergence_weight": 0.4, "coverage_weight": 0.3, "stability_weight": 0.3 } } } # In run_optimization.py callback = AdaptiveExploitationCallback( target_value=target_value, tolerance=tolerance, min_confidence_for_exploitation=0.65, min_trials=15, verbose=True ) study.optimize(objective, n_trials=100, callbacks=[callback]) ``` **What You'll See During Optimization**: ``` [CONFIDENCE REPORT - Trial #15] Phase: EXPLORATION Overall Confidence: 42.3% - Convergence: 38.5% - Coverage: 52.1% - Stability: 35.8% MEDIUM CONFIDENCE (42.3%) - Continue exploration with some exploitation [CONFIDENCE REPORT - Trial #25] Phase: EXPLORATION Overall Confidence: 68.7% - Convergence: 71.2% - Coverage: 67.5% - Stability: 66.4% HIGH CONFIDENCE (68.7%) - Transitioning to exploitation phase ============================================================ PHASE TRANSITION: EXPLORATION → EXPLOITATION Surrogate confidence: 68.7% Now focusing on refining best regions ============================================================ ``` **WHY THIS IS CRITICAL**: - **Not arbitrary**: Transitions based on actual surrogate learning, not fixed trial counts - **Adaptive**: Different problems may need different exploration durations - **Efficient**: Stops wasting trials on exploration once landscape is understood - **Rigorous**: Uses statistical metrics (coefficient of variation, fANOVA) not guesswork - **Configurable**: Confidence threshold and weights can be tuned per study **Parameters**: - `min_confidence_for_exploitation`: Default 0.65 (65%) - `min_trials_for_confidence`: Minimum 15 trials before assessing confidence - Confidence weights: Convergence (0.4), Coverage (0.3), Stability (0.3) ### Protocol 9: Professional Optuna Visualizations **Purpose**: Reports MUST use Optuna's built-in visualization library for professional-quality analysis plots. **Required Optuna Plots**: 1. **Parallel Coordinate Plot** (`plot_parallel_coordinate`) - Shows parameter interactions and relationships - Each line = one trial, colored by objective value - Identifies promising parameter combinations 2. **Optimization History** (`plot_optimization_history`) - Professional convergence visualization - Better than basic matplotlib plots 3. **Parameter Importance** (`plot_param_importances`) - Quantifies which parameters matter most - Uses fANOVA or other importance metrics 4. **Slice Plot** (`plot_slice`) - Individual parameter effects - Shows objective vs each parameter 5. **Contour Plot** (`plot_contour`) - 2D only - Parameter interaction heatmap - Reveals coupled effects **Implementation**: ```python from optuna.visualization import ( plot_optimization_history, plot_parallel_coordinate, plot_param_importances, plot_slice, plot_contour ) def create_optuna_plots(study: optuna.Study, output_dir: Path) -> Dict[str, str]: """Create professional Optuna visualization plots.""" plots = {} # Parallel Coordinate fig = plot_parallel_coordinate(study) fig.write_image(str(output_dir / 'optuna_parallel_coordinate.png'), width=1200, height=600) # Optimization History fig = plot_optimization_history(study) fig.write_image(str(output_dir / 'optuna_optimization_history.png'), width=1000, height=600) # Parameter Importance fig = plot_param_importances(study) fig.write_image(str(output_dir / 'optuna_param_importances.png'), width=800, height=500) # Slice Plot fig = plot_slice(study) fig.write_image(str(output_dir / 'optuna_slice.png'), width=1000, height=600) # Contour (2D only) if len(study.best_params) == 2: fig = plot_contour(study) fig.write_image(str(output_dir / 'optuna_contour.png'), width=800, height=800) return plots # Pass study object to report generator report = generate_markdown_report( history_file, target_value=target_value, tolerance=tolerance, reports_dir=reports_dir, study=study # CRITICAL - pass study for Optuna plots ) ``` **Report Section**: ```markdown ## Advanced Optimization Analysis (Optuna) The following plots leverage Optuna's professional visualization library to provide deeper insights into the optimization process. ### Parallel Coordinate Plot ![Parallel Coordinate](optuna_parallel_coordinate.png) This interactive plot shows how different parameter combinations lead to different objective values... ### Parameter Importance Analysis ![Parameter Importance](optuna_param_importances.png) This analysis quantifies which design variables have the most impact... ``` **WHY THIS IS CRITICAL**: - **Professional Quality**: Optuna plots are research-grade, publication-ready - **Deeper Insights**: Parallel coordinates show interactions basic plots miss - **Parameter Importance**: Tells you which variables actually matter - **Industry Standard**: Optuna is state-of-the-art, used by top research labs - **Client Expectations**: "WAY better report than that" requires professional visualizations **NEVER**: - Use only matplotlib when Optuna visualizations are available - Forget to pass `study` object to report generator - Skip parameter importance analysis ### Protocol 6: UTF-8 Encoding **ALWAYS** use `encoding='utf-8'` when writing files: ```python with open(file_path, 'w', encoding='utf-8') as f: f.write(content) ``` **WHY**: Checkmarks (✓), mathematical symbols, and international characters require UTF-8. --- ## FILE STRUCTURE REFERENCE ### Workflow Configuration (`1_setup/workflow_config.json`) ```json { "study_name": "my_optimization", "optimization_request": "Tune the first natural frequency mode to exactly 115 Hz", "design_variables": [ {"parameter": "thickness", "bounds": [2, 10]}, {"parameter": "diameter", "bounds": [50, 150]} ], "objectives": [{ "name": "frequency_error", "goal": "minimize", "extraction": { "action": "extract_first_natural_frequency", "params": {"mode_number": 1, "target_frequency": 115.0} } }], "constraints": [{ "name": "frequency_tolerance", "type": "less_than", "threshold": 0.1 }] } ``` ### Generated Runner Template Structure ```python """ Auto-generated optimization runner Created: {timestamp} Intelligently configured from benchmarking """ import sys, json, optuna from pathlib import Path from optimization_engine.nx_updater import NXParameterUpdater from optimization_engine.nx_solver import NXSolver def extract_results(op2_file, workflow): """Extract results matched to workflow objectives""" # Auto-generated based on benchmark discoveries ... def main(): # Load workflow config_file = Path(__file__).parent / "1_setup/workflow_config.json" workflow = json.load(open(config_file)) # Setup paths output_dir = Path(__file__).parent / "2_results" reports_dir = Path(__file__).parent / "3_reports" # Initialize updater = NXParameterUpdater(prt_file) solver = NXSolver() # Create Optuna study study = optuna.create_study(...) def objective(trial): # Sample design variables params = {var['parameter']: trial.suggest_float(var['parameter'], *var['bounds']) for var in workflow['design_variables']} # Update model updater.update_expressions(params) # Run simulation (with discovered solution name!) result = solver.run_simulation(sim_file, solution_name="{DISCOVERED_SOLUTION}") # Extract results results = extract_results(result['op2_file'], workflow) # Calculate objective (with target matching if applicable) obj_config = workflow['objectives'][0] if 'target_frequency' in obj_config.get('extraction', {}).get('params', {}): target = obj_config['extraction']['params']['target_frequency'] objective_value = abs(results['first_frequency'] - target) else: objective_value = results[list(results.keys())[0]] # Save incremental history with open(history_file, 'w', encoding='utf-8') as f: json.dump(history, f, indent=2) return objective_value # Run optimization study.optimize(objective, n_trials=n_trials) # Generate markdown report with graphs from optimization_engine.generate_report_markdown import generate_markdown_report report = generate_markdown_report(history_file, target_value={TARGET}, tolerance=0.1) with open(reports_dir / 'OPTIMIZATION_REPORT.md', 'w', encoding='utf-8') as f: f.write(report) if __name__ == "__main__": main() ``` --- ## TESTING PROTOCOL Before releasing any study: 1. ✅ Benchmarking completed successfully 2. ✅ All solutions discovered and solved 3. ✅ Objectives matched to results with HIGH confidence 4. ✅ Runner uses discovered solution name (not hardcoded) 5. ✅ Objective function handles target-matching correctly 6. ✅ Incremental history updates after each trial 7. ✅ Reports generate in `3_reports/` as markdown with graphs 8. ✅ Reports mention target values explicitly --- ## TROUBLESHOOTING ### Issue: "No object found with this name" when solving **Cause**: Using guessed solution name instead of discovered name. **Fix**: Use benchmark data to get actual solution name. ### Issue: Optimization not learning / converging **Cause**: Objective function returning wrong value (e.g., raw frequency instead of error from target). **Fix**: Check if objective has target value. If yes, minimize `abs(result - target)`. ### Issue: No eigenvalues found in OP2 **Cause**: Either (1) solving wrong solution, or (2) FEM not updated before solving. **Fix**: 1. Check benchmark discovered correct modal solution 2. Verify `_solve_all_solutions()` calls `UpdateFemodel()` ### Issue: Reports are plain text without graphs **Cause**: Using old `generate_report` instead of `generate_report_markdown`. **Fix**: Import and use `generate_markdown_report()` from `optimization_engine.generate_report_markdown`. --- ## EXTENSIBILITY ARCHITECTURE ### Core Principle: Modular Hooks System Atomizer is designed to be extensible without modifying core code. All customization happens through **hooks**, **extractors**, and **plugins**. ### Hook System **Purpose**: Allow users and future features to inject custom behavior at key points in the workflow. **Hook Points**: 1. **pre_trial_hook** - Before each optimization trial starts - Use case: Custom parameter validation, parameter transformations - Receives: trial_number, design_variables - Returns: modified design_variables or None to skip trial 2. **post_trial_hook** - After each trial completes - Use case: Custom logging, notifications, early stopping - Receives: trial_number, design_variables, results, objective - Returns: None or dict with custom metrics 3. **pre_solve_hook** - Before NX solver is called - Use case: Custom model modifications, additional setup - Receives: prt_file, sim_file, design_variables - Returns: None 4. **post_solve_hook** - After NX solver completes - Use case: Custom result extraction, post-processing - Receives: op2_file, sim_file, solve_success - Returns: dict with additional results 5. **post_study_hook** - After entire optimization study completes - Use case: Custom reports, notifications, deployment - Receives: study_dir, best_params, best_objective, history - Returns: None **Hook Implementation**: Hooks are Python functions defined in `{study_dir}/1_setup/hooks.py`: ```python # studies/my_study/1_setup/hooks.py def pre_trial_hook(trial_number, design_variables): """Validate parameters before trial.""" print(f"[HOOK] Starting trial {trial_number}") # Example: Apply custom constraints if design_variables['thickness'] < design_variables['diameter'] / 10: print("[HOOK] Skipping trial - thickness too small") return None # Skip trial return design_variables # Proceed with trial def post_trial_hook(trial_number, design_variables, results, objective): """Log custom metrics after trial.""" frequency = results.get('first_frequency', 0) # Example: Save to custom database custom_metrics = { 'frequency_ratio': frequency / 115.0, 'parameter_sum': sum(design_variables.values()) } print(f"[HOOK] Custom metrics: {custom_metrics}") return custom_metrics def post_study_hook(study_dir, best_params, best_objective, history): """Generate custom reports after optimization.""" print(f"[HOOK] Optimization complete!") print(f"[HOOK] Best objective: {best_objective}") # Example: Send email notification # send_email(f"Optimization complete: {best_objective}") # Example: Deploy best design # deploy_to_production(best_params) ``` **Hook Loading** (in generated runner): ```python # Load hooks if they exist hooks_file = Path(__file__).parent / "1_setup/hooks.py" hooks = {} if hooks_file.exists(): import importlib.util spec = importlib.util.spec_from_file_location("hooks", hooks_file) hooks_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(hooks_module) hooks['pre_trial'] = getattr(hooks_module, 'pre_trial_hook', None) hooks['post_trial'] = getattr(hooks_module, 'post_trial_hook', None) hooks['post_study'] = getattr(hooks_module, 'post_study_hook', None) # In objective function: if hooks.get('pre_trial'): modified_params = hooks['pre_trial'](trial.number, params) if modified_params is None: raise optuna.TrialPruned() # Skip this trial params = modified_params ``` ### Extractor System **Purpose**: Modular result extraction from OP2 files. **Extractor Library**: `optimization_engine/extractors/` ``` optimization_engine/ └── extractors/ ├── __init__.py ├── frequency_extractor.py ├── displacement_extractor.py ├── stress_extractor.py └── custom_extractor.py # User-defined ``` **Extractor Interface**: ```python # optimization_engine/extractors/base_extractor.py from abc import ABC, abstractmethod from pathlib import Path from typing import Dict, Any class BaseExtractor(ABC): """Base class for all extractors.""" @abstractmethod def extract(self, op2_file: Path, params: Dict[str, Any]) -> Dict[str, float]: """ Extract results from OP2 file. Args: op2_file: Path to OP2 result file params: Extraction parameters (e.g., mode_number, node_id) Returns: Dict of result name → value """ pass @property @abstractmethod def result_types(self) -> list: """List of result types this extractor can handle.""" pass ``` **Example Custom Extractor**: ```python # optimization_engine/extractors/custom_extractor.py from .base_extractor import BaseExtractor from pyNastran.op2.op2 import OP2 import numpy as np class CustomResonanceExtractor(BaseExtractor): """Extract resonance quality factor from frequency response.""" @property def result_types(self): return ['quality_factor', 'resonance_bandwidth'] def extract(self, op2_file, params): model = OP2() model.read_op2(str(op2_file)) # Custom extraction logic frequencies = model.eigenvalues[1].eigenvalues # Calculate Q-factor f0 = frequencies[0] f1 = frequencies[1] bandwidth = abs(f1 - f0) q_factor = f0 / bandwidth return { 'quality_factor': q_factor, 'resonance_bandwidth': bandwidth } ``` **Extractor Registration** (in workflow config): ```json { "objectives": [{ "name": "q_factor", "goal": "maximize", "extraction": { "action": "custom_resonance_extractor", "params": {} } }] } ``` ### Plugin System **Purpose**: Add entirely new functionality without modifying core. **Plugin Structure**: ``` plugins/ ├── ai_design_suggester/ │ ├── __init__.py │ ├── plugin.py │ └── README.md ├── sensitivity_analyzer/ │ ├── __init__.py │ ├── plugin.py │ └── README.md └── cad_generator/ ├── __init__.py ├── plugin.py └── README.md ``` **Plugin Interface**: ```python # plugins/base_plugin.py class BasePlugin(ABC): """Base class for Atomizer plugins.""" @property @abstractmethod def name(self) -> str: """Plugin name.""" pass @abstractmethod def initialize(self, config: Dict): """Initialize plugin with configuration.""" pass @abstractmethod def execute(self, context: Dict) -> Dict: """ Execute plugin logic. Args: context: Current optimization context (study_dir, history, etc.) Returns: Dict with plugin results """ pass ``` **Example Plugin**: ```python # plugins/ai_design_suggester/plugin.py from plugins.base_plugin import BasePlugin class AIDesignSuggester(BasePlugin): """Use AI to suggest promising design regions.""" @property def name(self): return "ai_design_suggester" def initialize(self, config): self.model_type = config.get('model', 'random_forest') def execute(self, context): """Analyze history and suggest next designs.""" history = context['history'] # Train surrogate model X = [list(h['design_variables'].values()) for h in history] y = [h['objective'] for h in history] # Suggest next promising region suggested_params = self._suggest_next_design(X, y) return { 'suggested_design': suggested_params, 'expected_improvement': 0.15 } ``` ### API-Ready Architecture **Purpose**: Enable future web API / REST interface without refactoring. **API Endpoints** (future): ```python # api/server.py (future implementation) from fastapi import FastAPI from optimization_engine.hybrid_study_creator import HybridStudyCreator app = FastAPI() @app.post("/api/v1/studies/create") async def create_study(request: StudyRequest): """Create optimization study from API request.""" creator = HybridStudyCreator() study_dir = creator.create_from_workflow( workflow_json=request.workflow, model_files=request.model_files, study_name=request.study_name ) return {"study_id": study_dir.name, "status": "created"} @app.post("/api/v1/studies/{study_id}/run") async def run_optimization(study_id: str, n_trials: int): """Run optimization via API.""" # Import and run the generated runner # Stream progress back to client pass @app.get("/api/v1/studies/{study_id}/status") async def get_status(study_id: str): """Get optimization status and current best.""" # Read incremental history JSON # Return current progress pass @app.get("/api/v1/studies/{study_id}/report") async def get_report(study_id: str): """Get markdown report.""" # Read OPTIMIZATION_REPORT.md # Return as markdown or HTML pass ``` **Design Principles for API Compatibility**: 1. **Stateless Operations**: All operations work with paths and JSON, no global state 2. **Incremental Results**: History JSON updated after each trial (enables streaming) 3. **Standard Formats**: All inputs/outputs are JSON, Markdown, or standard file formats 4. **Self-Contained Studies**: Each study folder is independent and portable ### Configuration System **Purpose**: Centralized configuration with environment overrides. **Configuration Hierarchy**: 1. Default config (in code) 2. Global config: `~/.atomizer/config.json` 3. Project config: `./atomizer.config.json` 4. Study config: `{study_dir}/1_setup/config.json` 5. Environment variables: `ATOMIZER_*` **Example Configuration**: ```json { "nx": { "executable": "C:/Program Files/Siemens/NX2412/ugraf.exe", "timeout_seconds": 600, "journal_runner": "C:/Program Files/Siemens/NX2412/run_journal.exe" }, "optimization": { "default_n_trials": 50, "sampler": "TPE", "pruner": "MedianPruner" }, "reports": { "auto_generate": true, "include_sensitivity": true, "plot_dpi": 150 }, "hooks": { "enabled": true, "timeout_seconds": 30 }, "plugins": { "enabled": ["ai_design_suggester", "sensitivity_analyzer"], "disabled": [] } } ``` ### Future Feature Foundations **Ready for Implementation**: 1. **Multi-Objective Optimization** - Hook: `extract_multiple_objectives()` - Extractor: Return dict with multiple values - Report: Pareto front visualization 2. **Constraint Handling** - Hook: `evaluate_constraints()` - Optuna: Add constraint callbacks - Report: Constraint violation history 3. **Surrogate Models** - Plugin: `surrogate_model_trainer` - Hook: `post_trial_hook` trains model - Use for cheap pre-screening 4. **Sensitivity Analysis** - Plugin: `sensitivity_analyzer` - Hook: `post_study_hook` runs analysis - Report: Sensitivity charts 5. **Design Space Exploration** - Plugin: `design_space_explorer` - Different sampling strategies - Adaptive design of experiments 6. **Parallel Evaluation** - Multiple NX instances - Distributed solver via Ray/Dask - Asynchronous trial management 7. **CAD Generation** - Plugin: `cad_generator` - Hook: `pre_solve_hook` generates geometry - Parametric model creation 8. **Live Dashboards** - Web UI showing real-time progress - Reads incremental history JSON - Interactive design space plots --- ## PROTOCOL 8: Adaptive Surrogate-Based Optimization (STUDY-AWARE) ### Purpose Intelligent Bayesian optimization with confidence-based exploration→exploitation transitions. **CRITICAL**: All adaptive components MUST be study-aware, tracking state across multiple optimization sessions using the Optuna study database, not just the current session. ### Implementation **Module**: `optimization_engine/adaptive_surrogate.py` **Key Classes**: 1. `SurrogateConfidenceMetrics`: Study-aware confidence calculation 2. `AdaptiveExploitationCallback`: Optuna callback with phase transition tracking ### Confidence Metrics (Study-Aware) Uses `study.trials` directly, NOT session-based history: ```python all_trials = [t for t in study.trials if t.state == COMPLETE] ``` **Metrics**: 1. **Convergence Score** (40% weight) - Recent improvement rate and consistency - Based on last 10 completed trials 2. **Exploration Coverage** (30% weight) - Parameter space coverage relative to bounds - Calculated as spread/range for each parameter 3. **Prediction Stability** (30% weight) - How stable recent best values are - Indicates surrogate model reliability **Overall Confidence** = weighted combination **Ready for Exploitation** = confidence ≥ 65% AND coverage ≥ 50% ### Phase Transitions **Exploration Phase**: - Initial trials (usually < 15) - Focus: broad parameter space coverage - TPE sampler with n_startup_trials random sampling **Exploitation Phase**: - After confidence threshold reached - Focus: refining best regions - TPE sampler with n_ei_candidates=24 for intensive Expected Improvement **Transition Triggering**: - Automatic when confidence ≥ 65% - Logged to terminal with banner - Saved to `phase_transitions.json` ### Tracking Files (Study-Aware) **Location**: `studies/{study_name}/2_results/` **Files**: 1. `phase_transitions.json`: Phase transition events ```json [{ "trial_number": 45, "from_phase": "exploration", "to_phase": "exploitation", "confidence_metrics": { "overall_confidence": 0.72, "convergence_score": 0.68, "exploration_coverage": 0.75, "prediction_stability": 0.81 } }] ``` 2. `confidence_history.json`: Confidence snapshots every 5 trials ```json [{ "trial_number": 15, "phase": "exploration", "confidence_metrics": {...}, "total_trials": 15 }] ``` ### Configuration **File**: `1_setup/optimization_config.json` ```json { "adaptive_strategy": { "enabled": true, "min_confidence_for_exploitation": 0.65, "min_trials_for_confidence": 15, "target_confidence_metrics": { "convergence_weight": 0.4, "coverage_weight": 0.3, "stability_weight": 0.3 } }, "sampler": { "type": "TPESampler", "params": { "n_startup_trials": 10, "n_ei_candidates": 24, "multivariate": true } } } ``` ### Report Integration **Markdown Report Sections**: 1. **Adaptive Optimization Strategy** section - Phase transition details - Confidence at transition - Metrics breakdown 2. **Confidence Progression Plot** - Overall confidence over trials - Component metrics (convergence, coverage, stability) - Red vertical line marking exploitation transition - Horizontal threshold line at 65% **Generator**: `optimization_engine/generate_report_markdown.py` - Reads `phase_transitions.json` - Reads `confidence_history.json` - Creates `confidence_progression.png` ### Critical Bug Fix (Nov 19, 2025) **Problem**: Original implementation tracked trials in session-based `self.history`, resetting on each run. **Solution**: Use `study.trials` directly for ALL calculations: - Confidence metrics - Phase transition decisions - Trial counting This ensures optimization behavior is consistent across interrupted/resumed sessions. --- ## PROTOCOL 9: Professional Optimization Visualizations (Optuna Integration) ### Purpose Leverage Optuna's professional visualization library for publication-quality optimization analysis. ### Implementation **Module**: `optimization_engine/generate_report_markdown.py` **Function**: `create_optuna_plots(study, output_dir)` ### Generated Plots 1. **Parallel Coordinate Plot** (`optuna_parallel_coordinate.png`) - Multi-dimensional parameter visualization - Color-coded by objective value - Shows parameter interactions and promising regions 2. **Optimization History** (`optuna_optimization_history.png`) - Trial-by-trial objective progression - Best value trajectory - Professional alternative to custom convergence plot 3. **Parameter Importance** (`optuna_param_importances.png`) - fANOVA-based importance analysis - Quantifies which parameters most affect objective - Critical for understanding sensitivity 4. **Slice Plot** (`optuna_slice.png`) - Individual parameter effects - Other parameters held constant - Shows univariate relationships 5. **Contour Plot** (`optuna_contour.png`) - 2D parameter interaction heatmaps - All pairwise combinations - Reveals interaction effects ### Report Integration All plots automatically added to markdown report under: **"Advanced Optimization Analysis (Optuna)"** section Each plot includes explanatory text about: - What the plot shows - How to interpret it - What insights it provides ### Requirements - `study` object must be passed to `generate_markdown_report()` - Minimum 10 trials for meaningful visualizations - Handles errors gracefully if plots fail --- ## PROTOCOL 10: Intelligent Multi-Strategy Optimization (IMSO) ### Purpose **Make Atomizer self-tuning** - automatically discover problem characteristics and select the best optimization algorithm, adapting strategy mid-run if needed. **User Experience**: User provides optimization goal. Atomizer intelligently tests strategies, analyzes landscape, and converges efficiently WITHOUT manual algorithm tuning. ### Design Philosophy Different FEA problems need different optimization strategies: - **Smooth unimodal** (e.g., beam bending) → CMA-ES for fast local convergence - **Smooth multimodal** (e.g., resonance tuning) → GP-BO → CMA-ES hybrid - **Rugged multimodal** (e.g., complex assemblies) → TPE for robust exploration - **High-dimensional** (e.g., topology optimization) → TPE scales best **Traditional approach**: User manually configures sampler (error-prone, suboptimal) **Protocol 10 approach**: Atomizer discovers problem type, recommends strategy, switches mid-run if stagnating ### Three-Phase Architecture **Stage 1: Landscape Characterization (Trials 1-15)** - Run initial exploration with random sampling - Analyze problem characteristics: - Smoothness (nearby points → similar objectives?) - Multimodality (multiple local optima?) - Parameter correlation (coupled effects?) - Noise level (simulation stability?) - Generate landscape report for transparency **Stage 2: Intelligent Strategy Selection (Trial 15+)** - Use decision tree to match landscape → strategy - Recommend sampler with confidence score - Switch to recommended strategy - Log decision reasoning **Stage 3: Adaptive Optimization with Monitoring (Ongoing)** - Monitor strategy performance - Detect stagnation (no improvement over N trials) - Re-analyze landscape if needed - Switch strategies dynamically - Track transitions for learning ### Core Components **1. Landscape Analyzer** (`optimization_engine/landscape_analyzer.py`) Computes problem characteristics from trial history: ```python from optimization_engine.landscape_analyzer import LandscapeAnalyzer analyzer = LandscapeAnalyzer(min_trials_for_analysis=10) landscape = analyzer.analyze(study) # Returns: { 'smoothness': 0.75, # 0-1, higher = smoother 'multimodal': False, # Multiple local optima? 'n_modes': 1, # Estimated local optima count 'parameter_correlation': {...}, # Correlation with objective 'noise_level': 0.12, # Evaluation noise estimate 'landscape_type': 'smooth_unimodal' # Classification } ``` **Landscape Types**: - `smooth_unimodal`: Single smooth bowl → **CMA-ES** - `smooth_multimodal`: Multiple smooth regions → **GP-BO or TPE** - `rugged_unimodal`: Single rough region → **TPE** - `rugged_multimodal`: Multiple rough regions → **TPE** - `noisy`: High noise → **Robust methods** **2. Strategy Selector** (`optimization_engine/strategy_selector.py`) Expert decision tree for strategy recommendation: ```python from optimization_engine.strategy_selector import IntelligentStrategySelector selector = IntelligentStrategySelector(verbose=True) strategy, details = selector.recommend_strategy( landscape=landscape, trials_completed=15, trials_budget=100 ) # Returns: ('cmaes', { 'confidence': 0.92, 'reasoning': 'Smooth unimodal with strong correlation - CMA-ES converges quickly', 'sampler_config': { 'type': 'CmaEsSampler', 'params': {'restart_strategy': 'ipop'} } }) ``` **Decision Tree Logic**: 1. **High noise** → TPE (robust) 2. **Smooth + correlated** → CMA-ES (fast local convergence) 3. **Smooth + low-D** → GP-BO (sample efficient) 4. **Multimodal** → TPE (handles multiple modes) 5. **High-D** → TPE (scales best) 6. **Default** → TPE (safe general-purpose) **3. Strategy Portfolio Manager** (`optimization_engine/strategy_portfolio.py`) Manages dynamic strategy switching: ```python from optimization_engine.strategy_portfolio import StrategyTransitionManager manager = StrategyTransitionManager( stagnation_window=10, min_improvement_threshold=0.001, verbose=True, tracking_dir=study_dir / "intelligent_optimizer" ) # Checks for switching conditions should_switch, reason = manager.should_switch_strategy(study, landscape) # Executes transition with logging manager.execute_strategy_switch( study, from_strategy='tpe', to_strategy='cmaes', reason='Stagnation detected', trial_number=45 ) ``` **Switching Triggers**: - **Stagnation**: <0.1% improvement over 10 trials - **Thrashing**: High variance without improvement - **Strategy exhaustion**: Algorithm reached theoretical limit **4. Intelligent Optimizer Orchestrator** (`optimization_engine/intelligent_optimizer.py`) Main entry point coordinating all components: ```python from optimization_engine.intelligent_optimizer import IntelligentOptimizer optimizer = IntelligentOptimizer( study_name="my_study", study_dir=Path("studies/my_study/2_results"), config=opt_config, verbose=True ) results = optimizer.optimize( objective_function=objective, design_variables={'thickness': (2, 10), 'diameter': (50, 150)}, n_trials=100, target_value=115.0, tolerance=0.1 ) # Returns comprehensive results with strategy performance breakdown ``` ### Configuration **File**: `1_setup/optimization_config.json` ```json { "intelligent_optimization": { "enabled": true, "characterization_trials": 15, "stagnation_window": 10, "min_improvement_threshold": 0.001, "min_analysis_trials": 10, "reanalysis_interval": 15 }, "sampler": { "type": "intelligent", "fallback": "TPESampler" } } ``` **Parameters**: - `enabled`: Enable Protocol 10 (default: true) - `characterization_trials`: Initial exploration trials (default: 15) - `stagnation_window`: Trials to check for stagnation (default: 10) - `min_improvement_threshold`: Minimum relative improvement (default: 0.001 = 0.1%) - `min_analysis_trials`: Minimum trials for landscape analysis (default: 10) - `reanalysis_interval`: Re-analyze landscape every N trials (default: 15) ### Tracking and Transparency **Location**: `studies/{study_name}/2_results/intelligent_optimizer/` **Files Generated**: 1. **`strategy_transitions.json`** - All strategy switches ```json [{ "trial_number": 45, "from_strategy": "tpe", "to_strategy": "cmaes", "reason": "Stagnation detected - <0.1% improvement in 10 trials", "best_value_at_switch": 0.485, "timestamp": "2025-11-19T14:23:15" }] ``` 2. **`strategy_performance.json`** - Performance breakdown by strategy ```json { "strategies": { "tpe": { "trials_used": 45, "best_value_achieved": 0.485, "improvement_rate": 0.032 }, "cmaes": { "trials_used": 55, "best_value_achieved": 0.185, "improvement_rate": 0.055 } } } ``` 3. **`intelligence_report.json`** - Complete decision log - Landscape analysis at each interval - Strategy recommendations with reasoning - Confidence metrics over time - All transition events ### Console Output **During Optimization**: ``` ====================================================================== STAGE 1: LANDSCAPE CHARACTERIZATION ====================================================================== Trial #10: Objective = 5.234 Trial #15: Objective = 3.456 ====================================================================== LANDSCAPE ANALYSIS REPORT ====================================================================== Total Trials Analyzed: 15 Dimensionality: 2 parameters LANDSCAPE CHARACTERISTICS: Type: SMOOTH_UNIMODAL Smoothness: 0.78 (smooth) Multimodal: NO (1 modes) Noise Level: 0.08 (low) PARAMETER CORRELATIONS: inner_diameter: +0.652 (strong positive) plate_thickness: -0.543 (strong negative) ====================================================================== ====================================================================== STAGE 2: STRATEGY SELECTION ====================================================================== ====================================================================== STRATEGY RECOMMENDATION ====================================================================== Recommended: CMAES Confidence: 92.0% Reasoning: Smooth unimodal with strong correlation - CMA-ES converges quickly ====================================================================== ====================================================================== STAGE 3: ADAPTIVE OPTIMIZATION ====================================================================== Trial #25: Objective = 1.234 Trial #45: Objective = 0.485 ====================================================================== STRATEGY TRANSITION ====================================================================== Trial #45 TPE → CMAES Reason: Stagnation detected - <0.1% improvement in 10 trials Best value at transition: 0.485 ====================================================================== Trial #55: Objective = 0.350 Trial #75: Objective = 0.185 ====================================================================== OPTIMIZATION COMPLETE ====================================================================== Protocol: Protocol 10: Intelligent Multi-Strategy Optimization Total Trials: 100 Best Value: 0.185 (Trial #98) Final Strategy: CMAES Strategy Transitions: 1 Trial #45: tpe → cmaes Best Parameters: inner_diameter: 124.486 plate_thickness: 5.072 ====================================================================== ``` ### Report Integration **Markdown Report Section** (auto-generated): ```markdown ## Intelligent Multi-Strategy Optimization (Protocol 10) This optimization used **Protocol 10**, Atomizer's intelligent self-tuning framework. ### Problem Characteristics Based on initial exploration (15 trials), the problem was classified as: - **Landscape Type**: Smooth Unimodal - **Smoothness**: 0.78 (smooth, well-behaved) - **Multimodality**: Single optimum (no competing solutions) - **Parameter Correlation**: Strong (0.65 overall) ### Strategy Selection **Recommended Strategy**: CMA-ES (Covariance Matrix Adaptation) - **Confidence**: 92% - **Reasoning**: Smooth unimodal landscape with strong parameter correlation - CMA-ES will converge quickly ### Strategy Performance | Strategy | Trials | Best Value | Improvement/Trial | |----------|--------|------------|-------------------| | TPE | 45 | 0.485 | 0.032 | | CMA-ES | 55 | 0.185 | 0.055 | **Transition Event (Trial #45)**: Switched from TPE to CMA-ES due to stagnation detection. This automatic adaptation achieved **2.6x faster** convergence rate with CMA-ES compared to TPE. ``` ### Integration with Existing Protocols **Protocol 10 + Protocol 8 (Adaptive Surrogate)**: - Landscape analyzer provides data for confidence calculation - Confidence metrics inform strategy switching decisions - Phase transitions tracked alongside strategy transitions **Protocol 10 + Protocol 9 (Optuna Visualizations)**: - Optuna plots show different strategy regions - Parameter importance analysis validates landscape classification - Slice plots confirm smoothness/multimodality assessment ### Algorithm Portfolio **Available Strategies**: 1. **TPE (Tree-structured Parzen Estimator)** - Default safe choice - Strengths: Robust, handles multimodality, scales to ~50D - Weaknesses: Slower convergence on smooth problems - Best for: General purpose, multimodal, rugged landscapes 2. **CMA-ES (Covariance Matrix Adaptation)** - Fast local optimizer - Strengths: Very fast convergence, handles parameter correlation - Weaknesses: Needs good initialization, poor for multimodal - Best for: Smooth unimodal, correlated parameters, final refinement 3. **GP-BO (Gaussian Process Bayesian Optimization)** - Sample efficient - Strengths: Excellent for expensive evaluations, good uncertainty - Weaknesses: Scales poorly >10D, expensive surrogate training - Best for: Smooth landscapes, low-dimensional, expensive simulations 4. **Random** - Baseline exploration - Strengths: No assumptions, good for initial characterization - Weaknesses: No learning, very slow convergence - Best for: Initial exploration only 5. **Hybrid GP→CMA-ES** (Future) - Best of both worlds - GP finds promising basin, CMA-ES refines locally - Requires transition logic (planned) ### Critical Implementation Details **Study-Aware Design**: - All components use `study.trials` not session history - Supports interrupted/resumed optimization - State persisted to JSON files **Callback Integration**: ```python # In generated runner from optimization_engine.intelligent_optimizer import create_intelligent_optimizer optimizer = create_intelligent_optimizer( study_name=study_name, study_dir=results_dir, verbose=True ) results = optimizer.optimize( objective_function=objective, design_variables=design_vars, n_trials=100 ) ``` **Fallback Behavior**: If `intelligent_optimization.enabled = false`, falls back to standard TPE optimization. ### When to Use Protocol 10 **ALWAYS use for**: - New problem types (unknown landscape) - Production optimization runs (want best performance) - Studies with >50 trial budget (enough for characterization) **Consider disabling for**: - Quick tests (<20 trials) - Known problem types with proven strategy - Debugging/development work ### Future Enhancements **Planned Features**: 1. **Transfer Learning**: Build database of landscape → strategy mappings 2. **Multi-Armed Bandit**: Thompson sampling for strategy portfolio 3. **Hybrid Strategies**: Automatic GP→CMA-ES transitions 4. **Parallel Strategy Testing**: Run multiple strategies concurrently 5. **Meta-Learning**: Learn switching thresholds from historical data --- ## VERSION HISTORY - **v1.3** (2025-11-19): Added Protocol 10 - Intelligent Multi-Strategy Optimization - Automatic landscape characterization - Intelligent strategy selection with decision tree - Dynamic strategy switching with stagnation detection - Comprehensive tracking and transparency - Four new modules: landscape_analyzer, strategy_selector, strategy_portfolio, intelligent_optimizer - Full integration with Protocols 8 and 9 - **v1.2** (2025-11-19): Overhauled adaptive optimization - Fixed critical study-aware bug in confidence tracking - Added phase transition persistence and reporting - Added confidence progression visualization - Integrated Optuna professional visualization library - Updated all protocols with study-aware architecture - **v1.1** (2025-11-19): Added extensibility architecture - Hook system specification - Extractor library architecture - Plugin system design - API-ready architecture - Configuration hierarchy - Future feature foundations - **v1.0** (2025-11-19): Initial protocol established - Intelligent benchmarking workflow - Solution discovery and matching - Target-matching objective functions - Markdown reports with graphs - UTF-8 encoding standards - Folder structure standards --- **END OF PROTOCOL** *This protocol must be followed for ALL Atomizer operations. Deviations require explicit documentation and user approval.*