From ecc02895fe7c426e7dae80225d7700e9ce7bae2f Mon Sep 17 00:00:00 2001 From: Bruno Schmitt Date: Fri, 3 Feb 2023 21:45:11 +0100 Subject: [PATCH] PhaseOracle and BooleanExpression allow setting the expression variable order (#6658) * Make tweedledum a hard requirement This commit switches the tweedledum requirement from being optional to a hard requirement for installing qiskit-terra. We rely on tweedledum to synthesize phase oracles which is commonly used functionality and several issues have been opened. This use of tweedledum will likely continue to grow so we should just list it as a requirement moving forward. We originally made it optional because the functionality depending on tweedledum was isolated to just the classical function compiler which wasn't widely used. There were also packaging issues in the past where the available precompiled binaries for tweedledum didn't support all of our supported environments, but those have been resolved. There is still an issue for arm64 macOS binaries but Qiskit doesn't have wide support for that yet (although there is a job for terra). At the same time this commit cleans up the optional requirements list so that aer is no longer listed there and we add an 'all' extra so that people can have a simple entypoint to install all the optional extras at once. Fixes #6333 Fixes Qiskit/qiskit#1253 * Remove unused imports * Cleanup docstrings * black setup * Update requirements.txt Co-authored-by: Bruno Schmitt * User defined variable order for oracle expressions * Fix lint problems * Update qiskit/circuit/classicalfunction/boolean_expression.py Co-authored-by: Luciano Bello * Tests and bug-fix * black * Add reno * black * Consider order in the parameters * black --------- Co-authored-by: Matthew Treinish Co-authored-by: Luciano Bello --- .../classicalfunction/boolean_expression.py | 12 ++++++--- qiskit/circuit/library/phase_oracle.py | 5 +++- ...expression-var-order-d87e9b04fb5d545c.yaml | 18 +++++++++++++ .../circuit/library/test_phase_oracle.py | 26 +++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index 8ac4c8356f24..7a66f2a202bc 100644 --- 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 index 699eddacbd1a..f00a8d56255a 100644 --- 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 diff --git a/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml b/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml new file mode 100644 index 000000000000..24f5b47da3d6 --- /dev/null +++ b/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml @@ -0,0 +1,18 @@ +--- +features: + - | + Add the parameter `var_order` when defining a `PhaseOracle`. This allows for defining the order in which the variables in the logical expression are being considered. + + .. code-block::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)', var_order=['A', 'B', '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]) diff --git a/test/python/circuit/library/test_phase_oracle.py b/test/python/circuit/library/test_phase_oracle.py index 825a8c00a802..e15d6df8ee60 100644 --- 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()