Skip to content

Commit

Permalink
MCXGate.inverse to return gate of same mode as original gate.
Browse files Browse the repository at this point in the history
  • Loading branch information
kdk committed Sep 3, 2020
1 parent 9e3af40 commit 071f71c
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 1 deletion.
18 changes: 18 additions & 0 deletions qiskit/circuit/library/standard_gates/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,10 @@ def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None, _name='mcx'):
num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state)
self.base_gate = XGate()

def inverse(self):
"""Invert this gate. The MCX is its own inverse."""
return MCXGate(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state)

@staticmethod
def get_num_ancilla_qubits(num_ctrl_qubits, mode='noancilla'):
"""Get the number of required ancilla qubits without instantiating the class.
Expand Down Expand Up @@ -811,6 +815,10 @@ class MCXGrayCode(MCXGate):
def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None):
super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name='mcx_gray')

def inverse(self):
"""Invert this gate. The MCX is its own inverse."""
return MCXGrayCode(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state)

def _define(self):
"""Define the MCX gate using the Gray code."""
# pylint: disable=cyclic-import
Expand Down Expand Up @@ -840,6 +848,10 @@ def get_num_ancilla_qubits(num_ctrl_qubits, mode='recursion'):
"""Get the number of required ancilla qubits."""
return MCXGate.get_num_ancilla_qubits(num_ctrl_qubits, mode)

def inverse(self):
"""Invert this gate. The MCX is its own inverse."""
return MCXRecursive(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state)

def _define(self):
"""Define the MCX gate using recursion."""
# pylint: disable=cyclic-import
Expand Down Expand Up @@ -896,6 +908,12 @@ def __init__(self, num_ctrl_qubits, dirty_ancillas=False, label=None, ctrl_state
super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name='mcx_vchain')
self._dirty_ancillas = dirty_ancillas

def inverse(self):
"""Invert this gate. The MCX is its own inverse."""
return MCXVChain(num_ctrl_qubits=self.num_ctrl_qubits,
dirty_ancillas=self._dirty_ancillas,
ctrl_state=self.ctrl_state)

@staticmethod
def get_num_ancilla_qubits(num_ctrl_qubits, mode='v-chain'):
"""Get the number of required ancilla qubits."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ fixes:
For ``C3XGate`` with a non-zero ``angle``, inverting the gate via
``C3XGate.inverse()`` had previously generated an incorrect inverse gate.
This has been corrected.
- |
The ``MCXGate`` modes have been updated to return a gate of the same mode
when calling ``.inverse()``. This resolves an issue where in some cases,
transpiling a circuit containing the inverse of an ``MCXVChain`` gate would
raise an error.
37 changes: 36 additions & 1 deletion test/python/circuit/test_gate_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ def test_cx_definition(self):
@ddt
class TestStandardGates(QiskitTestCase):
"""Standard Extension Test."""

@unpack
@data(
*inspect.getmembers(
Expand Down Expand Up @@ -152,6 +151,42 @@ def test_definition_parameters(self, class_name, gate_class):
if gate.definition is not None:
self.assertEqual(gate.definition.parameters, set(param_vector))

@unpack
@data(
*inspect.getmembers(
standard_gates,
predicate=lambda value: (inspect.isclass(value)
and issubclass(value, Gate)))
)
def test_inverse(self, class_name, gate_class):
"""Verify self-inverse pair yield identity for all standard gates."""

free_params = _get_free_params(gate_class)
n_params = len(free_params)
float_vector = [0.1 + 0.1*i for i in range(n_params)]

if class_name in ('MCPhaseGate', 'MCU1Gate'):
float_vector = float_vector[:-1]
gate = gate_class(*float_vector, num_ctrl_qubits=2)
elif class_name in ('MCXGate', 'MCXGrayCode', 'MCXRecursive', 'MCXVChain'):
num_ctrl_qubits = 3
float_vector = float_vector[:-1]
gate = gate_class(num_ctrl_qubits, *float_vector)
elif class_name == 'MSGate':
num_qubits = 3
float_vector = float_vector[:-1]
gate = gate_class(num_qubits, *float_vector)
else:
gate = gate_class(*float_vector)

from qiskit.quantum_info.operators.predicates import is_identity_matrix

self.assertTrue(is_identity_matrix(Operator(gate).dot(gate.inverse()).data))

if gate.definition is not None:
self.assertTrue(is_identity_matrix(Operator(gate).dot(gate.definition.inverse()).data))
self.assertTrue(is_identity_matrix(Operator(gate).dot(gate.inverse().definition).data))


class TestGateEquivalenceEqual(QiskitTestCase):
"""Test the decomposition of a gate in terms of other gates
Expand Down

0 comments on commit 071f71c

Please sign in to comment.