Skip to main content

cqlib_core/compile/
mod.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2025-2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! Compiler pipeline for lowering and optimizing quantum circuits.
14//!
15//! This module is the public entry point for cqlib circuit compilation. It
16//! takes a logical [`Circuit`](crate::circuit::Circuit), applies a deterministic
17//! staged lowering workflow, and returns a rebuilt circuit together with
18//! step-level diagnostics.
19//!
20//! The compiler workflow is intentionally a staged lowering pipeline rather
21//! than a dynamic pass manager. High-level circuit representations are lowered
22//! through stable compiler layers:
23//!
24//! ```text
25//! logical circuit
26//!   -> canonicalized logical IR
27//!   -> expanded circuit-backed definitions
28//!   -> decomposed unitary and multi-controlled gates
29//!   -> global commutation-set cancellation
30//!   -> knowledge-rule optimization
31//!   -> optional physical layout and SABRE routing
32//!   -> optional target-basis translation
33//!   -> canonicalized output
34//!   -> exact device instruction lowering
35//!   -> native-input canonicalization and fixed-point optimization
36//!   -> target-specific candidate validation
37//!   -> optional validated SABRE Pareto selection
38//! ```
39//!
40//! The last step applies only to [`CompileMode::Enhanced`] with a strict
41//! [`CompileTarget::Device`]. Candidate zero is finalized and validated first;
42//! every exploratory route then repeats the same post-routing suffix through
43//! validation. Selection performs no further circuit transform and either
44//! retains candidate zero or chooses an already validated Pareto improvement.
45//!
46//! # Public Entry Points
47//!
48//! - [`compile`] is the recommended user-facing API.
49//! - [`CompileConfig`] describes optimization effort, one explicit
50//!   [`CompileTarget`], and resource policy.
51//! - [`CompileResult`] returns the compiled circuit and the workflow step
52//!   report.
53//! - [`CompilerWorkflow`] is useful when callers want to construct and inspect
54//!   a workflow explicitly.
55//!
56//! Lower-level modules such as [`transform`], [`sabre`], [`knowledge`], and
57//! [`commutation`] expose reusable compiler infrastructure. They are intended
58//! for advanced users and internal composition; ordinary compilation should
59//! start with [`compile`].
60//!
61//! # Target Constraints
62//!
63//! [`CompileTarget::Logical`] keeps logical operations,
64//! [`CompileTarget::Basis`] lowers to an explicit basis, and
65//! [`CompileTarget::Device`] routes and lowers for one device. The
66//! [`CompileTarget::TopologyBasis`] target routes on a device topology and
67//! lowers to a non-empty caller-supplied basis without claiming native-device
68//! compatibility. Strict device compilation
69//! uses an exact-plan-aware SABRE
70//! movement graph and checks local
71//! unary and ordered two-qubit capabilities during routing. Its following
72//! device-lowering stage emits the shared exact-qargs plans, then
73//! [`Device::validate_circuit`](crate::device::Device::validate_circuit)
74//! validates the completed output.
75//! A successful device compilation therefore never returns a circuit rejected
76//! by the configured device.
77//!
78//! [`DeviceCompileTarget::initial_layout`] may skip automatic initial-layout
79//! selection. [`DeviceCompileTarget::seed`] controls only device layout and
80//! routing heuristics. [`CompileResult::device_metadata`] records the initial
81//! and final layouts whenever compilation performs device-topology routing.
82//!
83//! # Classical Control and High-Level Operations
84//!
85//! The workflow preserves classical-control structure. Transforms that support
86//! control-flow bodies recurse into them and report whether they changed the IR
87//! through [`TransformOutcome`](transform::TransformOutcome). The workflow does
88//! not pre-scan control-flow trees to decide whether a transform should run.
89//! This module does not currently lower dynamic classical control into a
90//! hardware runtime instruction format.
91//!
92//! # Step Reports
93//!
94//! [`WorkflowStepReport`] records the workflow-local stage and step name, plus
95//! whether the step changed the circuit, was skipped, or emitted a short
96//! reason. Step names describe workflow positions such as `route.sabre` or
97//! `translate.target_basis`; they are not required to equal
98//! [`Transformer::name`](transform::Transformer::name).
99//!
100//! For an enhanced strict-device run, the returned reports describe the retained
101//! baseline or winning candidate path followed by `select.sabre_pareto_beam`;
102//! they are not an exhaustive trace of every discarded exploratory branch.
103//!
104//! # Examples
105//!
106//! Compile a logical circuit with default logical optimization:
107//!
108//! ```rust
109//! use cqlib_core::circuit::{Circuit, Qubit};
110//! use cqlib_core::compile::{CompileConfig, CompileMode, CompileTarget, compile};
111//! use cqlib_core::compile::resource::ResourcePolicy;
112//!
113//! let mut circuit = Circuit::new(2);
114//! circuit.h(Qubit::new(0)).unwrap();
115//! circuit.cx(Qubit::new(0), Qubit::new(1)).unwrap();
116//!
117//! let result = compile(
118//!     &circuit,
119//!     CompileConfig {
120//!         mode: CompileMode::Normal,
121//!         target: CompileTarget::Logical,
122//!         resource_policy: ResourcePolicy::default(),
123//!     },
124//! )
125//! .unwrap();
126//!
127//! assert_eq!(result.mode, CompileMode::Normal);
128//! assert!(!result.steps.is_empty());
129//! ```
130//!
131//! Compile to an explicit target basis:
132//!
133//! ```rust
134//! use cqlib_core::circuit::{Circuit, Instruction, Qubit, StandardGate};
135//! use cqlib_core::compile::{CompileConfig, CompileMode, CompileTarget, compile};
136//! use cqlib_core::compile::resource::ResourcePolicy;
137//!
138//! let mut circuit = Circuit::new(2);
139//! circuit.h(Qubit::new(0)).unwrap();
140//! circuit.cx(Qubit::new(0), Qubit::new(1)).unwrap();
141//!
142//! let result = compile(
143//!     &circuit,
144//!     CompileConfig {
145//!         mode: CompileMode::Normal,
146//!         target: CompileTarget::Basis(vec![
147//!             Instruction::Standard(StandardGate::H),
148//!             Instruction::Standard(StandardGate::CZ),
149//!         ]),
150//!         resource_policy: ResourcePolicy::default(),
151//!     },
152//! )
153//! .unwrap();
154//!
155//! assert!(
156//!     result
157//!         .steps
158//!         .iter()
159//!         .any(|step| step.name == "translate.target_basis" && step.changed)
160//! );
161//! ```
162//!
163//! Compile for a device topology:
164//!
165//! ```rust
166//! use cqlib_core::circuit::{Circuit, Qubit};
167//! use cqlib_core::compile::{CompileConfig, CompileMode, CompileTarget, DeviceCompileTarget, compile};
168//! use cqlib_core::compile::resource::ResourcePolicy;
169//! use cqlib_core::device::Device;
170//! use cqlib_core::circuit::{Instruction, StandardGate};
171//!
172//! let mut circuit = Circuit::new(2);
173//! circuit.cx(Qubit::new(0), Qubit::new(1)).unwrap();
174//! let device = Device::bidirectional_line("line-2", 2)
175//!     .unwrap()
176//!     .with_native_gates(vec![Instruction::Standard(StandardGate::CX)])
177//!     .unwrap();
178//!
179//! let result = compile(
180//!     &circuit,
181//!     CompileConfig {
182//!         mode: CompileMode::Normal,
183//!         target: CompileTarget::Device(DeviceCompileTarget {
184//!             device,
185//!             initial_layout: None,
186//!             seed: Some(7),
187//!         }),
188//!         resource_policy: ResourcePolicy::default(),
189//!     },
190//! )
191//! .unwrap();
192//!
193//! assert!(
194//!     result
195//!         .steps
196//!         .iter()
197//!         .any(|step| step.name == "route.sabre" && !step.skipped)
198//! );
199//! ```
200//!
201//! Route from a supplied initial layout:
202//!
203//! ```rust
204//! use cqlib_core::circuit::{Circuit, Qubit};
205//! use cqlib_core::compile::{CompileConfig, CompileMode, CompileTarget, DeviceCompileTarget, compile};
206//! use cqlib_core::compile::resource::ResourcePolicy;
207//! use cqlib_core::device::{Device, Layout};
208//!
209//! let mut circuit = Circuit::new(1);
210//! circuit.h(Qubit::new(0)).unwrap();
211//! let layout = Layout::from_pairs(&[(0, 2)], 3).unwrap();
212//!
213//! let result = compile(
214//!     &circuit,
215//!     CompileConfig {
216//!         mode: CompileMode::Normal,
217//!         target: CompileTarget::Device(DeviceCompileTarget {
218//!             device: Device::line("line-3", 3)
219//!                 .unwrap()
220//!                 .with_native_gates(vec![
221//!                     cqlib_core::circuit::Instruction::Standard(
222//!                         cqlib_core::circuit::StandardGate::H,
223//!                     ),
224//!                 ])
225//!                 .unwrap(),
226//!             initial_layout: Some(layout),
227//!             seed: Some(11),
228//!         }),
229//!         resource_policy: ResourcePolicy::default(),
230//!     },
231//! )
232//! .unwrap();
233//!
234//! assert_eq!(result.circuit.operations()[0].qubits.as_slice(), &[Qubit::new(2)]);
235//! ```
236//!
237//! Inspect workflow step reports:
238//!
239//! ```rust
240//! use cqlib_core::circuit::Circuit;
241//! use cqlib_core::compile::{CompileConfig, CompileMode, CompileTarget, compile};
242//! use cqlib_core::compile::resource::ResourcePolicy;
243//!
244//! let result = compile(
245//!     &Circuit::new(1),
246//!     CompileConfig {
247//!         mode: CompileMode::Enhanced,
248//!         target: CompileTarget::Logical,
249//!         resource_policy: ResourcePolicy::default(),
250//!     },
251//! )
252//! .unwrap();
253//!
254//! let routing = result
255//!     .steps
256//!     .iter()
257//!     .find(|step| step.name == "route.sabre")
258//!     .unwrap();
259//! assert!(routing.skipped);
260//! ```
261
262pub mod commutation;
263pub mod compiler;
264pub(crate) mod device_planning;
265pub mod error;
266pub mod knowledge;
267pub(crate) mod numeric_matrix;
268pub(crate) mod parallelism;
269pub mod physical_target;
270pub mod resource;
271pub mod sabre;
272pub mod transform;
273pub mod workflow;
274
275#[cfg(test)]
276mod test_utils;
277
278/// Tolerance for proving equality between compiler parameter expressions.
279pub(crate) const PARAMETER_EQ_TOLERANCE: f64 = 1e-12;
280
281pub(crate) fn compare_some_first_by<T>(
282    left: Option<T>,
283    right: Option<T>,
284    compare: impl FnOnce(T, T) -> std::cmp::Ordering,
285) -> std::cmp::Ordering {
286    match (left, right) {
287        (Some(left), Some(right)) => compare(left, right),
288        (Some(_), None) => std::cmp::Ordering::Less,
289        (None, Some(_)) => std::cmp::Ordering::Greater,
290        (None, None) => std::cmp::Ordering::Equal,
291    }
292}
293
294/// Tolerance for treating a scalar as numerically zero.
295pub(crate) const NUMERIC_ZERO_TOLERANCE: f64 = 1e-14;
296
297/// Tolerance for checking whether a candidate phase ratio has unit norm.
298pub(crate) const UNIT_PHASE_NORM_TOLERANCE: f64 = 1e-8;
299
300pub use commutation::{
301    Commutation, CommutationChecker, CommutationConfig, CommutationResult, algebraic_commutation,
302    check_commutation,
303};
304pub use compiler::{
305    CompileConfig, CompileMode, CompileResult, CompileTarget, DeviceCompilationMetadata,
306    DeviceCompileTarget, compile, compile_owned,
307};
308pub use error::{CompilerError, SabreRoutingFailure};
309pub use sabre::{
310    SabreConfig, SabreHeuristicConfig, SabreRoutingDiagnostics, SabreRoutingResult,
311    SabreVf2PrepassConfig, normalize_initial_layout, sabre_route, validate_reachable_interactions,
312};
313pub use transform::{KnowledgeRewriteDiagnostics, VirtualPermutation};
314pub use workflow::{CompilerWorkflow, WorkflowStepReport};