Skip to main content

cqlib_core/compile/
compiler.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.
6// You may obtain a copy of this license in the LICENSE.txt file in
7// the root directory of this source tree or at
8// http://www.apache.org/licenses/LICENSE-2.0.
9//
10// Any modifications or derivative works of this code must retain this
11// copyright notice, and modified files need to carry a notice indicating
12// that they have been altered from the originals.
13
14//! Public compiler entry point.
15//!
16//! The compiler entry point runs the configured [`CompilerWorkflow`]
17//! and returns the optimized circuit plus step-level diagnostics. The workflow
18//! starts from a logical circuit, applies canonicalization, definition
19//! expansion, knowledge-based rewrite, unitary and multi-controlled-gate
20//! decomposition, optional device layout/routing, and target lowering.
21//!
22//! [`CompileTarget`] makes the target contract explicit: compilation is either
23//! logical, lowered to a requested basis, or routed and legalized for one
24//! concrete device. Device compilation uses the device topology for capacity
25//! checks and SABRE routing, then checks every emitted standard operation
26//! against the device's ordered native capabilities before final validation.
27//!
28//! [`CompileMode::Normal`] selects conservative production defaults.
29//! [`CompileMode::Enhanced`] keeps the same semantic contract but spends more
30//! rewrite, resynthesis, native-optimization, and routing effort, runs additional
31//! cleanup around routing and target lowering, and, for a strict device target,
32//! selects among fully lowered and validated routes with a bounded SABRE Pareto
33//! beam.
34
35use super::workflow::CompilerWorkflow;
36use crate::circuit::{Circuit, Instruction};
37use crate::compile::resource::ResourcePolicy;
38use crate::compile::transform::VirtualPermutation;
39use crate::compile::{CompilerError, WorkflowStepReport};
40use crate::device::{Device, Layout};
41
42/// Optimization effort selected for the compiler workflow.
43#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
44pub enum CompileMode {
45    /// Conservative logical optimization using production pass defaults.
46    #[default]
47    Normal,
48    /// A richer staged workflow with stronger pass budgets, target-aware
49    /// cleanup, and validated SABRE Pareto route selection for strict device
50    /// targets.
51    Enhanced,
52}
53
54/// User-facing configuration for [`compile`].
55///
56/// The configuration describes logical optimization effort, optional target
57/// constraints, and the ancillary-resource permissions available before layout.
58#[derive(Debug, Clone)]
59pub struct CompileConfig {
60    /// Optimization workflow mode.
61    pub mode: CompileMode,
62    /// Mutually exclusive logical, basis, or physical-device target.
63    pub target: CompileTarget,
64    /// Ancillary-resource permission for pre-layout decomposition passes.
65    ///
66    /// This controls whether logical clean ancillas may be allocated or dirty
67    /// input qubits may be borrowed. A device target derives hard capacity
68    /// from its usable physical qubits rather than this policy.
69    pub resource_policy: ResourcePolicy,
70}
71
72/// Target contract selected for a compilation run.
73///
74/// Device targets intentionally stay inline so the public configuration API
75/// does not require allocation solely to select a target. Compile
76/// configurations are coarse workflow objects, not per-operation data.
77#[allow(clippy::large_enum_variant)]
78#[derive(Debug, Clone)]
79pub enum CompileTarget {
80    /// Compile in logical qubit space without target-specific lowering.
81    Logical,
82    /// Lower to an explicit standard-gate basis.
83    Basis(Vec<Instruction>),
84    /// Route and lower for one concrete target device.
85    Device(DeviceCompileTarget),
86    /// Route on a device topology while lowering to an explicit gate basis.
87    ///
88    /// This target uses the device for capacity, layout, and
89    /// routing only. It does not require the output basis to match the
90    /// device's native capabilities and therefore does not perform exact
91    /// device-native lowering or final device validation.
92    TopologyBasis {
93        /// Device-specific layout and routing inputs.
94        device_target: DeviceCompileTarget,
95        /// Explicit standard-gate basis required at the output.
96        basis: Vec<Instruction>,
97    },
98}
99
100/// Device-specific compilation inputs.
101#[derive(Debug, Clone)]
102pub struct DeviceCompileTarget {
103    /// Device whose ordered native capabilities constrain the output.
104    pub device: Device,
105    /// Optional caller-supplied initial logical-to-physical layout.
106    pub initial_layout: Option<Layout>,
107    /// Optional deterministic seed for device layout and routing heuristics.
108    pub seed: Option<u32>,
109}
110
111/// Physical-layout information produced by device compilation.
112#[derive(Debug, Clone, PartialEq)]
113pub struct DeviceCompilationMetadata {
114    /// Logical-to-physical layout before routing begins.
115    pub initial_layout: Layout,
116    /// Original logical-output-to-physical layout after all routed swaps and
117    /// pre-layout virtual permutations.
118    pub final_layout: Layout,
119    /// Original logical output to rewritten logical output mapping accumulated
120    /// before physical layout. The initial circuit inputs remain identity-mapped.
121    pub virtual_permutation: VirtualPermutation,
122}
123
124/// Result returned by [`compile`].
125#[derive(Debug, Clone, PartialEq)]
126pub struct CompileResult {
127    /// Optimized circuit.
128    pub circuit: Circuit,
129    /// Whether any workflow step changed the input representation.
130    pub changed: bool,
131    /// Workflow mode used for this run.
132    pub mode: CompileMode,
133    /// Step-level report for the retained output path, in workflow order.
134    ///
135    /// Enhanced strict-device compilation may evaluate other route branches.
136    /// Discarded branches are summarized by `select.sabre_pareto_beam` rather
137    /// than retained as complete step sequences here.
138    pub steps: Vec<WorkflowStepReport>,
139    /// Physical-layout data when compilation used device-topology routing.
140    pub device_metadata: Option<DeviceCompilationMetadata>,
141}
142
143impl CompileResult {
144    /// Returns the first workflow report with the requested step name.
145    pub fn step(&self, name: &str) -> Option<&WorkflowStepReport> {
146        self.steps.iter().find(|step| step.name == name)
147    }
148
149    /// Returns whether any report with the requested name changed the circuit.
150    ///
151    /// Skipped reports are not considered changes.
152    pub fn step_changed(&self, name: &str) -> bool {
153        self.steps
154            .iter()
155            .any(|step| step.name == name && step.changed && !step.skipped)
156    }
157}
158
159/// Runs the configured compiler workflow over `circuit`.
160///
161/// The returned result records the optimized circuit and step-level reports for
162/// the retained output path in workflow order. A device target additionally
163/// returns its initial and final layouts. Errors are reported when a configured
164/// target, native realization, or candidate validation cannot be satisfied.
165///
166/// # Examples
167///
168/// ```rust
169/// use cqlib_core::circuit::{Circuit, Qubit};
170/// use cqlib_core::compile::{CompileConfig, CompileMode, CompileTarget, compile};
171/// use cqlib_core::compile::resource::ResourcePolicy;
172///
173/// let mut circuit = Circuit::new(2);
174/// circuit.h(Qubit::new(0)).unwrap();
175/// circuit.cx(Qubit::new(0), Qubit::new(1)).unwrap();
176///
177/// let result = compile(
178///     &circuit,
179///     CompileConfig {
180///         mode: CompileMode::Normal,
181///         target: CompileTarget::Logical,
182///         resource_policy: ResourcePolicy::default(),
183///     },
184/// )
185/// .unwrap();
186///
187/// assert_eq!(result.mode, CompileMode::Normal);
188/// assert!(!result.steps.is_empty());
189/// assert_eq!(result.circuit.qubits().len(), 2);
190/// ```
191pub fn compile(circuit: &Circuit, config: CompileConfig) -> Result<CompileResult, CompilerError> {
192    CompilerWorkflow::try_new(config)?.run(circuit)
193}
194
195/// Runs the configured compiler workflow by consuming `circuit`.
196///
197/// This is equivalent to [`compile`] but avoids cloning the complete input
198/// circuit when the caller no longer needs it.
199pub fn compile_owned(
200    circuit: Circuit,
201    config: CompileConfig,
202) -> Result<CompileResult, CompilerError> {
203    CompilerWorkflow::try_new(config)?.run_owned(circuit)
204}
205
206#[cfg(test)]
207#[path = "./compile_test.rs"]
208mod compile_test;