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};