mirror of https://github.com/Qiskit/qiskit.git
702 lines
41 KiB
Python
702 lines
41 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2017, 2019.
|
|
#
|
|
# 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 HoareOptimizer pass"""
|
|
|
|
import unittest
|
|
from numpy import pi
|
|
|
|
from qiskit.utils import optionals
|
|
from qiskit.transpiler.passes import HoareOptimizer
|
|
from qiskit.converters import circuit_to_dag
|
|
from qiskit import QuantumCircuit
|
|
from qiskit.circuit.library import XGate, RZGate, CSwapGate, SwapGate
|
|
from qiskit.dagcircuit import DAGOpNode
|
|
from qiskit.quantum_info import Statevector
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
@unittest.skipUnless(optionals.HAS_Z3, "z3-solver needs to be installed to run these tests")
|
|
class TestHoareOptimizer(QiskitTestCase):
|
|
"""Test the HoareOptimizer pass"""
|
|
|
|
def test_phasegate_removal(self):
|
|
"""Should remove the phase on a classical state,
|
|
but not on a superposition state.
|
|
"""
|
|
|
|
# ┌───┐
|
|
# q_0: ┤ Z ├──────
|
|
# ├───┤┌───┐
|
|
# q_1:─┤ H ├┤ Z ├─
|
|
# └───┘└───┘
|
|
circuit = QuantumCircuit(3)
|
|
circuit.z(0)
|
|
circuit.h(1)
|
|
circuit.z(1)
|
|
|
|
# q_0: ───────────
|
|
# ┌───┐┌───┐
|
|
# q_1:─┤ H ├┤ Z ├─
|
|
# └───┘└───┘
|
|
expected = QuantumCircuit(3)
|
|
expected.h(1)
|
|
expected.z(1)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=0)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_cswap_removal(self):
|
|
"""Should remove Fredkin gates because the optimizer
|
|
can deduce the targets are in the same state
|
|
"""
|
|
|
|
# ┌───┐┌───┐ ┌───┐ ┌───┐ ┌───┐
|
|
# q_0: ┤ X ├┤ X ├──■──┤ X ├──■──┤ X ├──■────■──┤ X ├─────────────────────────────────
|
|
# └───┘└─┬─┘┌─┴─┐└─┬─┘ │ └─┬─┘┌─┴─┐ │ └─┬─┘
|
|
# q_1: ───────┼──┤ X ├──■────┼────┼──┤ X ├──┼────■───■──■──■──■─────■─────■──────────
|
|
# │ └─┬─┘ ┌─┴─┐ │ └─┬─┘┌─┴─┐ │ │ │ │ │ │ │
|
|
# q_2: ───────┼────┼───────┤ X ├──■────┼──┤ X ├──■───┼──┼──┼──┼──■──┼──■──┼──■──■──■─
|
|
# ┌───┐ │ │ └─┬─┘ │ └─┬─┘ │ │ │ │ │ │ │ │ │ │ │
|
|
# q_3: ┤ H ├──■────┼─────────┼─────────┼────┼────────┼──┼──X──X──┼──┼──X──┼──┼──X──┼─
|
|
# ├───┤ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
|
|
# q_4: ┤ H ├───────■─────────┼─────────┼────┼────────┼──┼──┼──X──┼──X──┼──┼──X──┼──X─
|
|
# ├───┤ │ │ │ │ │ │ │ │ │ │ │ │ │
|
|
# q_5: ┤ H ├─────────────────■─────────┼────┼────────┼──┼──┼─────┼──X──┼──X──┼──X──┼─
|
|
# ├───┤ │ │ │ │ │ │ │ │ │ │
|
|
# q_6: ┤ H ├───────────────────────────■────■────────┼──┼──┼─────┼─────┼──X──┼─────X─
|
|
# └───┘ │ │ │ │ │ │
|
|
# q_7: ──────────────────────────────────────────────X──┼──┼─────X─────┼─────┼───────
|
|
# │ │ │ │ │ │
|
|
# q_8: ──────────────────────────────────────────────X──X──┼─────┼─────X─────┼───────
|
|
# │ │ │ │
|
|
# q_9: ─────────────────────────────────────────────────X──X─────X───────────X───────
|
|
circuit = QuantumCircuit(10)
|
|
# prep
|
|
circuit.x(0)
|
|
circuit.h(3)
|
|
circuit.h(4)
|
|
circuit.h(5)
|
|
circuit.h(6)
|
|
# find first non-zero bit of reg(3-6), store position in reg(1-2)
|
|
circuit.cx(3, 0)
|
|
circuit.ccx(0, 4, 1)
|
|
circuit.cx(1, 0)
|
|
circuit.ccx(0, 5, 2)
|
|
circuit.cx(2, 0)
|
|
circuit.ccx(0, 6, 1)
|
|
circuit.ccx(0, 6, 2)
|
|
circuit.ccx(1, 2, 0)
|
|
# shift circuit
|
|
circuit.cswap(1, 7, 8)
|
|
circuit.cswap(1, 8, 9)
|
|
circuit.cswap(1, 9, 3)
|
|
circuit.cswap(1, 3, 4)
|
|
circuit.cswap(1, 4, 5)
|
|
circuit.cswap(1, 5, 6)
|
|
circuit.cswap(2, 7, 9)
|
|
circuit.cswap(2, 8, 3)
|
|
circuit.cswap(2, 9, 4)
|
|
circuit.cswap(2, 3, 5)
|
|
circuit.cswap(2, 4, 6)
|
|
|
|
# ┌───┐┌───┐ ┌───┐ ┌───┐ ┌───┐
|
|
# q_0: ┤ X ├┤ X ├──■──┤ X ├──■──┤ X ├──■────■──┤ X ├───────────────
|
|
# └───┘└─┬─┘┌─┴─┐└─┬─┘ │ └─┬─┘┌─┴─┐ │ └─┬─┘
|
|
# q_1: ───────┼──┤ X ├──■────┼────┼──┤ X ├──┼────■───■──■──■───────
|
|
# │ └─┬─┘ ┌─┴─┐ │ └─┬─┘┌─┴─┐ │ │ │ │
|
|
# q_2: ───────┼────┼───────┤ X ├──■────┼──┤ X ├──■───┼──┼──┼──■──■─
|
|
# ┌───┐ │ │ └─┬─┘ │ └─┬─┘ │ │ │ │ │
|
|
# q_3: ┤ H ├──■────┼─────────┼─────────┼────┼────────X──┼──┼──X──┼─
|
|
# ├───┤ │ │ │ │ │ │ │ │ │
|
|
# q_4: ┤ H ├───────■─────────┼─────────┼────┼────────X──X──┼──┼──X─
|
|
# ├───┤ │ │ │ │ │ │ │
|
|
# q_5: ┤ H ├─────────────────■─────────┼────┼───────────X──X──X──┼─
|
|
# ├───┤ │ │ │ │
|
|
# q_6: ┤ H ├───────────────────────────■────■──────────────X─────X─
|
|
# └───┘
|
|
# q_7: ────────────────────────────────────────────────────────────
|
|
#
|
|
# q_8: ────────────────────────────────────────────────────────────
|
|
#
|
|
# q_9: ────────────────────────────────────────────────────────────
|
|
expected = QuantumCircuit(10)
|
|
# prep
|
|
expected.x(0)
|
|
expected.h(3)
|
|
expected.h(4)
|
|
expected.h(5)
|
|
expected.h(6)
|
|
# find first non-zero bit of reg(3-6), store position in reg(1-2)
|
|
expected.cx(3, 0)
|
|
expected.ccx(0, 4, 1)
|
|
expected.cx(1, 0)
|
|
expected.ccx(0, 5, 2)
|
|
expected.cx(2, 0)
|
|
expected.ccx(0, 6, 1)
|
|
expected.ccx(0, 6, 2)
|
|
expected.ccx(1, 2, 0)
|
|
# optimized shift circuit
|
|
expected.cswap(1, 3, 4)
|
|
expected.cswap(1, 4, 5)
|
|
expected.cswap(1, 5, 6)
|
|
expected.cswap(2, 3, 5)
|
|
expected.cswap(2, 4, 6)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=0)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_lnn_cnot_removal(self):
|
|
"""Should remove some cnots from swaps introduced
|
|
because of linear nearest architecture. Only uses
|
|
single-gate optimization techniques.
|
|
"""
|
|
|
|
# ┌───┐ ┌───┐ »
|
|
# q_0: ┤ H ├──■──┤ X ├──■────────────────────────────────────────────────────»
|
|
# └───┘┌─┴─┐└─┬─┘┌─┴─┐ ┌───┐ »
|
|
# q_1: ─────┤ X ├──■──┤ X ├──■──┤ X ├──■──────────────────────────────────■──»
|
|
# └───┘ └───┘┌─┴─┐└─┬─┘┌─┴─┐ ┌───┐ ┌───┐┌─┴─┐»
|
|
# q_2: ────────────────────┤ X ├──■──┤ X ├──■──┤ X ├──■─────────■──┤ X ├┤ X ├»
|
|
# └───┘ └───┘┌─┴─┐└─┬─┘┌─┴─┐ ┌─┴─┐└─┬─┘└───┘»
|
|
# q_3: ───────────────────────────────────┤ X ├──■──┤ X ├──■──┤ X ├──■───────»
|
|
# └───┘ └───┘┌─┴─┐└───┘ »
|
|
# q_4: ──────────────────────────────────────────────────┤ X ├───────────────»
|
|
# └───┘ »
|
|
# « ┌───┐
|
|
# «q_0: ───────■──┤ X ├
|
|
# « ┌───┐┌─┴─┐└─┬─┘
|
|
# «q_1: ┤ X ├┤ X ├──■──
|
|
# « └─┬─┘└───┘
|
|
# «q_2: ──■────────────
|
|
# «
|
|
# «q_3: ───────────────
|
|
# «
|
|
# «q_4: ───────────────
|
|
circuit = QuantumCircuit(5)
|
|
circuit.h(0)
|
|
for i in range(0, 3):
|
|
circuit.cx(i, i + 1)
|
|
circuit.cx(i + 1, i)
|
|
circuit.cx(i, i + 1)
|
|
circuit.cx(3, 4)
|
|
for i in range(3, 0, -1):
|
|
circuit.cx(i - 1, i)
|
|
circuit.cx(i, i - 1)
|
|
|
|
# ┌───┐ ┌───┐ ┌───┐
|
|
# q_0: ┤ H ├──■──┤ X ├───────────────────────────────────┤ X ├
|
|
# └───┘┌─┴─┐└─┬─┘ ┌───┐ ┌───┐└─┬─┘
|
|
# q_1: ─────┤ X ├──■────■──┤ X ├────────────────────┤ X ├──■──
|
|
# └───┘ ┌─┴─┐└─┬─┘ ┌───┐ ┌───┐└─┬─┘
|
|
# q_2: ───────────────┤ X ├──■────■──┤ X ├─────┤ X ├──■───────
|
|
# └───┘ ┌─┴─┐└─┬─┘ └─┬─┘
|
|
# q_3: ─────────────────────────┤ X ├──■────■────■────────────
|
|
# └───┘ ┌─┴─┐
|
|
# q_4: ───────────────────────────────────┤ X ├───────────────
|
|
# └───┘
|
|
expected = QuantumCircuit(5)
|
|
expected.h(0)
|
|
for i in range(0, 3):
|
|
expected.cx(i, i + 1)
|
|
expected.cx(i + 1, i)
|
|
expected.cx(3, 4)
|
|
for i in range(3, 0, -1):
|
|
expected.cx(i, i - 1)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=0)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_lnncnot_advanced_removal(self):
|
|
"""Should remove all cnots from swaps introduced
|
|
because of linear nearest architecture. This time
|
|
using multi-gate optimization techniques.
|
|
"""
|
|
|
|
# ┌───┐ ┌───┐ »
|
|
# q_0: ┤ H ├──■──┤ X ├──■────────────────────────────────────────────────────»
|
|
# └───┘┌─┴─┐└─┬─┘┌─┴─┐ ┌───┐ »
|
|
# q_1: ─────┤ X ├──■──┤ X ├──■──┤ X ├──■──────────────────────────────────■──»
|
|
# └───┘ └───┘┌─┴─┐└─┬─┘┌─┴─┐ ┌───┐ ┌───┐┌─┴─┐»
|
|
# q_2: ────────────────────┤ X ├──■──┤ X ├──■──┤ X ├──■─────────■──┤ X ├┤ X ├»
|
|
# └───┘ └───┘┌─┴─┐└─┬─┘┌─┴─┐ ┌─┴─┐└─┬─┘└───┘»
|
|
# q_3: ───────────────────────────────────┤ X ├──■──┤ X ├──■──┤ X ├──■───────»
|
|
# └───┘ └───┘┌─┴─┐└───┘ »
|
|
# q_4: ──────────────────────────────────────────────────┤ X ├───────────────»
|
|
# └───┘ »
|
|
# « ┌───┐
|
|
# «q_0: ───────■──┤ X ├
|
|
# « ┌───┐┌─┴─┐└─┬─┘
|
|
# «q_1: ┤ X ├┤ X ├──■──
|
|
# « └─┬─┘└───┘
|
|
# «q_2: ──■────────────
|
|
# «
|
|
# «q_3: ───────────────
|
|
# «
|
|
# «q_4: ───────────────
|
|
circuit = QuantumCircuit(5)
|
|
circuit.h(0)
|
|
for i in range(0, 3):
|
|
circuit.cx(i, i + 1)
|
|
circuit.cx(i + 1, i)
|
|
circuit.cx(i, i + 1)
|
|
circuit.cx(3, 4)
|
|
for i in range(3, 0, -1):
|
|
circuit.cx(i - 1, i)
|
|
circuit.cx(i, i - 1)
|
|
|
|
# ┌───┐
|
|
# q_0: ┤ H ├──■─────────────────
|
|
# └───┘┌─┴─┐
|
|
# q_1: ─────┤ X ├──■────────────
|
|
# └───┘┌─┴─┐
|
|
# q_2: ──────────┤ X ├──■───────
|
|
# └───┘┌─┴─┐
|
|
# q_3: ───────────────┤ X ├──■──
|
|
# └───┘┌─┴─┐
|
|
# q_4: ────────────────────┤ X ├
|
|
# └───┘
|
|
expected = QuantumCircuit(5)
|
|
expected.h(0)
|
|
for i in range(0, 4):
|
|
expected.cx(i, i + 1)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=6)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_successive_identity_removal(self):
|
|
"""Should remove a successive pair of H gates applying
|
|
on the same qubit.
|
|
"""
|
|
circuit = QuantumCircuit(1)
|
|
circuit.h(0)
|
|
circuit.h(0)
|
|
circuit.h(0)
|
|
|
|
expected = QuantumCircuit(1)
|
|
expected.h(0)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=4)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_targetsuccessive_identity_removal(self):
|
|
"""Should remove pair of controlled target successive
|
|
which are the inverse of each other, if they can be
|
|
identified to be executed as a unit (either both or none).
|
|
"""
|
|
|
|
# ┌───┐ ┌───┐┌───┐
|
|
# q_0: ┤ H ├──■──┤ X ├┤ X ├──■──
|
|
# ├───┤ │ └─┬─┘└───┘ │
|
|
# q_1: ┤ H ├──■────■─────────■──
|
|
# ├───┤┌─┴─┐ ┌─┴─┐
|
|
# q_2: ┤ H ├┤ X ├──────────┤ X ├
|
|
# └───┘└───┘ └───┘
|
|
circuit = QuantumCircuit(3)
|
|
circuit.h(0)
|
|
circuit.h(1)
|
|
circuit.h(2)
|
|
circuit.ccx(0, 1, 2)
|
|
circuit.cx(1, 0)
|
|
circuit.x(0)
|
|
circuit.ccx(0, 1, 2)
|
|
|
|
# ┌───┐┌───┐┌───┐
|
|
# q_0: ┤ H ├┤ X ├┤ X ├
|
|
# ├───┤└─┬─┘└───┘
|
|
# q_1: ┤ H ├──■───────
|
|
# ├───┤
|
|
# q_2: ┤ H ├──────────
|
|
# └───┘
|
|
expected = QuantumCircuit(3)
|
|
expected.h(0)
|
|
expected.h(1)
|
|
expected.h(2)
|
|
expected.cx(1, 0)
|
|
expected.x(0)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=4)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_targetsuccessive_identity_advanced_removal(self):
|
|
"""Should remove target successive identity gates
|
|
with DIFFERENT sets of control qubits.
|
|
In this case CCCX(4,5,6,7) & CCX(5,6,7).
|
|
"""
|
|
|
|
# ┌───┐┌───┐ »
|
|
# q_0: ┤ H ├┤ X ├───────■─────────────────────────────■───────────────────■──»
|
|
# ├───┤└─┬─┘ │ │ │ »
|
|
# q_1: ┤ H ├──■─────────■─────────────────────────────■───────────────────■──»
|
|
# ├───┤┌───┐ │ ┌───┐ │ │ »
|
|
# q_2: ┤ H ├┤ X ├───────┼──┤ X ├──■──────────────■────┼───────────────────┼──»
|
|
# ├───┤└─┬─┘ ┌─┴─┐└─┬─┘ │ │ ┌─┴─┐ ┌─┴─┐»
|
|
# q_3: ┤ H ├──■────■──┤ X ├──■────■──────────────■──┤ X ├──■─────────■──┤ X ├»
|
|
# ├───┤┌───┐ │ └───┘ │ ┌───┐ │ └───┘ │ │ └───┘»
|
|
# q_4: ┤ H ├┤ X ├──┼──────────────┼──┤ X ├──■────┼─────────┼─────────┼───────»
|
|
# ├───┤└─┬─┘┌─┴─┐ ┌─┴─┐└─┬─┘ │ ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ »
|
|
# q_5: ┤ H ├──■──┤ X ├──────────┤ X ├──■────■──┤ X ├─────┤ X ├──■──┤ X ├─────»
|
|
# └───┘ └───┘ └───┘ ┌─┴─┐└───┘ └───┘┌─┴─┐├───┤ »
|
|
# q_6: ───────────────────────────────────┤ X ├───────────────┤ X ├┤ X ├─────»
|
|
# └───┘ └───┘└───┘ »
|
|
# q_7: ──────────────────────────────────────────────────────────────────────»
|
|
# »
|
|
# « ┌───┐┌───┐ »
|
|
# «q_0: ──────────────────────■──┤ X ├┤ X ├──■─────────────────────────────■──»
|
|
# « │ └─┬─┘└─┬─┘ │ │ »
|
|
# «q_1: ──────────────────────■────■────■────■─────────────────────────────■──»
|
|
# « ┌───┐ │ │ │ ┌───┐ │ »
|
|
# «q_2: ──■─────────■──┤ X ├──┼─────────┼────┼──┤ X ├──■──────────────■────┼──»
|
|
# « │ │ └─┬─┘┌─┴─┐ │ ┌─┴─┐└─┬─┘ │ │ ┌─┴─┐»
|
|
# «q_3: ──■─────────■────■──┤ X ├───────┼──┤ X ├──■────■──────────────■──┤ X ├»
|
|
# « │ ┌───┐ │ └───┘ │ └───┘ │ │ ┌───┐ │ └───┘»
|
|
# «q_4: ──┼──┤ X ├──┼───────────────────┼─────────┼────┼──┤ X ├──■────┼───────»
|
|
# « ┌─┴─┐└─┬─┘┌─┴─┐ │ │ ┌─┴─┐└─┬─┘ │ ┌─┴─┐ »
|
|
# «q_5: ┤ X ├──■──┤ X ├─────────────────┼─────────┼──┤ X ├──■────■──┤ X ├─────»
|
|
# « └───┘ └───┘ │ │ └───┘ │ │ └───┘ »
|
|
# «q_6: ────────────────────────────────■─────────■─────────■────■────────────»
|
|
# « ┌─┴─┐ »
|
|
# «q_7: ───────────────────────────────────────────────────────┤ X ├──────────»
|
|
# « └───┘ »
|
|
# «
|
|
# «q_0: ───────────────
|
|
# «
|
|
# «q_1: ───────────────
|
|
# « ┌───┐
|
|
# «q_2: ─────┤ X ├─────
|
|
# « └─┬─┘
|
|
# «q_3: ──■────■───────
|
|
# « │ ┌───┐
|
|
# «q_4: ──┼──┤ X ├─────
|
|
# « ┌─┴─┐└─┬─┘
|
|
# «q_5: ┤ X ├──■────■──
|
|
# « └───┘ │
|
|
# «q_6: ────────────■──
|
|
# « ┌─┴─┐
|
|
# «q_7: ──────────┤ X ├
|
|
# « └───┘
|
|
circuit = QuantumCircuit(8)
|
|
circuit.h(0)
|
|
circuit.h(1)
|
|
circuit.h(2)
|
|
circuit.h(3)
|
|
circuit.h(4)
|
|
circuit.h(5)
|
|
for i in range(3):
|
|
circuit.cx(i * 2 + 1, i * 2)
|
|
circuit.cx(3, 5)
|
|
for i in range(2):
|
|
circuit.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
circuit.cx(i * 2 + 3, i * 2 + 2)
|
|
circuit.ccx(4, 5, 6)
|
|
for i in range(1, -1, -1):
|
|
circuit.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
circuit.cx(3, 5)
|
|
circuit.cx(5, 6)
|
|
circuit.cx(3, 5)
|
|
circuit.x(6)
|
|
for i in range(2):
|
|
circuit.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
for i in range(1, -1, -1):
|
|
circuit.cx(i * 2 + 3, i * 2 + 2)
|
|
circuit.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
circuit.cx(1, 0)
|
|
circuit.ccx(6, 1, 0)
|
|
circuit.ccx(0, 1, 3)
|
|
circuit.ccx(6, 3, 2)
|
|
circuit.ccx(2, 3, 5)
|
|
circuit.ccx(6, 5, 4)
|
|
circuit.append(XGate().control(3), [4, 5, 6, 7], [])
|
|
for i in range(1, -1, -1):
|
|
circuit.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
circuit.cx(3, 5)
|
|
for i in range(1, 3):
|
|
circuit.cx(i * 2 + 1, i * 2)
|
|
circuit.ccx(5, 6, 7)
|
|
|
|
# ┌───┐┌───┐ »
|
|
# q_0: ┤ H ├┤ X ├───────■─────────────────────────────■───────────────────■──»
|
|
# ├───┤└─┬─┘ │ │ │ »
|
|
# q_1: ┤ H ├──■─────────■─────────────────────────────■───────────────────■──»
|
|
# ├───┤┌───┐ │ ┌───┐ │ │ »
|
|
# q_2: ┤ H ├┤ X ├───────┼──┤ X ├──■──────────────■────┼───────────────────┼──»
|
|
# ├───┤└─┬─┘ ┌─┴─┐└─┬─┘ │ │ ┌─┴─┐ ┌─┴─┐»
|
|
# q_3: ┤ H ├──■────■──┤ X ├──■────■──────────────■──┤ X ├──■─────────■──┤ X ├»
|
|
# ├───┤┌───┐ │ └───┘ │ ┌───┐ │ └───┘ │ │ └───┘»
|
|
# q_4: ┤ H ├┤ X ├──┼──────────────┼──┤ X ├──■────┼─────────┼─────────┼───────»
|
|
# ├───┤└─┬─┘┌─┴─┐ ┌─┴─┐└─┬─┘ │ ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ »
|
|
# q_5: ┤ H ├──■──┤ X ├──────────┤ X ├──■────■──┤ X ├─────┤ X ├──■──┤ X ├─────»
|
|
# └───┘ └───┘ └───┘ ┌─┴─┐└───┘ └───┘┌─┴─┐├───┤ »
|
|
# q_6: ───────────────────────────────────┤ X ├───────────────┤ X ├┤ X ├─────»
|
|
# └───┘ └───┘└───┘ »
|
|
# q_7: ──────────────────────────────────────────────────────────────────────»
|
|
# »
|
|
# « ┌───┐┌───┐ »
|
|
# «q_0: ──────────────────────■──┤ X ├┤ X ├──■────────────────────────■───────»
|
|
# « │ └─┬─┘└─┬─┘ │ │ »
|
|
# «q_1: ──────────────────────■────■────■────■────────────────────────■───────»
|
|
# « ┌───┐ │ │ │ ┌───┐ │ »
|
|
# «q_2: ──■─────────■──┤ X ├──┼─────────┼────┼──┤ X ├──■─────────■────┼───────»
|
|
# « │ │ └─┬─┘┌─┴─┐ │ ┌─┴─┐└─┬─┘ │ │ ┌─┴─┐ »
|
|
# «q_3: ──■─────────■────■──┤ X ├───────┼──┤ X ├──■────■─────────■──┤ X ├──■──»
|
|
# « │ ┌───┐ │ └───┘ │ └───┘ │ │ ┌───┐ │ └───┘ │ »
|
|
# «q_4: ──┼──┤ X ├──┼───────────────────┼─────────┼────┼──┤ X ├──┼─────────┼──»
|
|
# « ┌─┴─┐└─┬─┘┌─┴─┐ │ │ ┌─┴─┐└─┬─┘┌─┴─┐ ┌─┴─┐»
|
|
# «q_5: ┤ X ├──■──┤ X ├─────────────────┼─────────┼──┤ X ├──■──┤ X ├─────┤ X ├»
|
|
# « └───┘ └───┘ │ │ └───┘ │ └───┘ └───┘»
|
|
# «q_6: ────────────────────────────────■─────────■─────────■─────────────────»
|
|
# « »
|
|
# «q_7: ──────────────────────────────────────────────────────────────────────»
|
|
# « »
|
|
# «
|
|
# «q_0: ─────
|
|
# «
|
|
# «q_1: ─────
|
|
# « ┌───┐
|
|
# «q_2: ┤ X ├
|
|
# « └─┬─┘
|
|
# «q_3: ──■──
|
|
# « ┌───┐
|
|
# «q_4: ┤ X ├
|
|
# « └─┬─┘
|
|
# «q_5: ──■──
|
|
# «
|
|
# «q_6: ─────
|
|
# «
|
|
# «q_7: ─────
|
|
# «
|
|
expected = QuantumCircuit(8)
|
|
expected.h(0)
|
|
expected.h(1)
|
|
expected.h(2)
|
|
expected.h(3)
|
|
expected.h(4)
|
|
expected.h(5)
|
|
for i in range(3):
|
|
expected.cx(i * 2 + 1, i * 2)
|
|
expected.cx(3, 5)
|
|
for i in range(2):
|
|
expected.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
expected.cx(i * 2 + 3, i * 2 + 2)
|
|
expected.ccx(4, 5, 6)
|
|
for i in range(1, -1, -1):
|
|
expected.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
expected.cx(3, 5)
|
|
expected.cx(5, 6)
|
|
expected.cx(3, 5)
|
|
expected.x(6)
|
|
for i in range(2):
|
|
expected.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
for i in range(1, -1, -1):
|
|
expected.cx(i * 2 + 3, i * 2 + 2)
|
|
expected.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
expected.cx(1, 0)
|
|
expected.ccx(6, 1, 0)
|
|
expected.ccx(0, 1, 3)
|
|
expected.ccx(6, 3, 2)
|
|
expected.ccx(2, 3, 5)
|
|
expected.ccx(6, 5, 4)
|
|
for i in range(1, -1, -1):
|
|
expected.ccx(i * 2, i * 2 + 1, i * 2 + 3)
|
|
expected.cx(3, 5)
|
|
for i in range(1, 3):
|
|
expected.cx(i * 2 + 1, i * 2)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=5)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_control_removal(self):
|
|
"""Should replace CX by X."""
|
|
|
|
# ┌───┐
|
|
# q_0: ┤ X ├──■──
|
|
# └───┘┌─┴─┐
|
|
# q_1: ─────┤ X ├
|
|
# └───┘
|
|
circuit = QuantumCircuit(2)
|
|
circuit.x(0)
|
|
circuit.cx(0, 1)
|
|
|
|
# ┌───┐
|
|
# q_0: ┤ X ├
|
|
# ├───┤
|
|
# q_1: ┤ X ├
|
|
# └───┘
|
|
expected = QuantumCircuit(2)
|
|
expected.x(0)
|
|
expected.x(1)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=5)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
# Should replace CZ by Z
|
|
#
|
|
# ┌───┐ ┌───┐
|
|
# q_0: ┤ H ├─■─┤ H ├
|
|
# ├───┤ │ └───┘
|
|
# q_1: ┤ X ├─■──────
|
|
# └───┘
|
|
circuit = QuantumCircuit(2)
|
|
circuit.h(0)
|
|
circuit.x(1)
|
|
circuit.cz(0, 1)
|
|
circuit.h(0)
|
|
|
|
# ┌───┐┌───┐┌───┐
|
|
# q_0: ┤ H ├┤ Z ├┤ H ├
|
|
# ├───┤└───┘└───┘
|
|
# q_1: ┤ X ├──────────
|
|
# └───┘
|
|
expected = QuantumCircuit(2)
|
|
expected.h(0)
|
|
expected.x(1)
|
|
expected.z(0)
|
|
expected.h(0)
|
|
|
|
stv = Statevector.from_label("0" * circuit.num_qubits)
|
|
self.assertEqual(stv & circuit, stv & expected)
|
|
|
|
pass_ = HoareOptimizer(size=5)
|
|
result = pass_.run(circuit_to_dag(circuit))
|
|
|
|
self.assertEqual(result, circuit_to_dag(expected))
|
|
|
|
def test_is_identity(self):
|
|
"""The is_identity function determines whether a pair of gates
|
|
forms the identity, when ignoring control qubits.
|
|
"""
|
|
seq = [DAGOpNode(op=XGate().control()), DAGOpNode(op=XGate().control(2))]
|
|
self.assertTrue(HoareOptimizer()._is_identity(seq))
|
|
|
|
seq = [
|
|
DAGOpNode(op=RZGate(-pi / 2).control()),
|
|
DAGOpNode(op=RZGate(pi / 2).control(2)),
|
|
]
|
|
self.assertTrue(HoareOptimizer()._is_identity(seq))
|
|
|
|
seq = [DAGOpNode(op=CSwapGate()), DAGOpNode(op=SwapGate())]
|
|
self.assertTrue(HoareOptimizer()._is_identity(seq))
|
|
|
|
def test_multiple_pass(self):
|
|
"""Verify that multiple pass can be run
|
|
with the same Hoare instance.
|
|
"""
|
|
|
|
# ┌───┐┌───┐
|
|
# q_0:─┤ H ├┤ Z ├─
|
|
# ├───┤└───┘
|
|
# q_1: ┤ Z ├──────
|
|
# └───┘
|
|
circuit1 = QuantumCircuit(2)
|
|
circuit1.z(0)
|
|
circuit1.h(1)
|
|
circuit1.z(1)
|
|
|
|
circuit2 = QuantumCircuit(2)
|
|
circuit2.z(1)
|
|
circuit2.h(0)
|
|
circuit2.z(0)
|
|
|
|
# ┌───┐┌───┐
|
|
# q_0:─┤ H ├┤ Z ├─
|
|
# └───┘└───┘
|
|
# q_1: ───────────
|
|
expected = QuantumCircuit(2)
|
|
expected.h(0)
|
|
expected.z(0)
|
|
|
|
pass_ = HoareOptimizer()
|
|
|
|
pass_.run(circuit_to_dag(circuit1))
|
|
result2 = pass_.run(circuit_to_dag(circuit2))
|
|
|
|
self.assertEqual(result2, circuit_to_dag(expected))
|
|
|
|
def test_remove_control_then_identity(self):
|
|
"""This first simplifies a gate by removing its control, then removes the
|
|
simplified gate by canceling it with another gate.
|
|
See: https://github.com/Qiskit/qiskit-terra/issues/13079
|
|
"""
|
|
# ┌───┐┌───┐┌───┐
|
|
# q_0: ┤ X ├┤ X ├┤ X ├
|
|
# └─┬─┘└───┘└─┬─┘
|
|
# q_1: ──■─────────┼──
|
|
# ┌───┐ │
|
|
# q_2: ┤ X ├───────■──
|
|
# └───┘
|
|
circuit = QuantumCircuit(3)
|
|
circuit.cx(1, 0)
|
|
circuit.x(2)
|
|
circuit.x(0)
|
|
circuit.cx(2, 0)
|
|
|
|
simplified = HoareOptimizer()(circuit)
|
|
|
|
# The CX(1, 0) gate is removed as the control qubit q_1 is initially 0.
|
|
# The CX(2, 0) gate is first replaced by X(0) gate as the control qubit q_2 is at 1,
|
|
# then the two X(0) gates are removed.
|
|
#
|
|
# q_0: ─────
|
|
#
|
|
# q_1: ─────
|
|
# ┌───┐
|
|
# q_2: ┤ X ├
|
|
# └───┘
|
|
expected = QuantumCircuit(3)
|
|
expected.x(2)
|
|
|
|
self.assertEqual(simplified, expected)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|