Repository: Qiskit/qiskit (python)

Issue:
Incorrect list of parameters in `PauliTwoDesign` documentation
### Environment

- **Qiskit Terra version**: 0.45.1
- **Python version**:  3.10.8
- **Operating system**: openSUSE Tumbleweed  


### What is happening?

The list of parameters in the _Parameter_ section of the [PauliTwoDesign circuit](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.PauliTwoDesign) documentation page does not correspond with the actual function.
The function only accepts `num_qubits`, `reps`, `seed`, `insert_barriers` and `name`. 
The list of parameters appears to be the one for the [TwoLocal circuit](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.TwoLocal).

### How can we reproduce the issue?

- Visit the [PauliTwoDesign circuit](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.PauliTwoDesign) documentation page and scroll down to the _Parameters_ section (below the _References_ section).
- Notice `skip_unentangeld_qubits` is mentioned as a parameter for this circuit
- Try creating a circuit with this parameter.
```
>>> from qiskit.circuit.library import PauliTwoDesign
>>> circuit = PauliTwoDesign(3, reps=1, seed=5, skip_unentangled_qubits=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: PauliTwoDesign.__init__() got an unexpected keyword argument 'skip_unentangled_qubits'

```

### What should happen?

The documentation should list the correct parameters for the `PauliTwoDesign` function.

### Any suggestions?

Add a `Args` docstring with the correct parameters to the `init` function of `PauliTwoDesign`.

Pull request description:
Fix incorrect list of parameters in PauliTwoDesign documentation 
- [ ] I have added the tests to cover my changes.
- [x] I have updated the documentation accordingly.
- [x] I have read the CONTRIBUTING document.

### Summary
Add an Args docstring to the `init` function in `PauliTwoDesign.py` to fix the incorrect list of parameters in the documentation.

### Details and comments
The list of parameters in the `parameter` section for the [PauliTwoDesign circuit](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.PauliTwoDesign) appears to be the one for the [TwoLocal circuit](https://docs.quantum.ibm.com/api/qiskit/qiskit.circuit.library.TwoLocal). 
This PR adds an Args docstring with the correct parameters for the PauliTwoDesign function.

Since the `name` parameter is not in the list for `TwoLocal`, I assumed this was done on purpose and did not include `name` in the docstring for `PauliTwoDesign`.

Fixes #11423



Patch:
diff --git a/qiskit/circuit/library/n_local/pauli_two_design.py b/qiskit/circuit/library/n_local/pauli_two_design.py
index fa63c8c6f..b79f08889 100644
--- a/qiskit/circuit/library/n_local/pauli_two_design.py
+++ b/qiskit/circuit/library/n_local/pauli_two_design.py
@@ -75,6 +75,16 @@ class PauliTwoDesign(TwoLocal):
         insert_barriers: bool = False,
         name: str = "PauliTwoDesign",
     ):
+        """
+        Args:
+            num_qubits: The number of qubits of the Pauli Two-Design circuit.
+            reps: Specifies how often a block consisting of a rotation layer and entanglement
+                layer is repeated.
+            seed: The seed for randomly choosing the axes of the Pauli rotations.
+            insert_barriers: If ``True``, barriers are inserted in between each layer. If ``False``,
+                no barriers are inserted. Defaults to ``False``.
+
+        """
         from qiskit.circuit.library import RYGate  # pylint: disable=cyclic-import
 
         # store a random number generator
diff --git a/qiskit/providers/basicaer/unitary_simulator.py b/qiskit/providers/basicaer/unitary_simulator.py
index 16b1f69f0..de25f6bd8 100644
--- a/qiskit/providers/basicaer/unitary_simulator.py
+++ b/qiskit/providers/basicaer/unitary_simulator.py
@@ -17,11 +17,11 @@ the simulator. It is exponential in the number of qubits.
 
 .. code-block:: python
 
-    UnitarySimulator().run(qobj)
+    UnitarySimulator().run(run_input)
 
-Where the input is a Qobj object and the output is a BasicAerJob object, which can
-later be queried for the Result object. The result will contain a 'unitary'
-data field, which is a 2**n x 2**n complex numpy array representing the
+Where the input is a either Qobj object (deprecated) or QuantumCircuit or a list of circuits and 
+the output is a BasicAerJob object, which can later be queried for the Result object. The result 
+will contain a 'unitary' data field, which is a 2**n x 2**n complex numpy array representing the
 circuit's unitary matrix.
 """
 import logging
@@ -32,7 +32,6 @@ import warnings
 
 import numpy as np
 
-from qiskit.circuit.quantumcircuit import QuantumCircuit
 from qiskit.utils.multiprocessing import local_hardware_info
 from qiskit.providers.models import QasmBackendConfiguration
 from qiskit.providers.backend import BackendV1
@@ -203,11 +202,11 @@ class UnitarySimulatorPy(BackendV1):
         unitary[abs(unitary) < self._chop_threshold] = 0.0
         return unitary
 
-    def run(self, qobj, **backend_options):
-        """Run qobj asynchronously.
+    def run(self, run_input, **backend_options):
+        """Run experiments in run_input asynchronously.
 
         Args:
-            qobj (Qobj): payload of the experiment
+            run_input (Qobj, QuantumCircuit, list): payload of the experiment
             backend_options (dict): backend options
 
         Returns:
@@ -222,7 +221,7 @@ class UnitarySimulatorPy(BackendV1):
             The "initial_unitary" option specifies a custom initial unitary
             matrix for the simulator to be used instead of the identity
             matrix. This size of this matrix must be correct for the number
-            of qubits inall experiments in the qobj.
+            of qubits in all experiments in the run_input.
 
             The "chop_threshold" option specifies a truncation value for
             setting small values to zero in the output unitary. The default
@@ -238,21 +237,19 @@ class UnitarySimulatorPy(BackendV1):
                     "chop_threshold": 1e-15
                 }
         """
