Repository: Qiskit/qiskit

Issue:
Inverse of MCX gates generated without qubits for ancillae
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: master @ 7d79ab0
- **Python version**: 3.5
- **Operating system**: osx

### What is the current behavior?

Inverting an mcx gate (with ancilla) falls back to `ControlledGate.inverse` which builds the definition of the inverse gate without ancilla. This leads to an error when transpiling because the inverse gate uses fewer qubits than the original.

### Steps to reproduce the problem

```
>>> import qiskit as qk
>>> qc = qk.QuantumCircuit(5)
>>> qc.mcx([0,1,2],4,[3], mode='v-chain')
>>> qk.transpile(qc, basis_gates=['u3', 'cx']).count_ops()
OrderedDict([('u3', 16), ('cx', 12)])
>>> qk.transpile(qc.inverse(), basis_gates=['u3', 'cx']).count_ops()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/compiler/transpile.py", line 214, in transpile
    circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args)))
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/tools/parallel.py", line 106, in parallel_map
    return [task(values[0], *task_args, **task_kwargs)]
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/compiler/transpile.py", line 315, in _transpile_circuit
    output_name=transpile_config['output_name'])
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/transpiler/passmanager.py", line 212, in run
    return self._run_single_circuit(circuits, output_name, callback)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/transpiler/passmanager.py", line 275, in _run_single_circuit
    result = running_passmanager.run(circuit, output_name=output_name, callback=callback)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/transpiler/runningpassmanager.py", line 113, in run
    dag = self._do_pass(pass_, dag, passset.options)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/transpiler/runningpassmanager.py", line 143, in _do_pass
    dag = self._run_this_pass(pass_, dag)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/transpiler/runningpassmanager.py", line 155, in _run_this_pass
    new_dag = pass_.run(dag)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/transpiler/passes/basis/unroll_custom_definitions.py", line 88, in run
    dag.substitute_node_with_dag(node, unrolled_dag)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/dagcircuit/dagcircuit.py", line 842, in substitute_node_with_dag
    self._check_wires_list(wires, node)
  File "/Users/kevin.krsulichibm.com/q/qiskit-terra/qiskit/dagcircuit/dagcircuit.py", line 716, in _check_wires_list
    % (wire_tot, len(wires)))
qiskit.dagcircuit.exceptions.DAGCircuitError: 'expected 5 wires, got 4'
```

```
>>> print(MCXVChain(num_ctrl_qubits=3).definition)
     ┌───────┐     ┌───────┐
q_0: ┤0      ├─────┤0      ├
     │       │     │       │
q_1: ┤1      ├─────┤1      ├
     │       │     │       │
q_2: ┤  RCCX ├──■──┤  RCCX ├
     │       │┌─┴─┐│       │
q_3: ┤       ├┤ X ├┤       ├
     │       │└─┬─┘│       │
q_4: ┤2      ├──■──┤2      ├
     └───────┘     └───────┘
>>> print(MCXVChain(num_ctrl_qubits=3).inverse().definition)

q_0: ──■──
       │
q_1: ──■──
       │
q_2: ──■──
     ┌─┴─┐
q_3: ┤ X ├
     └───┘
```
### Suggested solutions

Adding something to `class MCXGate` like 
```
def inverse(self):
    return self.copy()
```



Discussion:

Resolving patch:
diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py
--- a/qiskit/circuit/instruction.py
+++ b/qiskit/circuit/instruction.py
@@ -267,7 +267,20 @@ def inverse(self):
         """
         if self.definition is None:
             raise CircuitError("inverse() not implemented for %s." % self.name)
-        inverse_gate = self.copy(name=self.name + '_dg')
+
+        from qiskit.circuit import QuantumCircuit, Gate  # pylint: disable=cyclic-import
+        if self.num_clbits:
+            inverse_gate = Instruction(name=self.name + '_dg',
+                                       num_qubits=self.num_qubits,
+                                       num_clbits=self.num_clbits,
+                                       params=self.params.copy())
+
+        else:
+            inverse_gate = Gate(name=self.name + '_dg',
+                                num_qubits=self.num_qubits,
+                                params=self.params.copy())
+
+        inverse_gate.definition = QuantumCircuit(*self.definition.qregs, *self.definition.cregs)
         inverse_gate.definition._data = [(inst.inverse(), qargs, cargs)
                                          for inst, qargs, cargs in reversed(self._definition)]
 
diff --git a/qiskit/circuit/library/standard_gates/sx.py b/qiskit/circuit/library/standard_gates/sx.py
--- a/qiskit/circuit/library/standard_gates/sx.py
+++ b/qiskit/circuit/library/standard_gates/sx.py
@@ -76,6 +76,10 @@ def _define(self):
         qc.data = rules
         self.definition = qc
 
+    def inverse(self):
+        """Return inverse SX gate (i.e. SXdg)."""
+        return SXdgGate()
+
     def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
         """Return a (multi-)controlled-SX gate.
 
@@ -150,6 +154,10 @@ def _define(self):
         qc.data = rules
         self.definition = qc
 
+    def inverse(self):
+        """Return inverse SXdg gate (i.e. SX)."""
+        return SXGate()
+
     def to_matrix(self):
         """Return a numpy.array for the SXdg gate."""
         return numpy.array([[1 - 1j, 1 + 1j],
diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py
--- a/qiskit/circuit/library/standard_gates/x.py
+++ b/qiskit/circuit/library/standard_gates/x.py
@@ -515,7 +515,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
 
     def inverse(self):
         """Invert this gate. The C3X is its own inverse."""
-        return C3XGate(angle=self._angle, ctrl_state=self.ctrl_state)
+        return C3XGate(angle=-self._angle, ctrl_state=self.ctrl_state)
 
     # This matrix is only correct if the angle is pi/4
     # def to_matrix(self):
@@ -747,6 +747,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, 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.
@@ -806,6 +810,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
@@ -835,6 +843,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
@@ -891,6 +903,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."""


Test patch:
diff --git a/test/python/circuit/test_gate_definitions.py b/test/python/circuit/test_gate_definitions.py
--- a/test/python/circuit/test_gate_definitions.py
+++ b/test/python/circuit/test_gate_definitions.py
@@ -120,7 +120,6 @@ def test_cx_definition(self):
 @ddt
 class TestStandardGates(QiskitTestCase):
     """Standard Extension Test."""
-
     @unpack
     @data(
         *inspect.getmembers(
@@ -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