Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 09:29:52

0001 """Example: Multi-operator workflow with Airflow-like orchestration.
0002 
0003 This example demonstrates:
0004 1. WorkflowDefinition with stages and schedulers
0005 2. Multiple operators (Bash, Python, Container)
0006 3. Stage-specific scheduler overrides
0007 4. Task data flow via XCom
0008 
0009 Project: AID2E v0.0.0
0010 """
0011 
0012 import sys
0013 from shlex import quote
0014 from pathlib import Path
0015 
0016 REPO_ROOT = Path(__file__).resolve().parents[3]
0017 if str(REPO_ROOT) not in sys.path:
0018     sys.path.insert(0, str(REPO_ROOT))
0019 
0020 from aid2e.utilities.workflows import (
0021     WorkflowDefinition,
0022     BranchDefinition,
0023     StageDefinition,
0024     JobDefinition,
0025     JobFactory,
0026     ParallelismPolicy,
0027     ArtifactSpec,
0028     JobContext,
0029     BashEvaluator,
0030     PythonEvaluator,
0031 )
0032 from aid2e.utilities.configurations.scheduler_config import SchedulerConfiguration
0033 from aid2e.utilities.configurations.objectives import ObjectiveDefinition
0034 from examples.dtlz2.dtlz2_evaluator import evaluate_both_objectives_wrapper
0035 
0036 
0037 # ============================================================================
0038 # Example 1: Using Operators Directly (Low-level)
0039 # ============================================================================
0040 
0041 def example_operators_direct():
0042     """Direct operator usage with JobContext."""
0043     
0044     # Create the working directory before direct execution.
0045     work_dir = Path('/tmp/aid2e_operators')
0046     work_dir.mkdir(parents=True, exist_ok=True)
0047 
0048     # Create a task context
0049     context = JobContext(
0050         task_id='stage_prepare:task_1',
0051         job_id='task_1',
0052         stage_id='stage_prepare',
0053         workflow_id='dtlz2_eval',
0054         design_point={f'x{i}': 0.5 for i in range(1, 11)},
0055         execution_dir=str(work_dir)
0056     )
0057     
0058     # Example 1: BashEvaluator
0059     bash_op = BashEvaluator(
0060         engine_id='prepare_params',
0061         bash_command='echo "Preparing with x1={{design_point.x1}}, x2={{design_point.x2}}" > params.txt'
0062     )
0063     result = bash_op.execute(context)
0064     print(f"BashEvaluator result: {result}")
0065     
0066     # Example 2: PythonEvaluator
0067     python_op = PythonEvaluator(
0068         engine_id='compute',
0069         python_callable=evaluate_both_objectives_wrapper,
0070     )
0071     result = python_op.execute(context)
0072     print(f"PythonEvaluator result: {result}")
0073     print(f"XCom objectives: {context.xcom_pull(context.task_id, 'objectives')}")
0074 
0075 
0076 # ============================================================================
0077 # Example 2: Workflow Definition with Stage-specific Schedulers
0078 # ============================================================================
0079 
0080 def example_workflow_definition():
0081     """Define DTLZ2 command jobs with a stage-level Slurm override."""
0082     
0083     workflow = WorkflowDefinition(
0084         name='dtlz2_evaluation',
0085         description='Evaluate DTLZ2 using a stage-specific scheduler',
0086         
0087         # Workflow default scheduler
0088         scheduler=SchedulerConfiguration(
0089             runner_type='JobLibRunner',
0090             parameters={'n_jobs': -1}
0091         ),
0092         
0093         branches=[
0094             BranchDefinition(
0095                 name='main',
0096                 stages=[
0097                     # The scheduler evaluates four copies of the same design.
0098                     StageDefinition(
0099                         name='evaluate',
0100                         jobs=[
0101                             JobDefinition(
0102                                 name='dtlz2_eval',
0103                                 command=(
0104                                     '"{{python_executable}}" "{{repo_root}}/examples/dtlz2/dtlz2_evaluator.py" '
0105                                     '--design "{{design_file}}" '
0106                                     '--output "{{output_dir}}/objectives.json" --repeat 0'
0107                                 ),
0108                                 outputs=[
0109                                     ArtifactSpec(path='{{output_dir}}/objectives.json', format='json')
0110                                 ]
0111                             )
0112                         ],
0113                         job_factory=JobFactory(
0114                             type='range',
0115                             params={'n': 4}  # Four jobs, not four optimizer candidates
0116                         ),
0117                         # Stage scheduler overrides the workflow default; adjust for your cluster.
0118                         scheduler=SchedulerConfiguration(
0119                             runner_type='SlurmRunner',
0120                             parameters={
0121                                 'partition': 'gpu',
0122                                 'ntasks': 4,
0123                                 'cpus_per_task': 2,
0124                                 'mem': '16G',
0125                                 'time': '00:30:00'
0126                             }
0127                         ),
0128                         parallelism=ParallelismPolicy(
0129                             max_concurrent=4,
0130                             retry_max=2,
0131                             timeout_sec=300
0132                         )
0133                     ),
0134                 ]
0135             )
0136         ],
0137         
0138         objectives=[
0139             ObjectiveDefinition(name='f1', direction='minimize'),
0140             ObjectiveDefinition(name='f2', direction='minimize'),
0141         ]
0142     )
0143     
0144     return workflow
0145 
0146 
0147 # ============================================================================
0148 # Example 3: Workflow with ContainerEvaluator
0149 # ============================================================================
0150 
0151 def example_workflow_with_containers():
0152     """Define a direct Docker job writing /tmp/aid2e_container_output/objectives.json.
0153 
0154     Execution requires Docker and a ten-variable qualified design point.
0155     This example only constructs the workflow.
0156     """
0157     
0158     output_dir = Path('/tmp/aid2e_container_output')
0159     command = (
0160         'python /app/examples/dtlz2/dtlz2_evaluator.py '
0161         + ' '.join(f'--x{i} "$X{i}"' for i in range(1, 11))
0162         + ' --output /output/objectives.json --repeat 0'
0163     )
0164     workflow = WorkflowDefinition(
0165         name='containerized_evaluation',
0166         description='Direct Docker execution; no scheduler configured',
0167         branches=[
0168             BranchDefinition(
0169                 name='main',
0170                 stages=[
0171                     StageDefinition(
0172                         name='evaluate_containers',
0173                         jobs=[
0174                             JobDefinition(
0175                                 name='dtlz2_container',
0176                                 command=command,
0177                                 payload={
0178                                     'evaluator_type': 'container',
0179                                     'image': 'python:3.11-slim',
0180                                     'container_command': ['/bin/sh', '-c', quote(command)],
0181                                     'volumes': {
0182                                         quote(str(REPO_ROOT)): '/app:ro',
0183                                         str(output_dir): '/output',
0184                                     },
0185                                     'environment': {
0186                                         f'X{i}': f'{{{{design_point.DTLZ2_variables.x{i}}}}}'
0187                                         for i in range(1, 11)
0188                                     },
0189                                 },
0190                                 resources={'memory': '4g', 'cpus': '2'},
0191                                 outputs=[
0192                                     ArtifactSpec(path=str(output_dir / 'objectives.json'), format='json')
0193                                 ],
0194                             )
0195                         ],
0196                     )
0197                 ]
0198             )
0199         ]
0200     )
0201 
0202     return workflow
0203 
0204 
0205 # ============================================================================
0206 # Example 4: Stage Scheduler Resolution
0207 # ============================================================================
0208 
0209 def example_scheduler_resolution():
0210     """Demonstrate stage scheduler resolution hierarchy."""
0211     
0212     workflow = WorkflowDefinition(
0213         name='scheduler_demo',
0214         
0215         # Level 1: Workflow scheduler
0216         scheduler=SchedulerConfiguration(
0217             runner_type='JobLibRunner',
0218             parameters={'n_jobs': 2}
0219         ),
0220         
0221         branches=[
0222             BranchDefinition(
0223                 name='branch1',
0224                 # Level 2: Branch scheduler
0225                 scheduler=SchedulerConfiguration(
0226                     runner_type='JobLibRunner',
0227                     parameters={'n_jobs': 4}
0228                 ),
0229                 stages=[
0230                     StageDefinition(
0231                         name='stage_uses_branch',
0232                         jobs=[JobDefinition(name='job1', command='echo "hi"')],
0233                         scheduler=None  # Uses branch scheduler (n_jobs=4)
0234                     ),
0235                     StageDefinition(
0236                         name='stage_uses_own',
0237                         jobs=[JobDefinition(name='job2', command='echo "hi"')],
0238                         # Level 3: Stage scheduler (MOST SPECIFIC)
0239                         scheduler=SchedulerConfiguration(
0240                             runner_type='SlurmRunner',
0241                             parameters={'partition': 'gpu'}
0242                         )
0243                     ),
0244                 ]
0245             ),
0246             BranchDefinition(
0247                 name='branch2',
0248                 scheduler=None,  # Uses workflow scheduler
0249                 stages=[
0250                     StageDefinition(
0251                         name='stage_uses_workflow',
0252                         jobs=[JobDefinition(name='job3', command='echo "hi"')],
0253                         scheduler=None  # Uses workflow scheduler (n_jobs=2)
0254                     ),
0255                 ]
0256             )
0257         ]
0258     )
0259     
0260     # Resolution for each stage:
0261     print("\nScheduler Resolution:")
0262     print("-" * 60)
0263     print("stage_uses_branch:")
0264     print("  1. stage.scheduler? No")
0265     print("  2. branch.scheduler? Yes → JobLibRunner(n_jobs=4)")
0266     print()
0267     print("stage_uses_own:")
0268     print("  1. stage.scheduler? Yes → SlurmRunner(partition=gpu)")
0269     print()
0270     print("stage_uses_workflow:")
0271     print("  1. stage.scheduler? No")
0272     print("  2. branch.scheduler? No")
0273     print("  3. workflow.scheduler? Yes → JobLibRunner(n_jobs=2)")
0274 
0275 
0276 # ============================================================================
0277 # Main
0278 # ============================================================================
0279 
0280 if __name__ == '__main__':
0281     print("=" * 70)
0282     print("Example 1: Direct Operator Usage")
0283     print("=" * 70)
0284     example_operators_direct()
0285     
0286     print("\n" + "=" * 70)
0287     print("Example 2: Workflow Definition with Stage Schedulers")
0288     print("=" * 70)
0289     workflow = example_workflow_definition()
0290     print(f"Workflow: {workflow.name}")
0291     print(f"Stages: {[s.name for b in workflow.branches for s in b.stages]}")
0292     for branch in workflow.branches:
0293         for stage in branch.stages:
0294             scheduler_desc = stage.scheduler or "(inherited)"
0295             print(f"  {stage.name}: scheduler = {scheduler_desc}")
0296     
0297     print("\n" + "=" * 70)
0298     print("Example 3: Workflow with Containers")
0299     print("=" * 70)
0300     workflow = example_workflow_with_containers()
0301     print(f"Workflow: {workflow.name} (definition only; execution requires Docker)")
0302     
0303     print("\n" + "=" * 70)
0304     print("Example 4: Scheduler Resolution")
0305     print("=" * 70)
0306     example_scheduler_resolution()