From 4f1270cbb391163e7d111908b517884108a4e422 Mon Sep 17 00:00:00 2001 From: Masato Fukushima Date: Tue, 12 May 2026 21:09:17 -0400 Subject: [PATCH] Validate provided scheduler before qompile --- CHANGELOG.md | 4 ++++ graphqomb/qompiler.py | 6 +++++- tests/test_scheduler_integration.py | 26 ++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92582790..3e8a9d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **Non-Unitary Parity Projection Example**: Added `examples/nonunitary_parity_projection.py` demonstrating measurement-induced entanglement via a 3-node star graph parity projector +### Fixed + +- **Qompiler**: `qompile()` now validates a provided scheduler before pattern generation, so invalid manual schedules fail early with `ValueError`. + ## [0.3.0] - 2026-04-08 ### Added diff --git a/graphqomb/qompiler.py b/graphqomb/qompiler.py index 1aa07d0a..198c283f 100644 --- a/graphqomb/qompiler.py +++ b/graphqomb/qompiler.py @@ -54,6 +54,7 @@ def qompile( # noqa: PLR0913 scheduler to schedule the graph state preparation and measurements, if `None`, a `Scheduler` is constructed internally and solved with the default ``MINIMIZE_TIME`` strategy before compiling the pattern, + otherwise the provided scheduler is validated before compiling the pattern, by default `None` Returns @@ -97,6 +98,7 @@ def _qompile( scheduler to schedule the graph state preparation and measurements, if `None`, a `Scheduler` is constructed internally and solved with the default ``MINIMIZE_TIME`` strategy before compiling the pattern, + otherwise the provided scheduler is validated before compiling the pattern, by default `None` Returns @@ -112,9 +114,11 @@ def _qompile( topo_order.reverse() # children first commands: list[Command] = [] - if not scheduler: + if scheduler is None: scheduler = Scheduler(graph, pauli_frame.xflow, pauli_frame.zflow) scheduler.solve_schedule() + else: + scheduler.validate_schedule() timeline = scheduler.timeline diff --git a/tests/test_scheduler_integration.py b/tests/test_scheduler_integration.py index 17c1c4d6..a475423a 100644 --- a/tests/test_scheduler_integration.py +++ b/tests/test_scheduler_integration.py @@ -395,6 +395,32 @@ def test_validate_schedule_dag_violations() -> None: scheduler2.validate_schedule() +def test_qompile_validates_provided_scheduler() -> None: + """Qompile should reject invalid schedules before pattern generation.""" + graph = GraphState() + node0 = graph.add_physical_node() + node1 = graph.add_physical_node() + node2 = graph.add_physical_node() + graph.add_physical_edge(node0, node1) + graph.add_physical_edge(node1, node2) + + qindex = 0 + graph.register_input(node0, qindex) + graph.register_output(node2, qindex) + graph.assign_meas_basis(node0, PlannerMeasBasis(Plane.XY, 0.0)) + graph.assign_meas_basis(node1, PlannerMeasBasis(Plane.XY, 0.0)) + + flow = {node0: {node1}, node1: {node2}} + scheduler = Scheduler(graph, flow) + scheduler.manual_schedule( + prepare_time={node1: 0, node2: 0}, + measure_time={node0: 2, node1: 1}, + ) + + with pytest.raises(ValueError, match="DAG violation"): + qompile(graph, flow, scheduler=scheduler) + + def test_validate_schedule_same_time_prep_meas() -> None: """Test that validate_schedule rejects schedules with nodes prepared and measured at same time.""" # Create a graph