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}