Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
268234a
Add ControlPatternSimplification transpiler pass skeleton
Nov 18, 2025
81fa801
Implements foundational components for the ControlPatternSimplification
Nov 18, 2025
853bb6e
Implements gate detection and grouping logic for ControlPatternSimpli…
Nov 18, 2025
e46318e
Implement the boolean logic
Nov 19, 2025
196907a
Add test file
Nov 19, 2025
040f986
Added case that merge while the parameters are different
Nov 19, 2025
41977e3
Add test for identical control patterns with different parameters
Nov 19, 2025
d35c894
Remove sympy dependency
Nov 19, 2025
cdd746c
Add more tests
Nov 19, 2025
d99d3ff
Implementing xor pairs trick
Nov 20, 2025
6bb3f36
Remove invalid test logic
Nov 20, 2025
a8acafa
Pass advanced tests
Nov 20, 2025
40d1da8
Add releasenotes
Nov 20, 2025
7d23f36
Remove sympy dependency
Nov 25, 2025
61eac4b
Reformating with black
Nov 25, 2025
2b3d095
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Nov 26, 2025
98b20ef
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Nov 27, 2025
a30dc12
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Nov 27, 2025
9ab1389
Add alexander unittest
Nov 28, 2025
31d3120
Remove redundant code lines
Nov 28, 2025
793d2b2
Add support for unconditional simplified gates
Nov 30, 2025
5133d8e
Add more code reduction opportunities
Nov 30, 2025
a64b61b
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Dec 1, 2025
7ac9786
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Dec 3, 2025
6ff6319
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Dec 4, 2025
a3ba655
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Dec 4, 2025
d855fb1
Merge branch 'main' into feature/control-pattern-optimization
Mostafa-Atallah2020 Dec 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
CommutativeInverseCancellation
ConsolidateBlocks
ContractIdleWiresInControlFlow
ControlPatternSimplification
ElidePermutations
HoareOptimizer
InverseCancellation
Expand Down Expand Up @@ -231,6 +232,7 @@
from .optimization import CommutativeInverseCancellation
from .optimization import ConsolidateBlocks
from .optimization import ContractIdleWiresInControlFlow
from .optimization import ControlPatternSimplification
from .optimization import ElidePermutations
from .optimization import HoareOptimizer
from .optimization import InverseCancellation
Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/optimization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
from .contract_idle_wires_in_control_flow import ContractIdleWiresInControlFlow
from .optimize_clifford_t import OptimizeCliffordT
from .litinski_transformation import LitinskiTransformation
from .control_pattern_simplification import ControlPatternSimplification
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Transpiler pass for simplifying multi-controlled gates with complementary control patterns."""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.dagcircuit import DAGCircuit
from qiskit.utils import optionals as _optionals


@_optionals.HAS_SYMPY.require_in_instance
class ControlPatternSimplification(TransformationPass):
"""Simplify multi-controlled gates using Boolean algebraic pattern matching.

This pass detects consecutive multi-controlled gates with identical base operations,
target qubits, and parameters (e.g., rotation angles) but different control patterns.
It then applies Boolean algebraic simplification to reduce gate counts.

**Supported Gate Types:**

The optimization works for any parametric controlled gate where the same parameter
value is used across multiple gates, including:

- Multi-controlled rotation gates: MCRX, MCRY, MCRZ
- Multi-controlled phase gates: MCRZ, MCPhase
- Any custom controlled gates with identical parameters

**Optimization Techniques:**

1. **Complementary patterns**: Patterns like ['11', '01'] represent
``(q0 ∧ q1) ∨ (q0 ∧ ¬q1) = q0``, reducing 2 multi-controlled gates to 1 single-controlled gate.

2. **Subset patterns**: Patterns like ['111', '110'] simplify via
``(q0 ∧ q1 ∧ q2) ∨ (q0 ∧ q1 ∧ ¬q2) = (q0 ∧ q1)``,
reducing the number of control qubits.

3. **XOR pairs**: Patterns like ['110', '101'] satisfy ``q1 ⊕ q2 = 1`` and can be
optimized using CNOT gates, reducing 2 multi-controlled gates to 1 multi-controlled gate + 2 CNOTs.

4. **Complete partitions**: Patterns like ['00','01','10','11'] → unconditional gates.

**Example:**

.. code-block:: python

from qiskit import QuantumCircuit
from qiskit.circuit.library import RXGate, RYGate, RZGate
from qiskit.transpiler.passes import ControlPatternSimplification

# Works with any rotation gate (RX, RY, RZ, etc.)
theta = np.pi / 4

# Example with RX gates
qc = QuantumCircuit(3)
qc.append(RXGate(theta).control(2, ctrl_state='11'), [0, 1, 2])
qc.append(RXGate(theta).control(2, ctrl_state='01'), [0, 1, 2])

# Apply optimization
pass_ = ControlPatternSimplification()
optimized_qc = pass_(qc)

# Result: Single CRX gate controlled by q0

# Also works with RY, RZ, Phase, and other parametric gates
qc2 = QuantumCircuit(3)
qc2.append(RYGate(theta).control(2, ctrl_state='11'), [0, 1, 2])
qc2.append(RYGate(theta).control(2, ctrl_state='01'), [0, 1, 2])
optimized_qc2 = pass_(qc2) # Same optimization applied

