Skip to content

Commit 6b9f15b

Browse files
Porting synth_c3x to Rust
This is the beginning of porting of (some) of the MCX synthesis methods to Rust. Here it also serves as a very simple example of using SynthesisData.
1 parent f245ff7 commit 6b9f15b

File tree

5 files changed

+120
-32
lines changed

5 files changed

+120
-32
lines changed

crates/accelerate/src/synthesis/common/mod.rs

+17
Original file line numberDiff line numberDiff line change
@@ -122,23 +122,40 @@ impl<'a> SynthesisData<'a> {
122122
}
123123

124124
// Convenience functions
125+
126+
/// Appends XGate to the circuit.
125127
#[inline]
126128
pub fn x(&mut self, q: u32) {
127129
self.push_standard_gate(StandardGate::XGate, &[], &[q]);
128130
}
129131

132+
/// Appends HGate to the circuit.
130133
#[inline]
131134
pub fn h(&mut self, q: u32) {
132135
self.push_standard_gate(StandardGate::HGate, &[], &[q]);
133136
}
134137

138+
/// Appends TGate to the circuit.
135139
#[inline]
136140
pub fn t(&mut self, q: u32) {
137141
self.push_standard_gate(StandardGate::TGate, &[], &[q]);
138142
}
139143

144+
/// Appends TdgGate to the circuit.
140145
#[inline]
141146
pub fn tdg(&mut self, q: u32) {
142147
self.push_standard_gate(StandardGate::TdgGate, &[], &[q]);
143148
}
149+
150+
/// Appends PhaseGate to the circuit.
151+
#[inline]
152+
pub fn p(&mut self, theta: f64, q: u32) {
153+
self.push_standard_gate(StandardGate::PhaseGate, &[Param::Float(theta)], &[q]);
154+
}
155+
156+
/// Appends CXGate to the circuit.
157+
#[inline]
158+
pub fn cx(&mut self, q1: u32, q2: u32) {
159+
self.push_standard_gate(StandardGate::CXGate, &[], &[q1, q2]);
160+
}
144161
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// This code is part of Qiskit.
2+
//
3+
// (C) Copyright IBM 2025
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+
use crate::synthesis::common::SynthesisData;
14+
15+
use std::f64::consts::PI;
16+
17+
// const PI2: f64 = PI / 2.;
18+
// const PI4: f64 = PI / 4.;
19+
const PI8: f64 = PI / 8.;
20+
21+
/// Efficient synthesis for 3-controlled X-gate.
22+
pub fn c3x<'a>() -> SynthesisData<'a> {
23+
let mut qc = SynthesisData::new(4);
24+
qc.h(3);
25+
qc.p(PI8, 0);
26+
qc.p(PI8, 1);
27+
qc.p(PI8, 2);
28+
qc.p(PI8, 3);
29+
qc.cx(0, 1);
30+
qc.p(-PI8, 1);
31+
qc.cx(0, 1);
32+
qc.cx(1, 2);
33+
qc.p(-PI8, 2);
34+
qc.cx(0, 2);
35+
qc.p(PI8, 2);
36+
qc.cx(1, 2);
37+
qc.p(-PI8, 2);
38+
qc.cx(0, 2);
39+
qc.cx(2, 3);
40+
qc.p(-PI8, 3);
41+
qc.cx(1, 3);
42+
qc.p(PI8, 3);
43+
qc.cx(2, 3);
44+
qc.p(-PI8, 3);
45+
qc.cx(0, 3);
46+
qc.p(PI8, 3);
47+
qc.cx(2, 3);
48+
qc.p(-PI8, 3);
49+
qc.cx(1, 3);
50+
qc.p(PI8, 3);
51+
qc.cx(2, 3);
52+
qc.p(-PI8, 3);
53+
qc.cx(0, 3);
54+
qc.h(3);
55+
qc
56+
}

crates/accelerate/src/synthesis/multi_controlled/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,20 @@
1010
// copyright notice, and modified files need to carry a notice indicating
1111
// that they have been altered from the originals.
1212

13+
use mcx::c3x;
1314
use pyo3::prelude::*;
15+
use qiskit_circuit::circuit_data::CircuitData;
1416

1517
mod mcmt;
18+
mod mcx;
19+
20+
#[pyfunction]
21+
pub fn py_c3x(py: Python) -> PyResult<CircuitData> {
22+
c3x().to_circuit_data(py)
23+
}
1624

1725
pub fn multi_controlled(m: &Bound<PyModule>) -> PyResult<()> {
26+
m.add_function(wrap_pyfunction!(py_c3x, m)?)?;
1827
m.add_function(wrap_pyfunction!(mcmt::mcmt_v_chain, m)?)?;
1928
Ok(())
2029
}

qiskit/synthesis/multi_controlled/mcx_synthesis.py

+4-32
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
C3SXGate,
2626
)
2727

28+
from qiskit._accelerate.synthesis.multi_controlled import py_c3x
29+
2830

2931
def synth_mcx_n_dirty_i15(
3032
num_ctrl_qubits: int,
@@ -302,38 +304,8 @@ def synth_mcx_noaux_v24(num_ctrl_qubits: int) -> QuantumCircuit:
302304

303305
def synth_c3x() -> QuantumCircuit:
304306
"""Efficient synthesis of 3-controlled X-gate."""
305-
306-
q = QuantumRegister(4, name="q")
307-
qc = QuantumCircuit(q, name="mcx")
308-
qc.h(3)
309-
qc.p(np.pi / 8, [0, 1, 2, 3])
310-
qc.cx(0, 1)
311-
qc.p(-np.pi / 8, 1)
312-
qc.cx(0, 1)
313-
qc.cx(1, 2)
314-
qc.p(-np.pi / 8, 2)
315-
qc.cx(0, 2)
316-
qc.p(np.pi / 8, 2)
317-
qc.cx(1, 2)
318-
qc.p(-np.pi / 8, 2)
319-
qc.cx(0, 2)
320-
qc.cx(2, 3)
321-
qc.p(-np.pi / 8, 3)
322-
qc.cx(1, 3)
323-
qc.p(np.pi / 8, 3)
324-
qc.cx(2, 3)
325-
qc.p(-np.pi / 8, 3)
326-
qc.cx(0, 3)
327-
qc.p(np.pi / 8, 3)
328-
qc.cx(2, 3)
329-
qc.p(-np.pi / 8, 3)
330-
qc.cx(1, 3)
331-
qc.p(np.pi / 8, 3)
332-
qc.cx(2, 3)
333-
qc.p(-np.pi / 8, 3)
334-
qc.cx(0, 3)
335-
qc.h(3)
336-
return qc
307+
circ = QuantumCircuit._from_circuit_data(py_c3x())
308+
return circ
337309

338310

339311
def synth_c4x() -> QuantumCircuit:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
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+
"""Test MCX synthesis."""
14+
15+
import unittest
16+
17+
from qiskit.quantum_info import Operator
18+
from qiskit.circuit.library import C3XGate
19+
from qiskit.synthesis.multi_controlled import synth_c3x
20+
21+
from test import QiskitTestCase # pylint: disable=wrong-import-order
22+
23+
24+
class TestMCXSynth(QiskitTestCase):
25+
"""Test MCX synthesis methods."""
26+
27+
def test_c3x(self):
28+
"""Test the default synthesis method for C3XGate."""
29+
# ToDo: it might be nicer to compare with the actual matrix
30+
self.assertEqual(Operator(C3XGate().definition), Operator(synth_c3x()))
31+
32+
33+
if __name__ == "__main__":
34+
unittest.main()

0 commit comments

Comments
 (0)