mirror of https://github.com/Qiskit/qiskit.git
Make tweedledum a hard requirement (#6588)
* 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 <bruno.schmitt@epfl.ch> Co-authored-by: Luciano Bello <bel@zurich.ibm.com> Co-authored-by: Bruno Schmitt <bruno.schmitt@epfl.ch>
This commit is contained in:
parent
8c062c7772
commit
927fd0226b
|
@ -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]
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
"""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):
|
||||
|
@ -31,17 +31,7 @@ class BooleanExpression(ClassicalElement):
|
|||
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.
|
||||
"""
|
||||
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 +53,6 @@ class BooleanExpression(ClassicalElement):
|
|||
Returns:
|
||||
bool: result of the evaluation.
|
||||
"""
|
||||
from tweedledum import BitVec
|
||||
|
||||
bits = []
|
||||
for bit in bitstring:
|
||||
bits.append(BitVec(1, bit))
|
||||
|
@ -92,8 +80,7 @@ class BooleanExpression(ClassicalElement):
|
|||
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)
|
||||
|
@ -113,16 +100,8 @@ class BooleanExpression(ClassicalElement):
|
|||
BooleanExpression: A gate for the input string
|
||||
|
||||
Raises:
|
||||
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):
|
||||
|
|
|
@ -17,8 +17,8 @@ This module is used internally by ``qiskit.transpiler.classicalfunction.Classica
|
|||
import ast
|
||||
import _ast
|
||||
|
||||
from qiskit.exceptions import MissingOptionalLibraryError
|
||||
from .utils import HAS_TWEEDLEDUM
|
||||
from tweedledum.classical import LogicNetwork
|
||||
|
||||
from .exceptions import ClassicalFunctionParseError, ClassicalFunctionCompilerTypeError
|
||||
|
||||
|
||||
|
@ -36,12 +36,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 +51,6 @@ class ClassicalFunctionVisitor(ast.NodeVisitor):
|
|||
|
||||
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)}
|
||||
|
|
|
@ -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 qiskit.exceptions import QiskitError
|
||||
from .classical_element import ClassicalElement
|
||||
from .utils import HAS_TWEEDLEDUM
|
||||
from .classical_function_visitor import ClassicalFunctionVisitor
|
||||
from .utils import tweedledum2qiskit
|
||||
|
||||
|
||||
class ClassicalFunction(ClassicalElement):
|
||||
|
@ -35,17 +38,10 @@ class ClassicalFunction(ClassicalElement):
|
|||
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):
|
||||
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 +107,6 @@ class ClassicalFunction(ClassicalElement):
|
|||
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 +127,6 @@ class ClassicalFunction(ClassicalElement):
|
|||
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 +153,6 @@ class ClassicalFunction(ClassicalElement):
|
|||
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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
The `tweedledum <https://pypi.org/project/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 <https://pypi.org/project/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.
|
|
@ -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.1,<2.0
|
||||
|
|
32
setup.py
32
setup.py
|
@ -77,6 +77,23 @@ with open(README_PATH) as readme_file:
|
|||
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",
|
||||
|
@ -109,19 +126,10 @@ setup(
|
|||
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,
|
||||
"bip-mapper": ["cplex", "docplex"],
|
||||
"crosstalk-pass": z3_requirements,
|
||||
"all": visualization_extras + z3_requirements,
|
||||
},
|
||||
project_urls={
|
||||
"Bug Tracker": "https://github.com/Qiskit/qiskit-terra/issues",
|
||||
|
|
|
@ -11,12 +11,9 @@
|
|||
# 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
|
||||
from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM
|
||||
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.circuit.library.standard_gates import XGate
|
||||
|
@ -27,7 +24,6 @@ from . import examples
|
|||
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)
|
||||
|
|
|
@ -11,11 +11,8 @@
|
|||
# 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
|
||||
from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM
|
||||
|
||||
from qiskit.test import QiskitTestCase
|
||||
from . import bad_examples as examples
|
||||
|
@ -28,28 +25,24 @@ class TestParseFail(QiskitTestCase):
|
|||
"""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:
|
||||
|
|
|
@ -11,11 +11,8 @@
|
|||
# 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.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM
|
||||
from qiskit.test import QiskitTestCase
|
||||
from .utils import get_truthtable_from_function, example_list
|
||||
|
||||
|
@ -25,7 +22,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)
|
||||
|
|
|
@ -11,12 +11,9 @@
|
|||
# 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
|
||||
from qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM
|
||||
|
||||
from qiskit import QuantumCircuit, QuantumRegister
|
||||
from qiskit.circuit.library.standard_gates import XGate
|
||||
|
@ -27,7 +24,6 @@ from . import examples
|
|||
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 +35,6 @@ class TestSynthesis(QiskitTestCase):
|
|||
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)
|
||||
|
|
|
@ -11,12 +11,9 @@
|
|||
# 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 qiskit.circuit.classicalfunction.classicalfunction import HAS_TWEEDLEDUM
|
||||
|
||||
from . import examples, bad_examples
|
||||
|
||||
|
@ -24,21 +21,18 @@ from . import examples, bad_examples
|
|||
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 +41,6 @@ class TestTypeCheck(QiskitTestCase):
|
|||
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 +49,6 @@ class TestTypeCheck(QiskitTestCase):
|
|||
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 +57,6 @@ class TestTypeCheck(QiskitTestCase):
|
|||
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 +73,6 @@ class TestTypeCheckFail(QiskitTestCase):
|
|||
"""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
|
||||
|
|
Loading…
Reference in New Issue