**References:**

- Atallah et al., "Graph Matching Trotterization for Continuous Time Quantum Walk
Circuit Simulation", Proceedings of IEEE Quantum Computing and Engineering (QCE) 2025.
- Gonzalez et al., "Efficient sparse state preparation via quantum walks",
npj Quantum Information (2025).
- Amy et al., "Fast synthesis of depth-optimal quantum circuits", IEEE TCAD 32.6 (2013).
- Shende & Markov, "On the CNOT-cost of TOFFOLI gates", arXiv:0803.2316 (2008).
- Barenco et al., "Elementary gates for quantum computation", Phys. Rev. A 52.5 (1995).

.. note::
This pass requires the optional SymPy library for Boolean expression simplification.
Install with: ``pip install sympy``
"""

def __init__(self, tolerance=1e-10):
"""Initialize the control pattern simplification pass.

Args:
tolerance (float): Numerical tolerance for comparing gate parameters.
Default is 1e-10.

Raises:
MissingOptionalLibraryError: if SymPy is not installed.
"""
super().__init__()
self.tolerance = tolerance

def run(self, dag: DAGCircuit) -> DAGCircuit:
"""Run the ControlPatternSimplification pass on a DAGCircuit.

Args:
dag: The DAG to be optimized.

Returns:
DAGCircuit: The optimized DAG with simplified control patterns.
"""
# TODO: Implement the optimization logic
# 1. Identify runs of consecutive multi-controlled gates
# 2. Group gates with same base operation, target, and parameters
# (works for any parametric gate: RX, RY, RZ, Phase, etc.)
# 3. Extract control patterns from ctrl_state
# 4. Apply Boolean simplification using SymPy
# 5. Detect XOR patterns for CNOT tricks
# 6. Generate optimized circuit with reduced gate count
# 7. Replace original gates with optimized version

return dag
93 changes: 93 additions & 0 deletions test/python/transpiler/test_control_pattern_simplification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test the ControlPatternSimplification pass."""

import unittest
import numpy as np

from qiskit import QuantumCircuit
from qiskit.circuit.library import RXGate, RYGate, RZGate
from qiskit.transpiler.passes import ControlPatternSimplification
from qiskit.quantum_info import Statevector, state_fidelity
from test import QiskitTestCase # pylint: disable=wrong-import-order
from qiskit.utils import optionals


class TestControlPatternSimplification(QiskitTestCase):
"""Tests for ControlPatternSimplification transpiler pass."""

@unittest.skipUnless(optionals.HAS_SYMPY, "SymPy required for this test")
def test_complementary_patterns_rx(self):
"""Test complementary control patterns with RX gates ('11' and '01' -> single control on q0)."""
# TODO: Implement test
# Expected: 2 MCRX gates -> 1 CRX gate
theta = np.pi / 4
qc = QuantumCircuit(3)
qc.append(RXGate(theta).control(2, ctrl_state='11'), [0, 1, 2])
qc.append(RXGate(theta).control(2, ctrl_state='01'), [0, 1, 2])

# For now, just test that the pass can be instantiated
pass_ = ControlPatternSimplification()
# optimized = pass_(qc)
# self.assertLess(optimized.num_nonlocal_gates(), qc.num_nonlocal_gates())

@unittest.skipUnless(optionals.HAS_SYMPY, "SymPy required for this test")
def test_complementary_patterns_ry(self):
"""Test complementary control patterns with RY gates."""
# TODO: Implement test - same optimization should work for RY
theta = np.pi / 4
qc = QuantumCircuit(3)
qc.append(RYGate(theta).control(2, ctrl_state='11'), [0, 1, 2])
qc.append(RYGate(theta).control(2, ctrl_state='01'), [0, 1, 2])

pass_ = ControlPatternSimplification()
# optimized = pass_(qc)

@unittest.skipUnless(optionals.HAS_SYMPY, "SymPy required for this test")
def test_complementary_patterns_rz(self):
"""Test complementary control patterns with RZ gates."""
# TODO: Implement test - same optimization should work for RZ
theta = np.pi / 4
qc = QuantumCircuit(3)
qc.append(RZGate(theta).control(2, ctrl_state='11'), [0, 1, 2])
qc.append(RZGate(theta).control(2, ctrl_state='01'), [0, 1, 2])

pass_ = ControlPatternSimplification()
# optimized = pass_(qc)

@unittest.skipUnless(optionals.HAS_SYMPY, "SymPy required for this test")
def test_subset_patterns(self):
"""Test subset control patterns ('111' and '110' -> reduce control count)."""
# TODO: Implement test
pass

@unittest.skipUnless(optionals.HAS_SYMPY, "SymPy required for this test")
def test_xor_patterns(self):
"""Test XOR control patterns ('110' and '101' -> CNOT optimization)."""
# TODO: Implement test
pass

@unittest.skipUnless(optionals.HAS_SYMPY, "SymPy required for this test")
def test_state_equivalence(self):
"""Test that optimized circuit maintains state equivalence."""
# TODO: Implement comprehensive fidelity test
pass

def test_pass_without_sympy(self):
"""Test that the pass raises appropriate error without SymPy."""
# TODO: Test optional dependency handling
pass


if __name__ == "__main__":
unittest.main()