Repository: Qiskit/qiskit

Issue:
Grover's Migration from Aqua to Qiskit Terra
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Informations

- **Qiskit version**: 0.26.0
- **Python version**: 3.8
- **Operating system**: MacOS

### What is the current behavior?

Grover's algorithm does not behave as expected, at least does not take previously known as "Logical Expression" (now PhaseOracle) alphabetically. It runs fine in Aqua but not in Qiskit circuit library.

### Steps to reproduce the problem

```python
from qiskit import *
from qiskit.tools.visualization import plot_histogram
from qiskit.circuit.library import PhaseOracle
from qiskit.algorithms import Grover, AmplificationProblem

oracle = PhaseOracle('((A & C) | (B & D)) & ~(C & D)')
problem = AmplificationProblem(oracle=oracle, is_good_state=oracle.evaluate_bitstring)
backend = Aer.get_backend('qasm_simulator')
grover = Grover(quantum_instance=backend)
result = grover.amplify(problem)
print(result.circuit_results[0])
```

### What is the expected behavior?

It shouldn't give 1100 as a suggestion, since we declare C&D as not expression in PhaseOracle. 

However it gives:

{'1101': 255, '0011': 260, '1100': 260, '0111': 249}

In Aqua it gives (this is the expected behaviour):

{'0101': 261, '0111': 236, '1011': 262, '1010': 265}

### Suggested solutions

There should be something wrong in PhaseOracle implementation, please consider LogicalExpression library when you try to solve this to make it alphabetically ordered correctly.

Missing tweedledum as required package
<!-- ⚠️ 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**: 0.17.0
- **Python version**: 3.8.8
- **Operating system**: linux

### What is the current behavior?
`qiskit.circuit.classicalfunction.BooleanExpression` fails with message that the `tweedledum` library is required. However, `tweedledum` is not listed a requirement for the `qiskit-terra` package.


### Steps to reproduce the problem
Use API's that instantiate the `BooleanExpression` class. E.g.
```
from qiskit.circuit.library import PhaseOracle
oracle = PhaseOracle('x & ~y')  
```
Leads to error:
```
Traceback (most recent call last):
  File "<ipython-input-97-28069418327a>", line 3, in <module>
    oracle = PhaseOracle('x & ~y')               # previous API: qiskit.aqua.components.oracles.LogicalExpressionOracle
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/library/phase_oracle.py", line 55, in __init__
    expression = BooleanExpression(expression)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/classicalfunction/boolean_expression.py", line 39, in __init__
    raise MissingOptionalLibraryError(
MissingOptionalLibraryError: "The 'tweedledum' library is required to use 'BooleanExpression compiler'. You can install it with 'pip install tweedledum'."
```

### What is the expected behavior?

No error about missing library should happen.


### Suggested solutions

Add `tweedledum` to `./requirements.txt`.

Related to #5853 and #5830 where `tweedledum` is present in `./requirements-dev.txt`, but not added to `./requirements.txt`




Discussion:
Good catch! That change was not intended (at least no silently). Thanks for reporting this, we're looking into it 👍🏻 
Tweedledum is an optional dependency, mostly because it hasn't fully stabilized the python api yet and also because there are some packaging issues for some of our supported platforms. However, it is correctly listed in the setup.py: 

https://github.com/Qiskit/qiskit-terra/blob/main/setup.py#L108

which enables you to install terra with tweedledum using:

```
pip install 'qiskit-terra[classical-function-compiler]'
```

This is also documented, albeit not very clearly worded, in the documentation for the class: https://qiskit.org/documentation/stubs/qiskit.circuit.classicalfunction.BooleanExpression.html#qiskit.circuit.classicalfunction.BooleanExpression
@mtreinish I get a similar error message when running tests locally with the command `tox --epy37`. I can install `tweedledum` locally, but that doesn't solve the error message inside the `tox` environment. 
> @mtreinish I get a similar error message when running tests locally with the command `tox --epy37`. I can install `tweedledum` locally, but that doesn't solve the error message inside the `tox` environment.

Did you have an existing tox venv created? If you did and it had an old version of tweedledum installed tox may not have pulled in the new requirement on subsequent runs and updated the venv. (this was a longstanding bug in tox with requirements files that I learned recently has been fixed and will be included in the upcoming tox 4.0 release). You can try rebuilding the venv with `tox -epy37 -r` which tells tox to rebuild the virtualenv.
@mtreinish I did rebuild with the `-r` option and the problem remained. But I cannot reproduce this now (tests work locally at the moment), so I am not sure what happened.



