File indexing completed on 2026-04-27 07:28:14
0001
0002 """
0003 AID2E Command Line Interface.
0004
0005 Provides commands for loading and running optimization configurations.
0006 """
0007
0008 import sys
0009 from pathlib import Path
0010 from typing import Optional
0011
0012 import click
0013 import importlib.metadata
0014
0015 from aid2e import __MAIN_VERSION__
0016 from aid2e.utilities.configurations import FullConfig, load_config
0017
0018
0019 @click.group()
0020 @click.version_option(version=__MAIN_VERSION__, prog_name="aid2e")
0021 def cli():
0022 """
0023 AID2E - AI assisted Detector Design for EIC.
0024
0025 A framework for optimization of detector designs and other complex systems.
0026 """
0027 pass
0028
0029
0030 def _load_plugin_commands(group: click.Group):
0031 """Discover and register plugin commands from entry points.
0032
0033 Uses the `aid2e.commands` entry point group. Each entry point must
0034 resolve to a Click command object.
0035 """
0036 try:
0037 eps = importlib.metadata.entry_points()
0038 candidates = eps.select(group="aid2e.commands") if hasattr(eps, "select") else eps.get("aid2e.commands", [])
0039 for ep in candidates:
0040 try:
0041 cmd = ep.load()
0042 if isinstance(cmd, click.core.Command):
0043 group.add_command(cmd)
0044 except Exception:
0045
0046 continue
0047 except Exception:
0048
0049 pass
0050
0051
0052 @cli.command(name="load")
0053 @click.argument("config_file", type=click.Path(exists=True))
0054 @click.option("--validate-only", is_flag=True, help="Only validate the configuration without running")
0055 @click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
0056 def load(config_file: str, validate_only: bool, verbose: bool):
0057 """
0058 Load and validate an optimization configuration from a YAML file.
0059
0060 CONFIG_FILE: Path to the YAML configuration file.
0061
0062 Example:
0063 aid2e load optimization.yml
0064 aid2e load optimization.yml --validate-only
0065 """
0066 try:
0067 if verbose:
0068 click.echo(f"Loading configuration from: {config_file}")
0069
0070 config = load_config(config_file)
0071
0072 click.echo(click.style("✓ Configuration loaded successfully!", fg="green"))
0073 click.echo()
0074 click.echo(click.style("Configuration Summary:", bold=True))
0075 click.echo(f" Problem: {config.problem.name}")
0076 click.echo(f" Type: {config.problem.problem_type}")
0077 click.echo(f" Output: {config.problem.output_location}")
0078 click.echo(f" Work: {config.problem.work_location}")
0079 click.echo()
0080 click.echo(f" Optimizer: {config.optimization.optimizer.name} ({config.optimization.optimizer.type})")
0081 click.echo(f" Iterations: {config.optimization.n_iterations}")
0082 click.echo(f" Initial Samples: {config.optimization.n_initial_samples}")
0083 click.echo(f" Parallel Evaluations: {config.optimization.parallel_evaluations}")
0084 click.echo()
0085
0086 param_names = config.problem.design_config.get_parameter_names()
0087 click.echo(f" Design Parameters: {len(param_names)}")
0088 if verbose:
0089 for name in param_names:
0090 param = config.problem.design_config.get_flat_parameters()[name]
0091 bounds = config.problem.design_config.get_parameter_bounds(name)
0092 click.echo(f" - {name}: {param.value} {bounds}")
0093
0094 if config.optimization.objectives:
0095 click.echo()
0096 click.echo(f" Objectives ({len(config.optimization.objectives)}):")
0097 for obj in config.optimization.objectives:
0098 click.echo(f" - {obj}")
0099
0100 if config.problem.design_config.parameter_constraints:
0101 click.echo()
0102 click.echo(f" Parameter Constraints ({len(config.problem.design_config.parameter_constraints)}):")
0103 for constraint in config.problem.design_config.parameter_constraints:
0104 click.echo(f" - {constraint.name}: {constraint.rule}")
0105
0106 if validate_only:
0107 click.echo()
0108 click.echo(click.style("✓ Validation complete. Configuration is valid.", fg="green"))
0109 return
0110
0111 click.echo()
0112 click.echo(click.style("Note: Optimization execution not yet implemented.", fg="yellow"))
0113 click.echo("The configuration has been validated and is ready to use.")
0114
0115 except FileNotFoundError as e:
0116 click.echo(click.style(f"✗ Error: {e}", fg="red"), err=True)
0117 sys.exit(1)
0118 except ValueError as e:
0119 click.echo(click.style(f"✗ Configuration Error: {e}", fg="red"), err=True)
0120 sys.exit(1)
0121 except Exception as e:
0122 click.echo(click.style(f"✗ Unexpected Error: {e}", fg="red"), err=True)
0123 if verbose:
0124 import traceback
0125 traceback.print_exc()
0126 sys.exit(1)
0127
0128
0129 @cli.command()
0130 @click.argument("config_file", type=click.Path(exists=True))
0131 def info(config_file: str):
0132 """
0133 Display detailed information about a configuration file.
0134
0135 CONFIG_FILE: Path to the YAML configuration file.
0136
0137 Example:
0138 aid2e info optimization.yml
0139 """
0140 try:
0141 config = load_config(config_file)
0142
0143 click.echo(click.style("=" * 60, bold=True))
0144 click.echo(click.style(f"Configuration: {config.problem.name}", bold=True))
0145 click.echo(click.style("=" * 60, bold=True))
0146 click.echo()
0147
0148 click.echo(click.style("PROBLEM CONFIGURATION", fg="cyan", bold=True))
0149 click.echo(f" Name: {config.problem.name}")
0150 click.echo(f" Type: {config.problem.problem_type}")
0151 click.echo(f" Output Location: {config.problem.output_location}")
0152 click.echo(f" Work Location: {config.problem.work_location}")
0153 click.echo()
0154
0155 click.echo(click.style("DESIGN PARAMETERS", fg="cyan", bold=True))
0156 param_names = config.problem.design_config.get_parameter_names()
0157 flat_params = config.problem.design_config.get_flat_parameters()
0158
0159 params_by_group = {}
0160 for name in param_names:
0161 group = name.split('.')[0]
0162 params_by_group.setdefault(group, []).append(name)
0163
0164 for group, names in params_by_group.items():
0165 click.echo(f"\n {group} ({len(names)} parameters):")
0166 for name in names:
0167 param = flat_params[name]
0168 param_short_name = name.split('.', 1)[1]
0169 bounds = config.problem.design_config.get_parameter_bounds(name)
0170 if bounds:
0171 click.echo(f" - {param_short_name}: {param.value} {bounds}")
0172 else:
0173 choices = config.problem.design_config.get_parameter_choices(name)
0174 click.echo(f" - {param_short_name}: {param.value} {choices}")
0175
0176 if config.problem.design_config.parameter_constraints:
0177 click.echo()
0178 click.echo(click.style("PARAMETER CONSTRAINTS", fg="cyan", bold=True))
0179 for constraint in config.problem.design_config.parameter_constraints:
0180 click.echo(f" - {constraint.name}")
0181 click.echo(f" Rule: {constraint.rule}")
0182 if constraint.description:
0183 click.echo(f" Description: {constraint.description}")
0184
0185 click.echo()
0186 click.echo(click.style("OPTIMIZATION CONFIGURATION", fg="cyan", bold=True))
0187 click.echo(f" Name: {config.optimization.name}")
0188 if config.optimization.description:
0189 click.echo(f" Description: {config.optimization.description}")
0190 click.echo(f" Optimizer: {config.optimization.optimizer.name} ({config.optimization.optimizer.type})")
0191 click.echo(f" Iterations: {config.optimization.n_iterations}")
0192 click.echo(f" Initial Samples: {config.optimization.n_initial_samples}")
0193 click.echo(f" Parallel Evaluations: {config.optimization.parallel_evaluations}")
0194
0195 if config.optimization.objectives:
0196 click.echo()
0197 click.echo(f" Objectives ({len(config.optimization.objectives)}):")
0198 for obj in config.optimization.objectives:
0199 click.echo(f" - {obj}")
0200
0201 if config.optimization.optimizer.parameters:
0202 click.echo()
0203 click.echo(" Optimizer Parameters:")
0204 for key, value in config.optimization.optimizer.parameters.items():
0205 click.echo(f" - {key}: {value}")
0206
0207 click.echo()
0208 click.echo(click.style("=" * 60, bold=True))
0209
0210 except Exception as e:
0211 click.echo(click.style(f"✗ Error: {e}", fg="red"), err=True)
0212 sys.exit(1)
0213
0214
0215 @cli.command()
0216 def version():
0217 """Display version information."""
0218 click.echo(f"AID2E Framework v{__MAIN_VERSION__}")
0219 click.echo("AI assisted Detector Design for EIC")
0220
0221
0222 @cli.command(name="optimize")
0223 @click.argument("config_file", type=click.Path(exists=True))
0224 @click.option("--validate-only", is_flag=True, help="Validate config but do not run")
0225 @click.option("-v", "--verbosity", count=True, help="Increase verbosity (can be used multiple times)")
0226 @click.option("--log", "log_file", type=click.Path(dir_okay=False), help="Path to log file")
0227 def optimize(config_file: str, validate_only: bool, verbosity: int, log_file: Optional[str]):
0228 """
0229 Run optimization based on configuration file.
0230
0231 CONFIG_FILE: Path to the YAML configuration file.
0232
0233 Example:
0234 aid2e optimize optimization.yml
0235 aid2e optimize optimization.yml --validate-only
0236 aid2e optimize optimization.yml -vv --log output.log
0237 """
0238 try:
0239 if verbosity > 0:
0240 click.echo(f"Loading configuration from: {config_file}")
0241
0242 config = load_config(config_file)
0243
0244 if validate_only:
0245 click.echo(click.style("✓ Configuration validated; skipping execution.", fg="green"))
0246 return
0247
0248
0249 click.echo(click.style(f"Running optimization: {config.optimization.name}", fg="cyan", bold=True))
0250 click.echo(f" Algorithm: {config.optimization.optimizer.name} ({config.optimization.optimizer.type})")
0251 click.echo(f" Iterations: {config.optimization.n_iterations}")
0252 click.echo(f" Verbosity: {verbosity}")
0253 if log_file:
0254 click.echo(f" Log file: {log_file}")
0255 click.echo()
0256
0257
0258 click.echo(click.style("Note: Optimizer execution not yet implemented.", fg="yellow"))
0259 click.echo("The configuration has been validated and is ready for optimization.")
0260
0261 except Exception as e:
0262 click.echo(click.style(f"✗ Error: {e}", fg="red"), err=True)
0263 if verbosity > 1:
0264 import traceback
0265 traceback.print_exc()
0266 sys.exit(1)
0267
0268
0269 _load_plugin_commands(cli)
0270
0271 if __name__ == "__main__":
0272 cli()