Repository: Qiskit/qiskit

Issue:
parameterized circuits raise exception at optimization_level>=2
<!-- ⚠️ 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**:
- **Python version**:
- **Operating system**:

### What is the current behavior?
When a parameterized circuit is passed to the compiler with optimization_level>=2 one gets the exception 
> ParameterExpression with unbound parameters ({Parameter(x)}) cannot be cast to a float.

This seems to be due to the commutation analysis pass attempting to convert the rx to a matrix.

### Steps to reproduce the problem
```
qc = QuantumCircuit(1)
theta = qiskit.circuit.parameter('theta')
qc.rx(theta, 0)
qc,x(0)
qiskit.compiler.transpile(qc, optimization_level=3)
```


### What is the expected behavior?



### Suggested solutions





Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/passes/collect_2q_blocks.py b/qiskit/transpiler/passes/collect_2q_blocks.py
--- a/qiskit/transpiler/passes/collect_2q_blocks.py
+++ b/qiskit/transpiler/passes/collect_2q_blocks.py
@@ -28,6 +28,7 @@
 
 from collections import defaultdict
 
+from qiskit.circuit import ParameterExpression
 from qiskit.transpiler.basepasses import AnalysisPass
 
 
@@ -68,7 +69,8 @@ def run(self, dag):
                         pnd = pred[0]
                         if pnd.name in good_names:
                             if (pnd.name == "cx" and set(pnd.qargs) == these_qubits) or \
-                                    pnd.name != "cx":
+                                pnd.name != "cx" and not any(isinstance(param, ParameterExpression)
+                                                             for param in pnd.op.params):
                                 group.append(pnd)
                                 nodes_seen[pnd] = True
                                 pred_next.extend(dag.quantum_predecessors(pnd))
@@ -101,7 +103,8 @@ def run(self, dag):
                                                     set(pnd.qargs))
                                 continue
                             # If a predecessor is a single qubit gate, add it
-                            if pnd.name != "cx":
+                            if pnd.name != "cx" and not any(isinstance(param, ParameterExpression)
+                                                            for param in pnd.op.params):
                                 if not nodes_seen[pnd]:
                                     group.append(pnd)
                                     nodes_seen[pnd] = True
@@ -140,7 +143,8 @@ def run(self, dag):
                         snd = succ[0]
                         if snd.name in good_names:
                             if (snd.name == "cx" and set(snd.qargs) == these_qubits) or \
-                                    snd.name != "cx":
+                                snd.name != "cx" and not any(isinstance(param, ParameterExpression)
+                                                             for param in snd.op.params):
                                 group.append(snd)
                                 nodes_seen[snd] = True
                                 succ_next.extend(dag.quantum_successors(snd))
@@ -177,7 +181,8 @@ def run(self, dag):
                             # If a successor is a single qubit gate, add it
                             # NB as we have eliminated all gates with names not in
                             # good_names, this check guarantees they are single qubit
-                            if snd.name != "cx":
+                            if snd.name != "cx" and not any(isinstance(param, ParameterExpression)
+                                                            for param in snd.op.params):
                                 if not nodes_seen[snd]:
                                     group.append(snd)
                                     nodes_seen[snd] = True
diff --git a/qiskit/transpiler/passes/commutation_analysis.py b/qiskit/transpiler/passes/commutation_analysis.py
--- a/qiskit/transpiler/passes/commutation_analysis.py
+++ b/qiskit/transpiler/passes/commutation_analysis.py
@@ -28,6 +28,7 @@
 from qiskit.transpiler.exceptions import TranspilerError
 from qiskit.transpiler.basepasses import AnalysisPass
 from qiskit.quantum_info.operators import Operator
+from qiskit.circuit import ParameterExpression
 
 _CUTOFF_PRECISION = 1E-10
 
@@ -104,6 +105,9 @@ def _commute(node1, node2):
     if node1.condition or node2.condition:
         return False
 
+    if any(isinstance(param, ParameterExpression) for param in node1.op.params + node2.op.params):
+        return False
+
     qarg = list(set(node1.qargs + node2.qargs))
     qbit_num = len(qarg)
 


Test patch:
diff --git a/test/python/circuit/test_parameters.py b/test/python/circuit/test_parameters.py
--- a/test/python/circuit/test_parameters.py
+++ b/test/python/circuit/test_parameters.py
@@ -26,6 +26,7 @@
 from qiskit.compiler import assemble, transpile
 from qiskit.execute import execute
 from qiskit.test import QiskitTestCase
+from qiskit.test.mock import FakeOurense
 from qiskit.tools import parallel_map
 from qiskit.circuit.exceptions import CircuitError
 
@@ -384,6 +385,24 @@ def test_transpiling_multiple_parameterized_circuits(self):
 
         self.assertTrue(len(job.result().results), 2)
 
+    def test_transpile_across_optimization_levels(self):
+        """Verify parameterized circuits can be transpiled with all default pass managers."""
+
+        qc = QuantumCircuit(5, 5)
+
+        theta = Parameter('theta')
+        phi = Parameter('phi')
+
+        qc.rx(theta, 0)
+        qc.x(0)
+        for i in range(5-1):
+            qc.rxx(phi, i, i+1)
+
+        qc.measure(range(5-1), range(5-1))
+
+        for i in [0, 1, 2, 3]:
+            transpile(qc, FakeOurense(), optimization_level=i)
+
     def test_repeated_gates_to_dag_and_back(self):
         """Verify circuits with repeated parameterized gates can be converted
         to DAG and back, maintaining consistency of circuit._parameter_table."""