Resolving patch:
diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py
--- a/qiskit/circuit/classicalfunction/boolean_expression.py
+++ b/qiskit/circuit/classicalfunction/boolean_expression.py
@@ -25,15 +25,19 @@
 class BooleanExpression(ClassicalElement):
     """The Boolean Expression gate."""
 
-    def __init__(self, expression: str, name: str = None) -> None:
+    def __init__(self, expression: str, name: str = None, var_order: list = None) -> None:
         """
         Args:
             expression (str): The logical expression string.
-            name (str): Optional. Instruction gate name. Otherwise part of
-                        the expression is going to be used.
+            name (str): Optional. Instruction gate name. Otherwise part of the expression is
+               going to be used.
+            var_order(list): A list with the order in which variables will be created.
+               (default: by appearance)
         """
 
-        self._tweedledum_bool_expression = BoolFunction.from_expression(expression)
+        self._tweedledum_bool_expression = BoolFunction.from_expression(
+            expression, var_order=var_order
+        )
 
         short_expr_for_name = (expression[:10] + "...") if len(expression) > 13 else expression
         num_qubits = (
diff --git a/qiskit/circuit/library/phase_oracle.py b/qiskit/circuit/library/phase_oracle.py
--- a/qiskit/circuit/library/phase_oracle.py
+++ b/qiskit/circuit/library/phase_oracle.py
@@ -52,6 +52,7 @@ def __init__(
         self,
         expression: Union[str, ClassicalElement],
         synthesizer: Optional[Callable[[BooleanExpression], QuantumCircuit]] = None,
+        var_order: list = None,
     ) -> None:
         """Creates a PhaseOracle object
 
@@ -59,12 +60,14 @@ def __init__(
             expression: A Python-like boolean expression.
             synthesizer: Optional. A function to convert a BooleanExpression into a QuantumCircuit
                If None is provided, Tweedledum's `pkrm_synth` with `phase_esop` will be used.
+            var_order(list): A list with the order in which variables will be created.
+               (default: by appearance)
         """
         from qiskit.circuit.classicalfunction.boolean_expression import BooleanExpression
         from qiskit.circuit.classicalfunction.classical_element import ClassicalElement
 
         if not isinstance(expression, ClassicalElement):
-            expression = BooleanExpression(expression)
+            expression = BooleanExpression(expression, var_order=var_order)
 
         self.boolean_expression = expression
 


Test patch:
diff --git a/test/python/circuit/library/test_phase_oracle.py b/test/python/circuit/library/test_phase_oracle.py
--- a/test/python/circuit/library/test_phase_oracle.py
+++ b/test/python/circuit/library/test_phase_oracle.py
@@ -69,6 +69,32 @@ def test_statevector(self, expression, good_states):
         self.assertListEqual(expected_valid, result_valid)
         self.assertListEqual(expected_invalid, result_invalid)
 
+    @data(
+        ("((A & C) | (B & D)) & ~(C & D)", None, [3, 7, 12, 13]),
+        ("((A & C) | (B & D)) & ~(C & D)", ["A", "B", "C", "D"], [5, 7, 10, 11]),
+    )
+    @unpack
+    def test_variable_order(self, expression, var_order, good_states):
+        """Circuit generation"""
+        oracle = PhaseOracle(expression, var_order=var_order)
+        num_qubits = oracle.num_qubits
+        circuit = QuantumCircuit(num_qubits)
+        circuit.h(range(num_qubits))
+        circuit.compose(oracle, inplace=True)
+        statevector = Statevector.from_instruction(circuit)
+
+        valid_state = -1 / sqrt(2**num_qubits)
+        invalid_state = 1 / sqrt(2**num_qubits)
+
+        states = list(range(2**num_qubits))
+        expected_valid = [state in good_states for state in states]
+        result_valid = [isclose(statevector.data[state], valid_state) for state in states]
+
+        expected_invalid = [state not in good_states for state in states]
+        result_invalid = [isclose(statevector.data[state], invalid_state) for state in states]
+        self.assertListEqual(expected_valid, result_valid)
+        self.assertListEqual(expected_invalid, result_invalid)
+
 
 if __name__ == "__main__":
     unittest.main()