| """ |
| experiments/diagnose_trial.py |
| ============================== |
| Diagnostic tool for debugging failed Phase 2 trials. |
| |
| Runs a single trial with verbose logging to understand why it's failing. |
| |
| Usage: |
| python experiments/diagnose_trial.py --verbose |
| python experiments/diagnose_trial.py --seed 42 --out debug_trial/ |
| """ |
|
|
| import argparse |
| import json |
| from pathlib import Path |
|
|
| import numpy as np |
|
|
| from qdot.core.types import ChargeLabel, HITLOutcome |
| from qdot.core.state import ExperimentState |
| from qdot.core.hitl import HITLManager |
| from qdot.core.governance import GovernanceLogger |
| from qdot.simulator.cim import CIMSimulatorAdapter |
| from qdot.perception.inspector import InspectionAgent |
| from qdot.agent.executive import ExecutiveAgent |
|
|
|
|
| def main(): |
| parser = argparse.ArgumentParser(description="Diagnose a single Phase 2 trial") |
| parser.add_argument("--seed", type=int, default=42) |
| parser.add_argument("--max-steps", type=int, default=100) |
| parser.add_argument("--budget", type=int, default=2048) |
| parser.add_argument("--verbose", action="store_true", help="Print step-by-step progress") |
| parser.add_argument("--out", type=str, default="results/diagnose") |
| args = parser.parse_args() |
|
|
| out_dir = Path(args.out) |
| out_dir.mkdir(parents=True, exist_ok=True) |
|
|
| print("\n" + "="*70) |
| print("PHASE 2 DIAGNOSTIC TRIAL") |
| print("="*70 + "\n") |
|
|
| |
| state = ExperimentState.new(device_id="diagnostic_trial", target_label=ChargeLabel.DOUBLE_DOT) |
| adapter = CIMSimulatorAdapter(device_id="diagnostic_trial", seed=args.seed) |
| inspector = InspectionAgent(ensemble=None, ood_detector=None) |
|
|
| hitl = HITLManager(enabled=True) |
| hitl.set_test_mode(auto_outcome=HITLOutcome.APPROVED) |
|
|
| governance = GovernanceLogger(run_id=state.run_id, log_dir=str(out_dir / "governance")) |
|
|
| |
| agent = ExecutiveAgent( |
| state=state, |
| adapter=adapter, |
| inspection_agent=inspector, |
| hitl_manager=hitl, |
| governance_logger=governance, |
| max_steps=args.max_steps, |
| measurement_budget=args.budget, |
| ) |
|
|
| |
| print("Running trial...") |
| print(f"Max steps: {args.max_steps}") |
| print(f"Measurement budget: {args.budget}\n") |
|
|
| step_log = [] |
| last_stage = state.stage |
|
|
| for step_num in range(args.max_steps): |
| |
| should_continue = agent._step() |
|
|
| |
| step_info = { |
| "step": state.step, |
| "stage": str(state.stage), |
| "measurements": state.total_measurements, |
| "backtracks": state.consecutive_backtracks, |
| "voltage": {"vg1": state.current_voltage.vg1, "vg2": state.current_voltage.vg2}, |
| } |
| step_log.append(step_info) |
|
|
| |
| if args.verbose: |
| if state.stage != last_stage: |
| print(f"\n{'='*60}") |
| print(f"STAGE TRANSITION: {last_stage} → {state.stage}") |
| print(f"{'='*60}") |
| last_stage = state.stage |
|
|
| print(f"[{state.step:3d}] {state.stage.name:<16} | " |
| f"meas={state.total_measurements:4d} | " |
| f"backtracks={state.consecutive_backtracks} | " |
| f"V=({state.current_voltage.vg1:+.3f}, {state.current_voltage.vg2:+.3f})") |
|
|
| if not should_continue: |
| break |
|
|
| |
| print("\n" + "="*70) |
| print("DIAGNOSTIC SUMMARY") |
| print("="*70 + "\n") |
|
|
| print(f"Final stage: {state.stage}") |
| print(f"Success: {state.stage.name == 'COMPLETE'}") |
| print(f"Total steps: {state.step}") |
| print(f"Total measurements: {state.total_measurements}") |
| print(f"Total backtracks: {state.total_backtracks}") |
| print(f"Safety violations: {state.safety_violations}") |
| print(f"HITL events: {len(state.hitl_events)}") |
|
|
| |
| from collections import Counter |
| stage_counts = Counter(s["stage"] for s in step_log) |
| print(f"\nSteps per stage:") |
| for stage, count in stage_counts.most_common(): |
| print(f" {stage:<20} {count:3d} steps ({100*count/len(step_log):.1f}%)") |
|
|
| |
| print(f"\nPOTENTIAL ISSUES:") |
| if state.stage.name == 'COMPLETE': |
| print(" ✓ Trial completed successfully") |
| else: |
| if state.step >= args.max_steps: |
| print(f" ✗ Hit max step limit ({args.max_steps}) before completing") |
| print(f" → Consider increasing max_steps or investigating stage inefficiency") |
|
|
| if state.total_measurements >= args.budget: |
| print(f" ✗ Exhausted measurement budget ({args.budget})") |
| print(f" → Agent is taking too many measurements") |
|
|
| stuck_stage = max(stage_counts, key=stage_counts.get) |
| if stage_counts[stuck_stage] > 10: |
| print(f" ✗ Agent spent {stage_counts[stuck_stage]} steps in {stuck_stage}") |
| print(f" → Check stage success criteria and retry logic") |
|
|
| if state.consecutive_backtracks > 5: |
| print(f" ✗ High backtrack count ({state.consecutive_backtracks})") |
| print(f" → State machine may be stuck in a loop") |
|
|
| |
| log_path = out_dir / "step_log.json" |
| with open(log_path, "w") as f: |
| json.dump({ |
| "summary": { |
| "final_stage": str(state.stage), |
| "success": state.stage.name == 'COMPLETE', |
| "total_steps": state.step, |
| "total_measurements": state.total_measurements, |
| "total_backtracks": state.total_backtracks, |
| }, |
| "steps": step_log, |
| }, f, indent=2) |
|
|
| print(f"\nDetailed log saved to: {log_path}") |
| print(f"Governance logs saved to: {out_dir}/governance/") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|