Skip to main content

cqlib_core/circuit/gate/
mc_gate.rs

1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 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//! Multi-Controlled Gate Definitions
14//!
15//! This module provides [`MCGate`], a type representing gates with multiple
16//! control qubits applied to a base [`StandardGate`]. For example, a
17//! multi-controlled X gate with 2 controls is the CCX (Toffoli) gate.
18
19use crate::circuit::gate::{StandardGate, gate_matrix};
20use crate::circuit::{CircuitError, Parameter};
21use alloc::borrow::Cow;
22use ndarray::Array2;
23use num_complex::Complex;
24use smallvec::SmallVec;
25use std::fmt;
26
27/// A multi-controlled quantum gate.
28///
29/// `MCGate` represents a [`StandardGate`] with additional control qubits.
30/// The gate applies the base operation only when all control qubits are
31/// in the |1⟩ state.
32///
33/// # Examples
34///
35/// ```
36/// use cqlib_core::circuit::gate::{MCGate, StandardGate};
37///
38/// // Create a Toffoli-like gate (CCX) using 2 controls and X gate
39/// let ccx = MCGate::new(2, StandardGate::X);
40/// assert_eq!(ccx.num_ctrl_qubits(), 2);
41/// assert_eq!(ccx.num_qubits(), 3); // 2 controls + 1 target
42///
43/// // Create a multi-controlled Z gate
44/// let mcz = MCGate::new(3, StandardGate::Z);
45/// assert_eq!(mcz.num_ctrl_qubits(), 3);
46/// assert_eq!(mcz.num_qubits(), 4);
47/// ```
48#[derive(Eq, Hash, Ord, PartialEq, PartialOrd, Debug, Clone)]
49pub struct MCGate {
50    /// Number of additional control qubits (beyond any inherent controls).
51    num_controls: u8,
52    /// The base gate to be conditionally applied.
53    gate: StandardGate,
54}
55
56impl MCGate {
57    /// Creates a new multi-controlled gate.
58    ///
59    /// # Arguments
60    ///
61    /// * `num_controls` - The number of control qubits to add.
62    /// * `gate` - The base [`StandardGate`] to control.
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use cqlib_core::circuit::gate::{MCGate, StandardGate};
68    ///
69    /// // 3-controlled Hadamard
70    /// let mc_h = MCGate::new(3, StandardGate::H);
71    ///
72    /// // Controlled-RX (1 control added to RX)
73    /// let crx = MCGate::new(1, StandardGate::RX);
74    /// ```
75    pub fn new(num_controls: u8, gate: StandardGate) -> Self {
76        Self { num_controls, gate }
77    }
78
79    /// Returns the unitary matrix representation of the multi-controlled gate.
80    ///
81    /// The matrix is constructed by applying the control structure to the
82    /// base gate's matrix. For large numbers of controls, this results in
83    /// a sparse block-diagonal matrix.
84    ///
85    /// # Arguments
86    ///
87    /// * `params` - Parameters for the base gate (if parametric).
88    ///
89    /// # Returns
90    ///
91    /// A `Cow` containing the matrix, borrowed if no controls are present.
92    pub fn matrix(&self, params: &[f64]) -> Result<Cow<'_, Array2<Complex<f64>>>, CircuitError> {
93        let base_matrix = self.gate.matrix(params)?;
94        if self.num_controls == 0 {
95            return Ok(base_matrix);
96        }
97        // Construct controlled matrix
98        let controlled = gate_matrix::control_matrix(&base_matrix, self.num_controls as usize);
99        Ok(Cow::Owned(controlled))
100    }
101
102    /// Computes the inverse of the multi-controlled gate.
103    ///
104    /// The inverse of a controlled gate C(U) is C(U†).
105    ///
106    /// # Arguments
107    ///
108    /// * `params` - Parameters for the base gate.
109    ///
110    /// # Returns
111    ///
112    /// `Some((MCGate, parameters))` if the base gate is invertible, `None` otherwise.
113    pub fn inverse(&self, params: &[Parameter]) -> Option<(MCGate, SmallVec<[Parameter; 3]>)> {
114        // The inverse of a controlled gate C(U) is C(U†).
115        let (inv_gate, inv_params) = self.gate.inverse(params)?;
116        Some((
117            Self {
118                num_controls: self.num_controls,
119                gate: inv_gate,
120            },
121            inv_params,
122        ))
123    }
124
125    /// Returns the total number of control qubits.
126    ///
127    /// This includes both the explicitly added controls and any inherent
128    /// controls in the base gate (e.g., CX has 1 inherent control).
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use cqlib_core::circuit::gate::{MCGate, StandardGate};
134    ///
135    /// // Adding 2 controls to X gate
136    /// let gate = MCGate::new(2, StandardGate::X);
137    /// assert_eq!(gate.num_ctrl_qubits(), 2);
138    ///
139    /// // Adding 1 control to CX (which already has 1 control)
140    /// let gate = MCGate::new(1, StandardGate::CX);
141    /// assert_eq!(gate.num_ctrl_qubits(), 2); // 1 added + 1 inherent
142    /// ```
143    pub fn num_ctrl_qubits(&self) -> usize {
144        self.num_controls as usize + self.gate.num_ctrl_qubits()
145    }
146
147    /// Returns the total number of qubits (controls + targets).
148    ///
149    /// # Examples
150    ///
151    /// ```
152    /// use cqlib_core::circuit::gate::{MCGate, StandardGate};
153    ///
154    /// // CCX: 2 controls + 1 target = 3 qubits
155    /// let ccx = MCGate::new(2, StandardGate::X);
156    /// assert_eq!(ccx.num_qubits(), 3);
157    /// ```
158    pub fn num_qubits(&self) -> usize {
159        self.num_controls as usize + self.gate.num_qubits()
160    }
161
162    /// Returns the number of parameters required by the base gate.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// use cqlib_core::circuit::gate::{MCGate, StandardGate};
168    ///
169    /// // Controlled-RX requires 1 parameter
170    /// let crx = MCGate::new(1, StandardGate::RX);
171    /// assert_eq!(crx.num_params(), 1);
172    ///
173    /// // Multi-controlled H requires no parameters
174    /// let much = MCGate::new(2, StandardGate::H);
175    /// assert_eq!(much.num_params(), 0);
176    /// ```
177    pub fn num_params(&self) -> usize {
178        self.gate.num_params()
179    }
180
181    /// Returns a reference to the base gate.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use cqlib_core::circuit::gate::{MCGate, StandardGate};
187    ///
188    /// let gate = MCGate::new(2, StandardGate::Z);
189    /// assert_eq!(*gate.base_gate(), StandardGate::Z);
190    /// ```
191    pub fn base_gate(&self) -> &StandardGate {
192        &self.gate
193    }
194
195    /// Returns the stable operation name used by circuit APIs and statistics.
196    pub fn name(&self) -> String {
197        if self.num_controls == 0 {
198            self.gate.name().to_string()
199        } else {
200            format!("C{}-{}", self.num_controls, self.gate.name())
201        }
202    }
203}
204
205impl fmt::Display for MCGate {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        f.write_str(&self.name())
208    }
209}