From ac97b4c1508ae7016339245abe5171586e0e058f Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 16 Jun 2021 10:45:14 -0400 Subject: [PATCH 01/14] 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 --- .pylintrc | 2 +- .../classicalfunction/boolean_expression.py | 24 +++-------------- .../classical_function_visitor.py | 17 ++---------- .../classicalfunction/classicalfunction.py | 18 +++---------- qiskit/circuit/classicalfunction/utils.py | 9 ++----- ...requirements-updates-6059950dfde3cef6.yaml | 25 ++++++++++++++++++ requirements.txt | 1 + setup.py | 26 ++++++++++++++----- .../test_classical_function.py | 2 -- .../classical_function_compiler/test_parse.py | 5 ---- .../test_simulate.py | 2 -- .../test_synthesis.py | 3 --- .../test_typecheck.py | 8 ------ 13 files changed, 59 insertions(+), 83 deletions(-) create mode 100644 releasenotes/notes/requirements-updates-6059950dfde3cef6.yaml diff --git a/.pylintrc b/.pylintrc index d0bf6a25dd56..e18f07284aca 100644 --- a/.pylintrc +++ b/.pylintrc @@ -33,7 +33,7 @@ unsafe-load-any-extension=no # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code -extension-pkg-whitelist=retworkx, numpy +extension-pkg-whitelist=retworkx, numpy, tweedledum [MESSAGES CONTROL] diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index 06f8aa93911c..63a563ace771 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -12,14 +12,15 @@ """A quantum oracle constructed from a logical expression or a string in the DIMACS format.""" +from os.path import basename, isfile from typing import Callable, Optional -from os.path import basename, isfile +from tweedledum import BitVec, BoolFunction +from tweedledum.synthesis import pkrm_synth from qiskit.circuit import QuantumCircuit from qiskit.exceptions import MissingOptionalLibraryError from .classical_element import ClassicalElement -from .utils import HAS_TWEEDLEDUM class BooleanExpression(ClassicalElement): @@ -35,13 +36,6 @@ def __init__(self, expression: str, name: str = None) -> None: Raises: MissingOptionalLibraryError: If tweedledum is not installed. Tweedledum is required. """ - if not HAS_TWEEDLEDUM: - raise MissingOptionalLibraryError( - libname="tweedledum", - name="BooleanExpression compiler", - pip_install="pip install tweedledum", - ) - from tweedledum import BoolFunction self._tweedledum_bool_expression = BoolFunction.from_expression(expression) @@ -63,8 +57,6 @@ def simulate(self, bitstring: str) -> bool: Returns: bool: result of the evaluation. """ - from tweedledum import BitVec - bits = [] for bit in bitstring: bits.append(BitVec(1, bit)) @@ -92,8 +84,7 @@ def synth( qregs = None # TODO: Probably from self._tweedledum_bool_expression._signature if synthesizer is None: - from tweedledum.synthesis import pkrm_synth # pylint: disable=no-name-in-module - from .utils import tweedledum2qiskit + from .utils import tweedledum2qiskit # Avoid an import cycle truth_table = self._tweedledum_bool_expression.truth_table(output_bit=0) return tweedledum2qiskit(pkrm_synth(truth_table), name=self.name, qregs=qregs) @@ -116,13 +107,6 @@ def from_dimacs_file(cls, filename: str): MissingOptionalLibraryError: If tweedledum is not installed. Tweedledum is required. FileNotFoundError: If filename is not found. """ - if not HAS_TWEEDLEDUM: - raise MissingOptionalLibraryError( - libname="tweedledum", - name="BooleanExpression compiler", - pip_install="pip install tweedledum", - ) - from tweedledum import BoolFunction expr_obj = cls.__new__(cls) if not isfile(filename): diff --git a/qiskit/circuit/classicalfunction/classical_function_visitor.py b/qiskit/circuit/classicalfunction/classical_function_visitor.py index c4a6e1adeba5..2ed8a1846c64 100644 --- a/qiskit/circuit/classicalfunction/classical_function_visitor.py +++ b/qiskit/circuit/classicalfunction/classical_function_visitor.py @@ -17,8 +17,9 @@ import ast import _ast +from tweedledum.classical import LogicNetwork + from qiskit.exceptions import MissingOptionalLibraryError -from .utils import HAS_TWEEDLEDUM from .exceptions import ClassicalFunctionParseError, ClassicalFunctionCompilerTypeError @@ -36,12 +37,6 @@ class ClassicalFunctionVisitor(ast.NodeVisitor): } def __init__(self): - if not HAS_TWEEDLEDUM: - raise MissingOptionalLibraryError( - libname="tweedledum", - name="classical function compiler", - pip_install="pip install tweedledum", - ) self.scopes = [] self.args = [] self._network = None @@ -57,14 +52,6 @@ def visit_Module(self, node): def visit_FunctionDef(self, node): """The function definition should have type hints""" - if HAS_TWEEDLEDUM: - from tweedledum.classical import LogicNetwork # pylint: disable=no-name-in-module - else: - raise MissingOptionalLibraryError( - libname="tweedledum", - name="classical function compiler", - pip_install="pip install tweedledum", - ) if node.returns is None: raise ClassicalFunctionParseError("return type is needed") scope = {"return": (node.returns.id, None), node.returns.id: ("type", None)} diff --git a/qiskit/circuit/classicalfunction/classicalfunction.py b/qiskit/circuit/classicalfunction/classicalfunction.py index 330b6a30464a..696812fb5f73 100644 --- a/qiskit/circuit/classicalfunction/classicalfunction.py +++ b/qiskit/circuit/classicalfunction/classicalfunction.py @@ -15,11 +15,14 @@ import ast from typing import Callable, Optional +from tweedledum.classical import simulate +from tweedledum.synthesis import pkrm_synth + from qiskit.circuit import QuantumCircuit, QuantumRegister from qiskit.exceptions import MissingOptionalLibraryError, QiskitError from .classical_element import ClassicalElement -from .utils import HAS_TWEEDLEDUM from .classical_function_visitor import ClassicalFunctionVisitor +from .utils import tweedledum2qiskit class ClassicalFunction(ClassicalElement): @@ -40,12 +43,6 @@ def __init__(self, source, name=None): """ if not isinstance(source, str): raise QiskitError("ClassicalFunction needs a source code as a string.") - if not HAS_TWEEDLEDUM: - raise MissingOptionalLibraryError( - libname="tweedledum", - name="classical function compiler", - pip_install="pip install tweedledum", - ) self._ast = ast.parse(source) self._network = None self._scopes = None @@ -111,8 +108,6 @@ def simulate(self, bitstring: str) -> bool: Returns: bool: result of the evaluation. """ - from tweedledum.classical import simulate # pylint: disable=no-name-in-module - return simulate(self._network, bitstring) def simulate_all(self): @@ -133,8 +128,6 @@ def simulate_all(self): def truth_table(self): """Returns (and computes) the truth table""" if self._truth_table is None: - from tweedledum.classical import simulate # pylint: disable=no-name-in-module - self._truth_table = simulate(self._network) return self._truth_table @@ -161,9 +154,6 @@ def synth( if synthesizer: return synthesizer(self) - from .utils import tweedledum2qiskit - from tweedledum.synthesis import pkrm_synth # pylint: disable=no-name-in-module - return tweedledum2qiskit(pkrm_synth(self.truth_table[0]), name=self.name, qregs=qregs) def _define(self): diff --git a/qiskit/circuit/classicalfunction/utils.py b/qiskit/circuit/classicalfunction/utils.py index b8b8fc697eae..30e0940046c1 100644 --- a/qiskit/circuit/classicalfunction/utils.py +++ b/qiskit/circuit/classicalfunction/utils.py @@ -12,13 +12,8 @@ """Internal utils for Classical Function Compiler""" -try: - from tweedledum.ir import Qubit # pylint: disable=no-name-in-module - from tweedledum.passes import parity_decomp # pylint: disable=no-name-in-module - - HAS_TWEEDLEDUM = True -except Exception: # pylint: disable=broad-except - HAS_TWEEDLEDUM = False +from tweedledum.ir import Qubit +from tweedledum.passes import parity_decomp from qiskit.circuit import QuantumCircuit diff --git a/releasenotes/notes/requirements-updates-6059950dfde3cef6.yaml b/releasenotes/notes/requirements-updates-6059950dfde3cef6.yaml new file mode 100644 index 000000000000..e795de505ebc --- /dev/null +++ b/releasenotes/notes/requirements-updates-6059950dfde3cef6.yaml @@ -0,0 +1,25 @@ +--- +upgrade: + - | + The `tweedledum `__ library which + was previously an optional dependency has been made a requirement. This + was done because of the wide use of the + :class:`~qiskit.circuit.library.PhaseOracle` (which depends on + having tweedledum installed) with several algorithms + from :mod:`qiskit.algorithms`. + - | + The optional extra ``full-featured-simulators`` which could previously used + to install ``qiskit-aer`` with something like + ``pip install qiskit-terra[full-featured-simulators]`` has been removed + from the qiskit-terra package. If this was being used to install + ``qiskit-aer`` with ``qiskit-terra`` instead you should rely on the + `qiskit `__ metapackage or just install + qiskit-terra and qiskit-aer together with + ``pip install qiskit-terra qiskit-aer``. +features: + - | + A new optional extra ``all`` has been added to the qiskit-terra package. + This enables installing all the optional requirements with a single + extra, for example: ``pip install 'qiskit-terra[all]'``, Previously, it + was necessary to list all the extras individually to install all the + optional dependencies simultaneously. diff --git a/requirements.txt b/requirements.txt index 9e7be907d3a2..326702dd0bf5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,4 @@ fastjsonschema>=2.10 python-constraint>=1.4 python-dateutil>=2.8.0 symengine>0.7 ; platform_machine == 'x86_64' or platform_machine == 'aarch64' or platform_machine == 'ppc64le' or platform_machine == 'amd64' or platform_machine == 'arm64' +tweedledum>=1.0,<2.0 diff --git a/setup.py b/setup.py index 325f759b8e81..a5bfbd3f8246 100755 --- a/setup.py +++ b/setup.py @@ -70,6 +70,23 @@ '.*', '', readme_file.read(), flags=re.S | re.M) + +visualization_extras = [ + "matplotlib>=2.1", + "ipywidgets>=7.3.0", + "pydot", + "pillow>=4.2.1", + "pylatexenc>=1.4", + "seaborn>=0.9.0", + "pygments>=2.4", +] + + +z3_requirements = [ + "z3-solver>=4.7", +] + + setup( name="qiskit-terra", version="0.18.0", @@ -102,12 +119,9 @@ include_package_data=True, python_requires=">=3.6", extras_require={ - 'visualization': ['matplotlib>=2.1', 'ipywidgets>=7.3.0', - 'pydot', "pillow>=4.2.1", "pylatexenc>=1.4", - "seaborn>=0.9.0", "pygments>=2.4"], - 'classical-function-compiler': ['tweedledum>=1.0,<2.0'], - 'full-featured-simulators': ['qiskit-aer>=0.1'], - 'crosstalk-pass': ['z3-solver>=4.7'], + 'visualization': visualization_extras, + 'crosstalk-pass': z3_requirements, + 'all': visualization_extras + z3_requirements, }, project_urls={ "Bug Tracker": "https://github.com/Qiskit/qiskit-terra/issues", diff --git a/test/python/classical_function_compiler/test_classical_function.py b/test/python/classical_function_compiler/test_classical_function.py index 0f7747db1b66..2cf583e225e8 100644 --- a/test/python/classical_function_compiler/test_classical_function.py +++ b/test/python/classical_function_compiler/test_classical_function.py @@ -16,7 +16,6 @@ from qiskit.test import QiskitTestCase from qiskit.circuit.classicalfunction import classical_function as compile_classical_function -from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM from qiskit import QuantumCircuit from qiskit.circuit.library.standard_gates import XGate @@ -27,7 +26,6 @@ class TestOracleDecomposition(QiskitTestCase): """Tests ClassicalFunction.decomposition.""" - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_grover_oracle(self): """grover_oracle.decomposition""" oracle = compile_classical_function(examples.grover_oracle) diff --git a/test/python/classical_function_compiler/test_parse.py b/test/python/classical_function_compiler/test_parse.py index 5c5a3fafb9c1..1ca1ee8b7ef5 100644 --- a/test/python/classical_function_compiler/test_parse.py +++ b/test/python/classical_function_compiler/test_parse.py @@ -15,7 +15,6 @@ from qiskit.circuit.classicalfunction import ClassicalFunctionParseError from qiskit.circuit.classicalfunction import classical_function as compile_classical_function -from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM from qiskit.test import QiskitTestCase from . import bad_examples as examples @@ -28,28 +27,24 @@ def assertExceptionMessage(self, context, message): """Asserts the message of an exception context""" self.assertTrue(message in context.exception.args[0]) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_id_bad_return(self): """Trying to parse examples.id_bad_return raises ClassicalFunctionParseError""" with self.assertRaises(ClassicalFunctionParseError) as context: compile_classical_function(examples.id_bad_return) self.assertExceptionMessage(context, "return type error") - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_id_no_type_arg(self): """Trying to parse examples.id_no_type_arg raises ClassicalFunctionParseError""" with self.assertRaises(ClassicalFunctionParseError) as context: compile_classical_function(examples.id_no_type_arg) self.assertExceptionMessage(context, "argument type is needed") - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_id_no_type_return(self): """Trying to parse examples.id_no_type_return raises ClassicalFunctionParseError""" with self.assertRaises(ClassicalFunctionParseError) as context: compile_classical_function(examples.id_no_type_return) self.assertExceptionMessage(context, "return type is needed") - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_out_of_scope(self): """Trying to parse examples.out_of_scope raises ClassicalFunctionParseError""" with self.assertRaises(ClassicalFunctionParseError) as context: diff --git a/test/python/classical_function_compiler/test_simulate.py b/test/python/classical_function_compiler/test_simulate.py index 04b4c0941ac0..05de4216ac92 100644 --- a/test/python/classical_function_compiler/test_simulate.py +++ b/test/python/classical_function_compiler/test_simulate.py @@ -15,7 +15,6 @@ from ddt import ddt, data from qiskit.circuit.classicalfunction import classical_function as compile_classical_function -from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM from qiskit.test import QiskitTestCase from .utils import get_truthtable_from_function, example_list @@ -25,7 +24,6 @@ class TestSimulate(QiskitTestCase): """Tests LogicNetwork.simulate method""" @data(*example_list()) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_(self, a_callable): """Tests LogicSimulate.simulate() on all the examples""" network = compile_classical_function(a_callable) diff --git a/test/python/classical_function_compiler/test_synthesis.py b/test/python/classical_function_compiler/test_synthesis.py index 656c85b4cc1c..51ec566963e3 100644 --- a/test/python/classical_function_compiler/test_synthesis.py +++ b/test/python/classical_function_compiler/test_synthesis.py @@ -16,7 +16,6 @@ from qiskit.test import QiskitTestCase from qiskit.circuit.classicalfunction import classical_function as compile_classical_function -from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM from qiskit import QuantumCircuit, QuantumRegister from qiskit.circuit.library.standard_gates import XGate @@ -27,7 +26,6 @@ class TestSynthesis(QiskitTestCase): """Tests ClassicalFunction.synth method.""" - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_grover_oracle(self): """Synthesis of grover_oracle example""" oracle = compile_classical_function(examples.grover_oracle) @@ -39,7 +37,6 @@ def test_grover_oracle(self): self.assertEqual(quantum_circuit.name, "grover_oracle") self.assertEqual(quantum_circuit, expected) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_grover_oracle_arg_regs(self): """Synthesis of grover_oracle example with arg_regs""" oracle = compile_classical_function(examples.grover_oracle) diff --git a/test/python/classical_function_compiler/test_typecheck.py b/test/python/classical_function_compiler/test_typecheck.py index fce35bb64e6e..c871c8d1aeeb 100644 --- a/test/python/classical_function_compiler/test_typecheck.py +++ b/test/python/classical_function_compiler/test_typecheck.py @@ -16,7 +16,6 @@ from qiskit.test import QiskitTestCase from qiskit.circuit.classicalfunction import ClassicalFunctionCompilerTypeError from qiskit.circuit.classicalfunction import classical_function as compile_classical_function -from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM from . import examples, bad_examples @@ -24,21 +23,18 @@ class TestTypeCheck(QiskitTestCase): """Tests classicalfunction compiler type checker (good examples).""" - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_id(self): """Tests examples.identity type checking""" network = compile_classical_function(examples.identity) self.assertEqual(network.args, ["a"]) self.assertEqual(network.types, [{"Int1": "type", "a": "Int1", "return": "Int1"}]) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_bool_not(self): """Tests examples.bool_not type checking""" network = compile_classical_function(examples.bool_not) self.assertEqual(network.args, ["a"]) self.assertEqual(network.types, [{"Int1": "type", "a": "Int1", "return": "Int1"}]) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_id_assign(self): """Tests examples.id_assing type checking""" network = compile_classical_function(examples.id_assing) @@ -47,7 +43,6 @@ def test_id_assign(self): network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}] ) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_bit_and(self): """Tests examples.bit_and type checking""" network = compile_classical_function(examples.bit_and) @@ -56,7 +51,6 @@ def test_bit_and(self): network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}] ) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_bit_or(self): """Tests examples.bit_or type checking""" network = compile_classical_function(examples.bit_or) @@ -65,7 +59,6 @@ def test_bit_or(self): network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}] ) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_bool_or(self): """Tests examples.bool_or type checking""" network = compile_classical_function(examples.bool_or) @@ -82,7 +75,6 @@ def assertExceptionMessage(self, context, message): """Asserts the message of an exception context""" self.assertTrue(message in context.exception.args[0]) - @unittest.skipUnless(HAS_TWEEDLEDUM, "tweedledum not available") def test_bit_not(self): """Int1wise not does not work on bit (aka bool) ~True # -2 From 6f85a166a2c1ce1419f06a4117911dc22f590232 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 16 Jun 2021 13:22:07 -0400 Subject: [PATCH 02/14] Remove unused imports --- qiskit/circuit/classicalfunction/boolean_expression.py | 1 - qiskit/circuit/classicalfunction/classical_function_visitor.py | 1 - qiskit/circuit/classicalfunction/classicalfunction.py | 2 +- .../classical_function_compiler/test_classical_function.py | 2 -- test/python/classical_function_compiler/test_parse.py | 2 -- test/python/classical_function_compiler/test_simulate.py | 2 -- test/python/classical_function_compiler/test_synthesis.py | 2 -- test/python/classical_function_compiler/test_typecheck.py | 2 -- 8 files changed, 1 insertion(+), 13 deletions(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index 63a563ace771..f4f8b8b1f29f 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -19,7 +19,6 @@ from tweedledum.synthesis import pkrm_synth from qiskit.circuit import QuantumCircuit -from qiskit.exceptions import MissingOptionalLibraryError from .classical_element import ClassicalElement diff --git a/qiskit/circuit/classicalfunction/classical_function_visitor.py b/qiskit/circuit/classicalfunction/classical_function_visitor.py index 2ed8a1846c64..51bfdb6beb96 100644 --- a/qiskit/circuit/classicalfunction/classical_function_visitor.py +++ b/qiskit/circuit/classicalfunction/classical_function_visitor.py @@ -19,7 +19,6 @@ from tweedledum.classical import LogicNetwork -from qiskit.exceptions import MissingOptionalLibraryError from .exceptions import ClassicalFunctionParseError, ClassicalFunctionCompilerTypeError diff --git a/qiskit/circuit/classicalfunction/classicalfunction.py b/qiskit/circuit/classicalfunction/classicalfunction.py index 696812fb5f73..ddc4f3f61697 100644 --- a/qiskit/circuit/classicalfunction/classicalfunction.py +++ b/qiskit/circuit/classicalfunction/classicalfunction.py @@ -19,7 +19,7 @@ from tweedledum.synthesis import pkrm_synth from qiskit.circuit import QuantumCircuit, QuantumRegister -from qiskit.exceptions import MissingOptionalLibraryError, QiskitError +from qiskit.exceptions import QiskitError from .classical_element import ClassicalElement from .classical_function_visitor import ClassicalFunctionVisitor from .utils import tweedledum2qiskit diff --git a/test/python/classical_function_compiler/test_classical_function.py b/test/python/classical_function_compiler/test_classical_function.py index 2cf583e225e8..3d3ada14ec38 100644 --- a/test/python/classical_function_compiler/test_classical_function.py +++ b/test/python/classical_function_compiler/test_classical_function.py @@ -11,8 +11,6 @@ # that they have been altered from the originals. """Tests ClassicalFunction as a gate.""" -import unittest - from qiskit.test import QiskitTestCase from qiskit.circuit.classicalfunction import classical_function as compile_classical_function diff --git a/test/python/classical_function_compiler/test_parse.py b/test/python/classical_function_compiler/test_parse.py index 1ca1ee8b7ef5..dc532900c832 100644 --- a/test/python/classical_function_compiler/test_parse.py +++ b/test/python/classical_function_compiler/test_parse.py @@ -11,8 +11,6 @@ # that they have been altered from the originals. """Tests the classicalfunction parser.""" -import unittest - from qiskit.circuit.classicalfunction import ClassicalFunctionParseError from qiskit.circuit.classicalfunction import classical_function as compile_classical_function diff --git a/test/python/classical_function_compiler/test_simulate.py b/test/python/classical_function_compiler/test_simulate.py index 05de4216ac92..066def2dc62a 100644 --- a/test/python/classical_function_compiler/test_simulate.py +++ b/test/python/classical_function_compiler/test_simulate.py @@ -11,8 +11,6 @@ # that they have been altered from the originals. """Tests LogicNetwork.simulate method.""" -import unittest - from ddt import ddt, data from qiskit.circuit.classicalfunction import classical_function as compile_classical_function from qiskit.test import QiskitTestCase diff --git a/test/python/classical_function_compiler/test_synthesis.py b/test/python/classical_function_compiler/test_synthesis.py index 51ec566963e3..a3d6ce21407b 100644 --- a/test/python/classical_function_compiler/test_synthesis.py +++ b/test/python/classical_function_compiler/test_synthesis.py @@ -11,8 +11,6 @@ # that they have been altered from the originals. """Tests classicalfunction compiler synthesis.""" -import unittest - from qiskit.test import QiskitTestCase from qiskit.circuit.classicalfunction import classical_function as compile_classical_function diff --git a/test/python/classical_function_compiler/test_typecheck.py b/test/python/classical_function_compiler/test_typecheck.py index c871c8d1aeeb..e997ac3b9449 100644 --- a/test/python/classical_function_compiler/test_typecheck.py +++ b/test/python/classical_function_compiler/test_typecheck.py @@ -11,8 +11,6 @@ # that they have been altered from the originals. """Tests classicalfunction compiler type checker.""" -import unittest - from qiskit.test import QiskitTestCase from qiskit.circuit.classicalfunction import ClassicalFunctionCompilerTypeError from qiskit.circuit.classicalfunction import classical_function as compile_classical_function From 23d89c1000f7a50cdac0828b4e689dd45ea18c76 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 16 Jun 2021 14:06:57 -0400 Subject: [PATCH 03/14] Cleanup docstrings --- qiskit/circuit/classicalfunction/boolean_expression.py | 4 ---- qiskit/circuit/classicalfunction/classicalfunction.py | 1 - 2 files changed, 5 deletions(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index f4f8b8b1f29f..a639f1404f73 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -31,9 +31,6 @@ def __init__(self, expression: str, name: str = None) -> None: expression (str): The logical expression string. name (str): Optional. Instruction gate name. Otherwise part of the expression is going to be used. - - Raises: - MissingOptionalLibraryError: If tweedledum is not installed. Tweedledum is required. """ self._tweedledum_bool_expression = BoolFunction.from_expression(expression) @@ -103,7 +100,6 @@ def from_dimacs_file(cls, filename: str): BooleanExpression: A gate for the input string Raises: - MissingOptionalLibraryError: If tweedledum is not installed. Tweedledum is required. FileNotFoundError: If filename is not found. """ diff --git a/qiskit/circuit/classicalfunction/classicalfunction.py b/qiskit/circuit/classicalfunction/classicalfunction.py index ddc4f3f61697..dd770e714800 100644 --- a/qiskit/circuit/classicalfunction/classicalfunction.py +++ b/qiskit/circuit/classicalfunction/classicalfunction.py @@ -38,7 +38,6 @@ def __init__(self, source, name=None): name (str): Optional. Default: "*classicalfunction*". ClassicalFunction name. Raises: - MissingOptionalLibraryError: If tweedledum is not installed. QiskitError: If source is not a string. """ if not isinstance(source, str): From d9129ab6ae01f2d6d26dc6616b9890df40366efd Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 24 Jun 2021 22:41:35 +0200 Subject: [PATCH 04/14] black setup --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index c0937fddf9cb..b47791e10998 100755 --- a/setup.py +++ b/setup.py @@ -126,9 +126,9 @@ include_package_data=True, python_requires=">=3.6", extras_require={ - 'visualization': visualization_extras, - 'crosstalk-pass': z3_requirements, - 'all': visualization_extras + z3_requirements, + "visualization": visualization_extras, + "crosstalk-pass": z3_requirements, + "all": visualization_extras + z3_requirements, }, project_urls={ "Bug Tracker": "https://github.com/Qiskit/qiskit-terra/issues", From 62a2f0273ab4dd8547edd9b024fb52acccc89322 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 29 Jun 2021 07:42:19 -0400 Subject: [PATCH 05/14] Update requirements.txt Co-authored-by: Bruno Schmitt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 326702dd0bf5..e98ff8d32a5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,4 @@ fastjsonschema>=2.10 python-constraint>=1.4 python-dateutil>=2.8.0 symengine>0.7 ; platform_machine == 'x86_64' or platform_machine == 'aarch64' or platform_machine == 'ppc64le' or platform_machine == 'amd64' or platform_machine == 'arm64' -tweedledum>=1.0,<2.0 +tweedledum>=1.1,<2.0 From 017d4713502596dbcec806b0af0c2217cf9c0620 Mon Sep 17 00:00:00 2001 From: Bruno Schmitt Date: Tue, 29 Jun 2021 14:34:55 +0200 Subject: [PATCH 06/14] User defined variable order for oracle expressions --- qiskit/circuit/classicalfunction/boolean_expression.py | 5 +++-- qiskit/circuit/library/phase_oracle.py | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index a639f1404f73..3864f376fe40 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -25,15 +25,16 @@ class BooleanExpression(ClassicalElement): """The Boolean Expression gate.""" - def __init__(self, expression: str, name: str = None) -> None: + def __init__(self, expression: str, var_order: list = None, name: str = None) -> None: """ Args: expression (str): The logical expression string. + var_order(list): A list with the order in which variables will be created. (default: by appearance) name (str): Optional. Instruction gate name. Otherwise part of the expression is going to be used. """ - self._tweedledum_bool_expression = BoolFunction.from_expression(expression) + self._tweedledum_bool_expression = BoolFunction.from_expression(expression, 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 bd1033fe55ab..20cc35388d92 100644 --- a/qiskit/circuit/library/phase_oracle.py +++ b/qiskit/circuit/library/phase_oracle.py @@ -44,17 +44,19 @@ class PhaseOracle(QuantumCircuit): def __init__( self, expression: Union[str, ClassicalElement], + var_order: list = None, synthesizer: Optional[Callable[[BooleanExpression], QuantumCircuit]] = None, ) -> None: """Creates a PhaseOracle object Args: expression: A Python-like boolean expression. + var_order(list): A list with the order in which variables will be created. (default: by appearance) 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. """ if not isinstance(expression, ClassicalElement): - expression = BooleanExpression(expression) + expression = BooleanExpression(expression, var_order) self.boolean_expression = expression From 1f1d6e1cbfdcda7dc27cd25c7f743cb304212b76 Mon Sep 17 00:00:00 2001 From: Bruno Schmitt Date: Tue, 29 Jun 2021 15:36:55 +0200 Subject: [PATCH 07/14] Fix lint problems --- qiskit/circuit/classicalfunction/boolean_expression.py | 7 ++++--- qiskit/circuit/library/phase_oracle.py | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index 3864f376fe40..6557877560d1 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -29,9 +29,10 @@ def __init__(self, expression: str, var_order: list = None, name: str = None) -> """ Args: expression (str): The logical expression string. - var_order(list): A list with the order in which variables will be created. (default: by appearance) - 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) + name (str): Optional. Instruction gate name. Otherwise part of the expression is + going to be used. """ self._tweedledum_bool_expression = BoolFunction.from_expression(expression, var_order) diff --git a/qiskit/circuit/library/phase_oracle.py b/qiskit/circuit/library/phase_oracle.py index 20cc35388d92..d2da7df9bb00 100644 --- a/qiskit/circuit/library/phase_oracle.py +++ b/qiskit/circuit/library/phase_oracle.py @@ -51,7 +51,8 @@ def __init__( Args: expression: A Python-like boolean expression. - var_order(list): A list with the order in which variables will be created. (default: by appearance) + var_order(list): A list with the order in which variables will be created. + (default: by appearance) 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. """ From af72403715b8518c93b64d6b02de412e01c3334f Mon Sep 17 00:00:00 2001 From: Bruno Schmitt Date: Thu, 1 Jul 2021 18:39:54 +0200 Subject: [PATCH 08/14] Update qiskit/circuit/classicalfunction/boolean_expression.py Co-authored-by: Luciano Bello --- qiskit/circuit/classicalfunction/boolean_expression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index 6557877560d1..0c20a14b9f56 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -25,7 +25,7 @@ class BooleanExpression(ClassicalElement): """The Boolean Expression gate.""" - def __init__(self, expression: str, var_order: list = None, name: str = None) -> None: + def __init__(self, expression: str, name: str = None, var_order: list = None) -> None: """ Args: expression (str): The logical expression string. From ac03a546dea5fdf70db9f2228d180a727beb9a6c Mon Sep 17 00:00:00 2001 From: Bruno Schmitt Date: Fri, 2 Jul 2021 14:14:52 +0200 Subject: [PATCH 09/14] Tests and bug-fix --- qiskit/circuit/library/phase_oracle.py | 2 +- .../circuit/library/test_phase_oracle.py | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/qiskit/circuit/library/phase_oracle.py b/qiskit/circuit/library/phase_oracle.py index d2da7df9bb00..2de7d2d819e8 100644 --- a/qiskit/circuit/library/phase_oracle.py +++ b/qiskit/circuit/library/phase_oracle.py @@ -57,7 +57,7 @@ def __init__( If None is provided, Tweedledum's `pkrm_synth` with `phase_esop` will be used. """ if not isinstance(expression, ClassicalElement): - expression = BooleanExpression(expression, var_order) + expression = BooleanExpression(expression, var_order=var_order) self.boolean_expression = expression diff --git a/test/python/circuit/library/test_phase_oracle.py b/test/python/circuit/library/test_phase_oracle.py index 9482f2f98996..352418ad0abf 100644 --- a/test/python/circuit/library/test_phase_oracle.py +++ b/test/python/circuit/library/test_phase_oracle.py @@ -67,6 +67,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() From 2303dee5020df46f27104eb65b595b4949797bbe Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 2 Jul 2021 16:50:47 +0200 Subject: [PATCH 10/14] black --- test/python/circuit/library/test_phase_oracle.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/python/circuit/library/test_phase_oracle.py b/test/python/circuit/library/test_phase_oracle.py index 352418ad0abf..59c63e777deb 100644 --- a/test/python/circuit/library/test_phase_oracle.py +++ b/test/python/circuit/library/test_phase_oracle.py @@ -68,8 +68,8 @@ def test_statevector(self, expression, good_states): 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]), + ("((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): From 2b8fca419af042537c32d65919c066b140435c9a Mon Sep 17 00:00:00 2001 From: Bruno Schmitt Date: Sat, 10 Jul 2021 10:11:52 +0200 Subject: [PATCH 11/14] Add reno --- ...expression-var-order-d87e9b04fb5d545c.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml diff --git a/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml b/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml new file mode 100644 index 000000000000..c322eb0d1550 --- /dev/null +++ b/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml @@ -0,0 +1,19 @@ +--- +features: + - | + Add the ability for users to define the variable order when defining a + `PhaseOracle`. + + .. 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]) From 610dbf3ecb1ee3d2e536a87d2cba2caba9170844 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Thu, 2 Feb 2023 15:26:28 +0100 Subject: [PATCH 12/14] black --- test/python/circuit/library/test_phase_oracle.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/python/circuit/library/test_phase_oracle.py b/test/python/circuit/library/test_phase_oracle.py index 68cb60bcad87..e15d6df8ee60 100644 --- a/test/python/circuit/library/test_phase_oracle.py +++ b/test/python/circuit/library/test_phase_oracle.py @@ -83,10 +83,10 @@ def test_variable_order(self, expression, var_order, good_states): circuit.compose(oracle, inplace=True) statevector = Statevector.from_instruction(circuit) - valid_state = -1 / sqrt(2 ** num_qubits) - invalid_state = 1 / sqrt(2 ** num_qubits) + valid_state = -1 / sqrt(2**num_qubits) + invalid_state = 1 / sqrt(2**num_qubits) - states = list(range(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] From fb93bd38eca246e810d766e5cff60ad6e389dc80 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 3 Feb 2023 12:27:25 +0100 Subject: [PATCH 13/14] Consider order in the parameters --- qiskit/circuit/classicalfunction/boolean_expression.py | 6 +++--- qiskit/circuit/library/phase_oracle.py | 6 +++--- .../notes/expression-var-order-d87e9b04fb5d545c.yaml | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index bcbfa2617cad..c49f226785fb 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -29,13 +29,13 @@ def __init__(self, expression: str, name: str = None, var_order: list = None) -> """ Args: expression (str): The logical expression string. - var_order(list): A list with the order in which variables will be created. - (default: by appearance) 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, var_order) + 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 aa5a3cf95406..f00a8d56255a 100644 --- a/qiskit/circuit/library/phase_oracle.py +++ b/qiskit/circuit/library/phase_oracle.py @@ -51,17 +51,17 @@ class PhaseOracle(QuantumCircuit): def __init__( self, expression: Union[str, ClassicalElement], - var_order: list = None, synthesizer: Optional[Callable[[BooleanExpression], QuantumCircuit]] = None, + var_order: list = None, ) -> None: """Creates a PhaseOracle object Args: expression: A Python-like boolean expression. - var_order(list): A list with the order in which variables will be created. - (default: by appearance) 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 diff --git a/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml b/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml index c322eb0d1550..24f5b47da3d6 100644 --- a/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml +++ b/releasenotes/notes/expression-var-order-d87e9b04fb5d545c.yaml @@ -1,8 +1,7 @@ --- features: - | - Add the ability for users to define the variable order when defining a - `PhaseOracle`. + 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 da3bb949f4733a25c86025d676fb4c4c5376d3c1 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 3 Feb 2023 13:41:29 +0100 Subject: [PATCH 14/14] black --- qiskit/circuit/classicalfunction/boolean_expression.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index c49f226785fb..7a66f2a202bc 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -35,7 +35,9 @@ def __init__(self, expression: str, name: str = None, var_order: list = None) -> (default: by appearance) """ - self._tweedledum_bool_expression = BoolFunction.from_expression(expression, var_order=var_order) + 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 = (