-        if isinstance(qobj, (QuantumCircuit, list)):
-            from qiskit.compiler import assemble
-
-            out_options = {}
-            for key in backend_options:
-                if not hasattr(self.options, key):
-                    warnings.warn(
-                        "Option %s is not used by this backend" % key, UserWarning, stacklevel=2
-                    )
-                else:
-                    out_options[key] = backend_options[key]
-            qobj = assemble(qobj, self, **out_options)
-            qobj_options = qobj.config
-        else:
-            qobj_options = None
+        from qiskit.compiler import assemble
+
+        out_options = {}
+        for key in backend_options:
+            if not hasattr(self.options, key):
+                warnings.warn(
+                    "Option %s is not used by this backend" % key, UserWarning, stacklevel=2
+                )
+            else:
+                out_options[key] = backend_options[key]
+        qobj = assemble(run_input, self, **out_options)
+        qobj_options = qobj.config
+
         self._set_options(qobj_config=qobj_options, backend_options=backend_options)
         job_id = str(uuid.uuid4())
         job = BasicAerJob(self, job_id, self._run_job(job_id, qobj))
diff --git a/qiskit/quantum_info/operators/symplectic/pauli.py b/qiskit/quantum_info/operators/symplectic/pauli.py
index c5b93ceaf..4fa354110 100644
--- a/qiskit/quantum_info/operators/symplectic/pauli.py
+++ b/qiskit/quantum_info/operators/symplectic/pauli.py
@@ -83,7 +83,7 @@ class Pauli(BasePauli):
     Pauli character, and qubit-:math:`(n-1)` to the left-most Pauli
     character. For example ``'XYZ'`` represents
     :math:`X\otimes Y \otimes Z` with ``'Z'`` on qubit-0,
-    ``'Y'`` on qubit-1, and ``'X'`` on qubit-3.
+    ``'Y'`` on qubit-1, and ``'X'`` on qubit-2.
 
     The string representation can be converted to a ``Pauli`` using the
     class initialization (``Pauli('-iXYZ')``). A ``Pauli`` object can be
diff --git a/qiskit/transpiler/passes/optimization/template_matching/template_substitution.py b/qiskit/transpiler/passes/optimization/template_matching/template_substitution.py
index b648cbaca..06c5186d2 100644
--- a/qiskit/transpiler/passes/optimization/template_matching/template_substitution.py
+++ b/qiskit/transpiler/passes/optimization/template_matching/template_substitution.py
@@ -22,6 +22,7 @@ from qiskit.circuit import Parameter, ParameterExpression
 from qiskit.dagcircuit.dagcircuit import DAGCircuit
 from qiskit.dagcircuit.dagdependency import DAGDependency
 from qiskit.converters.dagdependency_to_dag import dagdependency_to_dag
+from qiskit.utils import optionals as _optionals
 
 
 class SubstitutionConfig:
@@ -496,6 +497,15 @@ class TemplateSubstitution:
         import sympy as sym
         from sympy.parsing.sympy_parser import parse_expr
 
+        if _optionals.HAS_SYMENGINE:
+            import symengine
+
+            # Converts Sympy expressions to Symengine ones.
+            to_native_symbolic = symengine.sympify
+        else:
+            # Our native form is sympy, so we don't need to do anything.
+            to_native_symbolic = lambda x: x
+
         circuit_params, template_params = [], []
         # Set of all parameter names that are present in the circuits to be optimised.
         circuit_params_set = set()
@@ -555,7 +565,7 @@ class TemplateSubstitution:
             node.op.params = sub_node_params
 
         # Create the fake binding dict and check
-        equations, circ_dict, temp_symbols, sol, fake_bind = [], {}, {}, {}, {}
+        equations, circ_dict, temp_symbols = [], {}, {}
         for circuit_param, template_param in zip(circuit_params, template_params):
             if isinstance(template_param, ParameterExpression):
                 if isinstance(circuit_param, ParameterExpression):
@@ -577,19 +587,18 @@ class TemplateSubstitution:
         if not temp_symbols:
             return template_dag_dep
 
