Repository: Qiskit/qiskit

Issue:
Slow transpilation of ExcitationPreserving
Transpilation of `ExcitationPreserving` to basis gates `u1, u2, u3, cx` is extremely slow, so much that it just can't be used.

Here is code to demonstrate it:
```
from time import time
from qiskit.circuit.library import ExcitationPreserving
from qiskit import transpile, Aer

sim = Aer.get_backend('qasm_simulator')

list_nqubits = [6, 12, 16, 20, 24]
for nqubits in list_nqubits:
    reps = int(nqubits/2)
    print('ExcitationPreserving(num_qubits=', nqubits, \
          ', reps=', reps, ')', sep='')
    
    start = time()
    circ =  ExcitationPreserving(num_qubits=nqubits, reps=reps)
    print('Construction time:', time()-start, 'seconds')

    print('Number of instructions:', len(circ.data))

    start = time()
    transpile(circ, sim,
              basis_gates=['u1', 'u2', 'u3', 'cx'],
              optimization_level=0)
    print('Transpilation time:', time()-start, 'seconds')
    print('') 
```

Output on my machine:
```
ExcitationPreserving(num_qubits=6, reps=3)
Construction time: 0.0012025833129882812 seconds
Number of instructions: 114
Transpilation time: 0.8360486030578613 seconds

ExcitationPreserving(num_qubits=12, reps=6)
Construction time: 0.0010845661163330078 seconds
Number of instructions: 876
Transpilation time: 15.953871726989746 seconds

ExcitationPreserving(num_qubits=16, reps=8)
Construction time: 0.0016622543334960938 seconds
Number of instructions: 2064
Transpilation time: 129.90289759635925 seconds

ExcitationPreserving(num_qubits=20, reps=10)
Construction time: 0.0025093555450439453 seconds
Number of instructions: 4020
Transpilation time: 1177.0042042732239 seconds

ExcitationPreserving(num_qubits=24, reps=12)
Construction time: 0.00400996208190918 seconds
Number of instructions: 6936
Transpilation time: 3988.637136697769 seconds
```




Discussion:
Looking quickly, `BasisTranslator` is the slowest pass, with most of its time replacing the translated instructions. On my machine, the `num_qubits=16` example with `logging.basicConfig(level='INFO')`:
```
INFO:qiskit.transpiler.runningpassmanager:Pass: UnrollCustomDefinitions - 8.49628 (ms)
INFO:qiskit.transpiler.passes.basis.basis_translator:Begin BasisTranslator from source basis {('ryy', 2), ('rxx', 2), ('rz', 1)} to target basis {'u2', 'u3', 'snapshot', 'delay', 'reset', 'barrier', 'measure', 'u1', 'cx'}.
INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation path search completed in 0.002s.
INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation paths composed in 0.016s.
INFO:qiskit.transpiler.passes.basis.basis_translator:Basis translation instructions replaced in 139.895s.
INFO:qiskit.transpiler.runningpassmanager:Pass: BasisTranslator - 139919.60716 (ms)
INFO:qiskit.compiler.transpile:Total Transpile Time - 140469.19107 (ms)
```
This is likely the same issue as described in https://github.com/Qiskit/qiskit-terra/issues/5174#issuecomment-713153710 , but seems exceptional in this case.
Note that this probably affects any user who wishes to run VQE on a device, with ExcitationPreserving variational form.
I looked into this issue a little further, and the runtime cost here appears to come from building the symbolic expression to track the global phase during transpilation.

For example, `ExcitationPreserving(num_qubits=12, reps=6)`and `(num_qubits=24, reps=12)` have 480 and 3624 parameters, and so after transpilation, end up with a symbolic expression like `global phase: -0.5*θ[0] - 2.0*θ[100] - 2.0*θ[101] - 2.0*θ[102] - 2.0*θ[103] - 2.0*θ[104] - ...` which currently takes a surprising amount of our time to build.

![image](https://user-images.githubusercontent.com/2241698/109674496-f696c280-7b44-11eb-926f-71aa4ff9a738.png)

This looks to be a problem in how we're using sympy (possibly unrelated to #5174 ), in that building and summing large parameterized expressions in sympy is relatively quick (e.g. `b = sum(sp.symbols(['x' + str(i) for i in range(1000)]))` takes 6.4s on my machine) compared to the extremely slow transpile times seen here.

As a short term workaround, binding parameters prior to transpilation reduces the time considerably (down from almost 4000s to 21s for `(num_qubits=24, reps=12)` on my machine) but some further investigation is needed to resolve the core issue.
Does it make sense to allow ignoring the global phase in transpilation? @yaelbh I assume you want to execute the transpiled circuit and the global phase doesn't matter?
The global phase doesn't matter
Global phases don't matter only if you compute probabilities of the state. But if you use the circuit and do further processing, like controlling the entire circuit, it becomes a relative phase and does matter. Therefore I don't think we can just generally ignore it upon transpilation. 

Resolving patch:
diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py
--- a/qiskit/dagcircuit/dagcircuit.py
+++ b/qiskit/dagcircuit/dagcircuit.py
@@ -987,7 +987,9 @@ def substitute_node_with_dag(self, node, input_dag, wires=None):
                                             replay_node.cargs)
 
         if in_dag.global_phase:
-            self.global_phase += in_dag.global_phase
+            from sympy import evaluate
+            with evaluate(False):
+                self.global_phase += in_dag.global_phase
 
         if wires is None:
             wires = in_dag.wires
diff --git a/qiskit/transpiler/passes/basis/basis_translator.py b/qiskit/transpiler/passes/basis/basis_translator.py
--- a/qiskit/transpiler/passes/basis/basis_translator.py
+++ b/qiskit/transpiler/passes/basis/basis_translator.py
@@ -162,7 +162,9 @@ def run(self, dag):
                     dag.substitute_node(node, dag_op, inplace=True)
 
                     if bound_target_dag.global_phase:
-                        dag.global_phase += bound_target_dag.global_phase
+                        from sympy import evaluate
+                        with evaluate(False):
+                            dag.global_phase += bound_target_dag.global_phase
                 else:
                     dag.substitute_node_with_dag(node, bound_target_dag)
             else:


Test patch: