Repository: Qiskit/qiskit

Issue:
Synthesis translation_method doesn't always respect basis_set
<!-- ⚠️ 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
- **Python version**: 3.8
- **Operating system**: Any

### What is the current behavior?

Transpiling a circuit with an XYX basis and setting the basis translation method to synthesis doesn't actually change the basis.

### Steps to reproduce the problem
```
import logging
from qiskit import QuantumCircuit
from qiskit.compiler import transpile
from qiskit.compiler.transpile import CouplingMap
  
logging.basicConfig(level='DEBUG')
  
edge_list = [[0, 1], [0,2], [1,2]]
  
cmap = CouplingMap(edge_list)
  
qc = QuantumCircuit(3)
  
qc.h(0)
qc.h(1)
qc.h(2)

print(transpile(qc, coupling_map=cmap, basis_gates=['id', 'ry', 'rx', 'cx'],
                translation_method='synthesis'))
```

will return 
```
         ┌───┐
q_0 -> 0 ┤ H ├───────
         ├───┤
q_1 -> 1 ┤ H ├───────
         ├───┤       
q_2 -> 2 ┤ H ├───────
         └───┘       
```

### What is the expected behavior?

The hadamard gates get translated to Rx(pi) Ry(-pi/2)

### Suggested solutions


Discussion:
This is expected since `UnitarySynthesis` only decompose `UnitaryGates` to the target basis, see lines [85](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/transpiler/passes/synthesis/unitary_synthesis.py#L85) and [96](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/transpiler/passes/synthesis/unitary_synthesis.py#L96). Should run over all `op_nodes` ?
I think the issue is actually earlier in the pass manager. With the translation method set to `synthesis` it is running collect2q blocks and consolidate blocks to generate the unitary gates which then unitary synthesis can decompose into the target basis set. But, because this circuit is all 1q it's not collecting any blocks and not creating the unitary gates for unitary synthesis to decompose.
Oh, i see. Thanks for clarifying it. 
So after some "investigation", i found out that this problem arises even with 2q gates if they are in a block with length 1. For example, transpiling the following circuit doesn't replace  the `cz` gate. Probably, line [114](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/transpiler/passes/optimization/consolidate_blocks.py#L114) is the root.
```python
qc = QuantumCircuit(2)
qc.cz(0, 1)

circ = transpile(qc, basis_gates=['id', 'ry', 'rx', 'cx'],
                translation_method='synthesis')
print(circ)
```
Furthermore, should there be a pass that collects consecutive 1q gates?
For the two qubit bug found by @georgios-ts , I agree L114 should be extended to check `basis_gates`. For the consolidation of 1q gates, #5216 seems the right approach.
Hello, Could I step into this?
@SeifMostafa sorry we missed that one. Are you still interested?

Resolving patch:
diff --git a/qiskit/transpiler/passes/optimization/consolidate_blocks.py b/qiskit/transpiler/passes/optimization/consolidate_blocks.py
--- a/qiskit/transpiler/passes/optimization/consolidate_blocks.py
+++ b/qiskit/transpiler/passes/optimization/consolidate_blocks.py
@@ -15,7 +15,7 @@
 """Replace each block of consecutive gates by a single Unitary node."""
 
 
-from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit
+from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Gate
 from qiskit.quantum_info.operators import Operator
 from qiskit.quantum_info.synthesis import TwoQubitBasisDecomposer
 from qiskit.extensions import UnitaryGate
@@ -111,11 +111,21 @@ def run(self, dag):
         # create the dag from the updated list of blocks
         basis_gate_name = self.decomposer.gate.name
         for block in blocks:
-            if len(block) == 1 and (block[0].name != basis_gate_name
-                                    or block[0].op.is_parameterized()):
-                # an intermediate node that was added into the overall list
-                new_dag.apply_operation_back(block[0].op, block[0].qargs,
-                                             block[0].cargs)
+            if len(block) == 1 and block[0].name != basis_gate_name:
+                # pylint: disable=too-many-boolean-expressions
+                if block[0].type == 'op' \
+                        and self.basis_gates \
+                        and block[0].name not in self.basis_gates \
+                        and len(block[0].cargs) == 0 and block[0].condition is None \
+                        and isinstance(block[0].op, Gate) \
+                        and hasattr(block[0].op, '__array__') \
+                        and not block[0].op.is_parameterized():
+                    new_dag.apply_operation_back(UnitaryGate(block[0].op.to_matrix()),
+                                                 block[0].qargs, block[0].cargs)
+                else:
+                    # an intermediate node that was added into the overall list
+                    new_dag.apply_operation_back(block[0].op, block[0].qargs,
+                                                 block[0].cargs)
             else:
                 # find the qubits involved in this block
                 block_qargs = set()


Test patch:
diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py
--- a/test/python/compiler/test_transpiler.py
+++ b/test/python/compiler/test_transpiler.py
@@ -1050,6 +1050,41 @@ def test_transpile_optional_registers(self, optimization_level):
         self.assertEqual(len(out.qubits), FakeAlmaden().configuration().num_qubits)
         self.assertEqual(out.clbits, clbits)
 
+    @data(0, 1, 2, 3)
+    def test_synthesis_translation_method_with_single_qubit_gates(self, optimization_level):
+        """Test that synthesis basis translation works for solely 1q circuit"""
+        qc = QuantumCircuit(3)
+        qc.h(0)
+        qc.h(1)
+        qc.h(2)
+        res = transpile(qc, basis_gates=['id', 'rz', 'x', 'sx', 'cx'],
+                        translation_method='synthesis', optimization_level=optimization_level)
+        expected = QuantumCircuit(3, global_phase=3*np.pi/4)
+        expected.rz(np.pi / 2, 0)
+        expected.rz(np.pi / 2, 1)
+        expected.rz(np.pi / 2, 2)
+        expected.sx(0)
+        expected.sx(1)
+        expected.sx(2)
+        expected.rz(np.pi / 2, 0)
+        expected.rz(np.pi / 2, 1)
+        expected.rz(np.pi / 2, 2)
+        self.assertEqual(res, expected)
+
+    @data(0, 1, 2, 3)
+    def test_synthesis_translation_method_with_gates_outside_basis(self, optimization_level):
+        """Test that synthesis translation works for circuits with single gates outside bassis"""
+        qc = QuantumCircuit(2)
+        qc.swap(0, 1)
+        res = transpile(qc, basis_gates=['id', 'rz', 'x', 'sx', 'cx'],
+                        translation_method='synthesis', optimization_level=optimization_level)
+        if optimization_level != 3:
+            self.assertTrue(Operator(qc).equiv(res))
+            self.assertNotIn('swap', res.count_ops())
+        else:
+            # Optimization level 3 eliminates the pointless swap
+            self.assertEqual(res, QuantumCircuit(2))
+
 
 class StreamHandlerRaiseException(StreamHandler):
     """Handler class that will raise an exception on formatting errors."""
diff --git a/test/python/transpiler/test_consolidate_blocks.py b/test/python/transpiler/test_consolidate_blocks.py
--- a/test/python/transpiler/test_consolidate_blocks.py
+++ b/test/python/transpiler/test_consolidate_blocks.py
@@ -304,6 +304,22 @@ def test_no_kak_in_basis(self):
         res = consolidate_blocks_pass.run(dag)
         self.assertEqual(res, dag)
 
+    def test_single_gate_block_outside_basis(self):
+        """Test that a single gate block outside the configured basis gets converted."""
+        qc = QuantumCircuit(2)
+        qc.swap(0, 1)
+        consolidate_block_pass = ConsolidateBlocks(
+            basis_gates=['id', 'cx', 'rz', 'sx', 'x'])
+        pass_manager = PassManager()
+        pass_manager.append(Collect2qBlocks())
+        pass_manager.append(consolidate_block_pass)
+        expected = QuantumCircuit(2)
+        expected.unitary(np.array([[1, 0, 0, 0],
+                                   [0, 0, 1, 0],
+                                   [0, 1, 0, 0],
+                                   [0, 0, 0, 1]]), [0, 1])
+        self.assertEqual(expected, pass_manager.run(qc))
+
 
 if __name__ == '__main__':
     unittest.main()