-        # Check compatibility by solving the resulting equation
-        sym_sol = sym.solve(equations, set(temp_symbols.values()))
-        for key in sym_sol:
-            try:
-                sol[str(key)] = ParameterExpression(circ_dict, sym_sol[key])
-            except TypeError:
-                return None
-
-        if not sol:
+        # Check compatibility by solving the resulting equation.  `dict=True` (surprisingly) forces
+        # the output to always be a list, even if there's exactly one solution.
+        sym_sol = sym.solve(equations, set(temp_symbols.values()), dict=True)
+        if not sym_sol:
+            # No solutions.
             return None
-
-        for key in temp_symbols:
-            fake_bind[key] = sol[str(key)]
+        # If there's multiple solutions, arbitrarily pick the first one.
+        sol = {
+            param.name: ParameterExpression(circ_dict, to_native_symbolic(expr))
+            for param, expr in sym_sol[0].items()
+        }
+        fake_bind = {key: sol[key.name] for key in temp_symbols}
 
         for node in template_dag_dep.get_nodes():
             bound_params = []
diff --git a/releasenotes/notes/remove-qobj-input-in-UnitarySimulatorPy-436c3a7c0dcc5eb6.yaml b/releasenotes/notes/remove-qobj-input-in-UnitarySimulatorPy-436c3a7c0dcc5eb6.yaml
new file mode 100644
index 000000000..a323af693
--- /dev/null
+++ b/releasenotes/notes/remove-qobj-input-in-UnitarySimulatorPy-436c3a7c0dcc5eb6.yaml
@@ -0,0 +1,20 @@
+---
+
+upgrade:
+  - |
+    It is no longer allowed to pass a :class:`~qiskit.qobj.QasmQobj` as the first input to
+    The :meth:`~qiskit.providers.basicaer.UnitarySimulatorPy.run`. This functionality was 
+    deprecated in Qiskit 0.46 and has now been removed.
+
+    Example usage:
+
+      from qiskit import transpile, QuantumCircuit
+      from qiskit.providers.basicaer import BasicAer
+
+      backend = BasicAer.get_backend('unitary_simulator')
+      circuit = QuantumCircuit(2)
+      qc.h(0)
+      qc.cx(0, 1)
+      qc.measure_all()
+      tqc = transpile(circuit, backend)
+      result = backend.run(tqc).result()
diff --git a/releasenotes/notes/template-match-symbols-c00786155f101e39.yaml b/releasenotes/notes/template-match-symbols-c00786155f101e39.yaml
new file mode 100644
index 000000000..a005879d2
--- /dev/null
+++ b/releasenotes/notes/template-match-symbols-c00786155f101e39.yaml
@@ -0,0 +1,8 @@
+---
+fixes:
+  - |
+    The :class:`.TemplateOptimization` pass will now return parametric expressions using the native
+    symbolic expression format of :class:`.ParameterExpression`, rather than always using Sympy.
+    For most supported platforms, this means that the expressions will be Symengine objects.
+    Previously, the pass could return mismatched objects, which could lead to later failures in
+    parameter-handling code.


Test patch:
diff --git a/test/python/transpiler/test_template_matching.py b/test/python/transpiler/test_template_matching.py
index 015e19e44..9e4613053 100644
--- a/test/python/transpiler/test_template_matching.py
+++ b/test/python/transpiler/test_template_matching.py
@@ -344,6 +344,43 @@ class TestTemplateMatching(QiskitTestCase):
         # however these are equivalent if the operators are the same
         self.assertTrue(Operator(circuit_in).equiv(circuit_out))
 
+    def test_output_symbolic_library_equal(self):
+        """Test that the template matcher returns parameter expressions that use the same symbolic
+        library as the default; it should not coerce everything to Sympy when playing with the
+        `ParameterExpression` internals."""
+
+        a, b = Parameter("a"), Parameter("b")
+
+        template = QuantumCircuit(1)
+        template.p(a, 0)
+        template.p(-a, 0)
+        template.rz(a, 0)
+        template.rz(-a, 0)
+
+        circuit = QuantumCircuit(1)
+        circuit.p(-b, 0)
+        circuit.p(b, 0)
+
+        pass_ = TemplateOptimization(template_list=[template], user_cost_dict={"p": 100, "rz": 1})
+        out = pass_(circuit)
+
+        expected = QuantumCircuit(1)
+        expected.rz(-b, 0)
+        expected.rz(b, 0)
+        self.assertEqual(out, expected)
+
+        def symbolic_library(expr):
+            """Get the symbolic library of the expression - 'sympy' or 'symengine'."""
+            return type(expr._symbol_expr).__module__.split(".")[0]
+
+        out_exprs = [expr for instruction in out.data for expr in instruction.operation.params]
+        self.assertEqual(
+            [symbolic_library(b)] * len(out_exprs), [symbolic_library(expr) for expr in out_exprs]
+        )
+
+        # Assert that the result still works with parametric assignment.
+        self.assertEqual(out.assign_parameters({b: 1.5}), expected.assign_parameters({b: 1.5}))
+
     def test_optimizer_does_not_replace_unbound_partial_match(self):
         """
         Test that partial matches with parameters will not raise errors.