test_p3_checkpoint.py 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. """P3 checkpoint test: search import_state + AdaptiveLoop export/restore.
  2. Verifies the /resume path:
  3. 1. FeasibilityFirstSearch.export_state -> import_state round-trip keeps
  4. run_id / budget / points / objective / convergence and can select the
  5. next batch.
  6. 2. AdaptiveLoop.export_state -> restore_state survives a simulated process
  7. restart (registry cleared) and can keep driving batches.
  8. Run: python scripts/test_p3_checkpoint.py (exit 0 = PASS)
  9. Isolated temp SQLite DB; no real Motor-CAD involved.
  10. """
  11. import os
  12. import sys
  13. import tempfile
  14. _TMP = os.path.join(tempfile.mkdtemp(), "ck.db")
  15. os.environ["AFM_DB_PATH"] = _TMP
  16. os.environ["KIMI_API_KEY"] = ""
  17. _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  18. sys.path.insert(0, os.path.join(_ROOT, "web", "backend"))
  19. sys.path.insert(0, _ROOT)
  20. from app.database import init_db # noqa: E402
  21. init_db()
  22. from app.services.feasibility_search import ( # noqa: E402
  23. FeasibilityFirstSearch, ParameterRange, L0PreScreeningEngine,
  24. )
  25. # ---------------------------------------------------------------- 1. search round-trip
  26. params = [ParameterRange(name="airgap_mm", min_value=0.8, max_value=2.0,
  27. step=0.1, unit="mm")]
  28. s1 = FeasibilityFirstSearch(parameters=params, l0_engine=L0PreScreeningEngine(),
  29. total_budget=8, batch_size=4, initial_samples=4,
  30. objective_metric="tavg_nm", objective_direction="maximize")
  31. batch = s1.generate_initial_batch()
  32. for i, p in enumerate(batch[:3]):
  33. s1.report_result(p.id, {"tavg_nm": 8.0 + 0.5 * p.id}, "ok")
  34. ex = s1.export_state()
  35. s2 = FeasibilityFirstSearch.import_state(ex, l0_engine=L0PreScreeningEngine())
  36. assert s2.state.run_id == s1.state.run_id
  37. assert s2.state.used_budget == s1.state.used_budget == 3
  38. assert len(s2.state.points) == len(s1.state.points)
  39. assert s2.objective_metric == "tavg_nm" and s2.objective_direction == "maximize"
  40. assert s2.state.convergence_status == s1.state.convergence_status
  41. nb = s2.select_next_batch()
  42. assert nb, "restored search must be able to select the next batch"
  43. print("[1] search export->import round-trip OK (next batch: %d pts)" % len(nb))
  44. # ---------------------------------------------------------------- 2. AdaptiveLoop resume
  45. from app.services.adaptive_loop import ( # noqa: E402
  46. AdaptiveLoop, create_loop, _loops,
  47. )
  48. loop = create_loop(user_requirement="max torque", total_budget=8, batch_size=4)
  49. loop.plan = {
  50. "plan_name": "p", "topology": "SSSR",
  51. "scan_variables": [
  52. {"name": "airgap_mm", "min_value": 0.8, "max_value": 2.0},
  53. {"name": "current_a", "min_value": 5.0, "max_value": 20.0},
  54. ],
  55. "search_strategy": {"max_solver_calls": 8, "batch_size": 4,
  56. "initial_samples": 4},
  57. "acceptance_criteria": {"objective_metric": "tavg_nm",
  58. "objective_direction": "maximize",
  59. "hard_constraints": []},
  60. }
  61. loop.initialize_search()
  62. sub = loop.submit_batch_to_executor()
  63. assert sub.get("task_id"), sub
  64. exported = loop.export_state()
  65. assert exported["search"] is not None
  66. assert exported["loop_id"] == loop.loop_id
  67. assert exported["phase"] == loop.phase.value
  68. # simulate process restart: in-memory registry is lost
  69. _loops.clear()
  70. assert len(_loops) == 0
  71. loop2 = AdaptiveLoop.restore_state(exported)
  72. assert loop2.loop_id == loop.loop_id
  73. assert loop2.phase.value == exported["phase"]
  74. assert loop2.search is not None
  75. sub2 = loop2.submit_batch_to_executor()
  76. assert sub2.get("task_id"), "restored loop must be able to submit a batch"
  77. print("[2] AdaptiveLoop export->restore after restart OK (batch resubmitted)")
  78. print("\nALL P3 CHECKPOINT TESTS PASSED")