Task: Filter possible 2q decomposers more carefully in unitary synthesis (Qiskit/qiskit#10008)

org: Qiskit
repo: qiskit
pr_number: 10008
issue_title: Filter possible 2q decomposers more carefully in unitary synthesis
issue_body: This PR fixes a few bugs in transpilation.\r\n\r\nCloses #9996\r\nCloses #9935\r\n\r\nThis PR supersedes #9994 , #9984, #9986, #9982 . These PRs will be closed\r\n\r\n* Skips running some decomposers when we know they will fail. In particular, before this PR, `XXDecomposer` was used unconditionally when a `Target` is supplied to `UnitarySynthesis`. Now it is skipped when there are no controlled basis gates to send to it, except in the case that the unitary to be synthesized is a product of 1q gates.\r\n* Makes `XXDecomposer`  throw more informative errors\r\n\r\n#### Behavior of previous bugs and tests\r\n\r\n* Adds a test that this bug  #9996 is fixed. Synthesis with a basis set whose only engtangling gate is iSwap no longer throws an error, but rather runs to completion. Resynthesizing with a CX-gate in the basis does not change the circuit. But the corresponding numeric unitary is correct.\r\n\r\n* `FakePrague` and other fake backends erroneously have no entangling gate ( #9983 #10004)  Prior to this PR this caused an error to be thrown when synthesizing some circuits #9935  With this PR an error is no longer thrown. All decomposers are filtered out in `DefaultUnitarySynthesis` so that none are run (Calling `transpile` with optimization level 3) A circuit is returned by `transpile`. But it is incorrect. An issue should be filed.\r\n\r\n* Synthesizing a non-entangling unitary with a basis containing no entangling gates still works. There was an existing test for this.\r\n\r\n* I am pretty sure that an error thrown by any decomposer will cause synthesis of the entire circuit to fail. There were existing tests for some cases in the test suite. These are still in place and still have the same behavior. \r\n\r\n#### Details\r\n\r\nPreviously all analysis of the unitary to be decomposed happened in the decomposers themselves. Now we do a bit of analysis on the unitary to be decomposed in order to choose which decomposers to run. This allows filtering some that would fail. To do this we call `TwoQubitWeylDecomposition` on the unitary. Since the 2q decomposers perform this same Weyl decomposition as a first step, we allow the decomposers to take this weyl decomposition as an argument. The argument is optional to avoid breaking code using the old API.
resolved_issues: number: 9935
title: IndexError in `XXDecomposer` during `FakePrague` `transpile()` with optimization level 3
body: ### Environment\n\n- **Qiskit Terra version**: 0.24.0 main (@ 5672c70649b4f83a6c7146c68a6a543d4a3908fd)\r\n- **Python version**:  3.10\r\n- **Operating system**:  Linux\r\n\n\n### What is happening?\n\nDuring `transpile()` with optimization level 3 on some circuits targeting `FakePrague` the 2q unitary synthesis code is raising an index error with the main branch. This code works fine on the most recent release.\n\n### How can we reproduce the issue?\n\n```python\r\nfrom qiskit.circuit import QuantumCircuit\r\nfrom qiskit.compiler import transpile\r\nfrom qiskit.providers.fake_provider import FakePrague\r\n\r\nbackend = FakePrague()\r\nnum_qubits = 10\r\n# Circuit creation\r\nqc = QuantumCircuit(num_qubits)\r\nqc.h(0)\r\nfor i in range(num_qubits - 1):\r\n    if i % 2 == 0:\r\n        qc.cy(0, i + 1)\r\n    else:\r\n        qc.cx(0, i + 1)\r\nqc.measure_all()\r\ntqc = transpile(\r\n    qc, backend, seed_transpiler=2023, optimization_level=3\r\n)\r\n```\n\n### What should happen?\n\nThe `transpile()` shouldn't error and it should successfully run 2q synthesis and return a compiled circuit.\n\n### Any suggestions?\n\nThis is a bug that has been introduced since the 0.23.0 release (as it still works on the released version). Looking at the git log this is likely a bug that was introduced in #9175 (https://github.com/Qiskit/qiskit-terra/commit/a8f83c4469f55a62f958f3eed2df10a598811eae) 

Reference solution:
fix_patch: diff --git a/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py b/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py\nindex 3d5714b5c2ab..2527816530c8 100644\n--- a/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py\n+++ b/qiskit/quantum_info/synthesis/xx_decompose/decomposer.py\n@@ -151,6 +151,13 @@ def _best_decomposition(canonical_coordinate, available_strengths):\n         canonical_coordinate = np.array(canonical_coordinate)\n \n         while True:\n+            if len(priority_queue) == 0:\n+                if len(available_strengths) == 0:\n+                    raise QiskitError(\n+                        "Attempting to synthesize entangling gate with no controlled gates in basis set."\n+                    )\n+                raise QiskitError("Unable to synthesize a 2q unitary with the supplied basis set.")\n+\n             sequence_cost, sequence = heapq.heappop(priority_queue)\n \n             strength_polytope = XXPolytope.from_strengths(*[x / 2 for x in sequence])\ndiff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py\nindex 9b42567b8697..efd92085887b 100644\n--- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py\n+++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py\n@@ -701,24 +701,25 @@ def is_controlled(gate):\n         # if we are using the approximation_degree knob, use it to scale already-given fidelities\n         if approximation_degree is not None:\n             basis_2q_fidelity = {k: v * approximation_degree for k, v in basis_2q_fidelity.items()}\n-        for basis_1q in available_1q_basis:\n-            if isinstance(pi2_basis, CXGate) and basis_1q == "ZSX":\n-                pi2_decomposer = TwoQubitBasisDecomposer(\n-                    pi2_basis,\n-                    euler_basis=basis_1q,\n+        if basis_2q_fidelity:\n+            for basis_1q in available_1q_basis:\n+                if isinstance(pi2_basis, CXGate) and basis_1q == "ZSX":\n+                    pi2_decomposer = TwoQubitBasisDecomposer(\n+                        pi2_basis,\n+                        euler_basis=basis_1q,\n+                        basis_fidelity=basis_2q_fidelity,\n+                        pulse_optimize=True,\n+                    )\n+                    embodiments.update({pi / 2: XXEmbodiments[type(pi2_decomposer.gate)]})\n+                else:\n+                    pi2_decomposer = None\n+                decomposer = XXDecomposer(\n                     basis_fidelity=basis_2q_fidelity,\n-                    pulse_optimize=True,\n+                    euler_basis=basis_1q,\n+                    embodiments=embodiments,\n+                    backup_optimizer=pi2_decomposer,\n                 )\n-                embodiments.update({pi / 2: XXEmbodiments[type(pi2_decomposer.gate)]})\n-            else:\n-                pi2_decomposer = None\n-            decomposer = XXDecomposer(\n-                basis_fidelity=basis_2q_fidelity,\n-                euler_basis=basis_1q,\n-                embodiments=embodiments,\n-                backup_optimizer=pi2_decomposer,\n-            )\n-            decomposers.append(decomposer)\n+                decomposers.append(decomposer)\n \n         self._decomposer_cache[qubits_tuple] = decomposers\n         return decomposers\n