Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate qiskit.circuit.classicalfunction #13786

Merged
merged 16 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion qiskit/circuit/classicalfunction/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
Overview
========

.. warning::

This module is deprecated as of Qiskit 1.4.0. It will be removed in the Qiskit 2.0 release.

The classical function compiler provides the necessary tools to map a classical
potentially irreversible functions into quantum circuits. Below is a simple example of
how to synthesize a simple boolean function defined using Python into a
Expand Down Expand Up @@ -111,7 +115,7 @@ def grover_oracle(a: Int1, b: Int1, c: Int1, d: Int1) -> Int1:
ClassicalFunctionCompilerTypeError

"""

from qiskit.utils.deprecation import deprecate_func
from .classicalfunction import ClassicalFunction
from .exceptions import (
ClassicalFunctionParseError,
Expand All @@ -121,6 +125,11 @@ def grover_oracle(a: Int1, b: Int1, c: Int1, d: Int1) -> Int1:
from .boolean_expression import BooleanExpression


@deprecate_func(
since="1.4",
removal_timeline="in Qiskit 2.0",
additional_msg="Use `PhaseOracle` or `BitFlipOracle` instead",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BitFlipOracle doesn't exist in Qiskit 1.4, we need to remove mentioning it from the deprecation messages. Since the PhaseOracle is not a direct replacement of the classical functions but requires extra steps, it might be good to tell the users how to get there

Suggested change
additional_msg="Use `PhaseOracle` or `BitFlipOracle` instead",
additional_msg=(
"Use the PhaseOracle instead, which can be turned into a "
"bit-flip oracle by applying Hadamard gates on the target "
"qubit before and after the instruction."
),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4510aee

)
def classical_function(func):
"""
Parses and type checks the callable ``func`` to compile it into an ``ClassicalFunction``
Expand Down
8 changes: 7 additions & 1 deletion qiskit/circuit/classicalfunction/boolean_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
from os.path import basename, isfile
from typing import Callable, Optional

from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit import QuantumCircuit
from qiskit.utils.optionals import HAS_TWEEDLEDUM
from .classical_element import ClassicalElement


@HAS_TWEEDLEDUM.require_in_instance
class BooleanExpression(ClassicalElement):
"""The Boolean Expression gate."""

@HAS_TWEEDLEDUM.require_in_instance
@deprecate_func(
since="1.4",
removal_timeline="in Qiskit 2.0",
additional_msg="Use `PhaseOracle` or `BitFlipOracle` instead",
)
def __init__(self, expression: str, name: str = None, var_order: list = None) -> None:
"""
Args:
Expand Down
8 changes: 7 additions & 1 deletion qiskit/circuit/classicalfunction/classicalfunction.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import ast
from typing import Callable, Optional

from qiskit.utils.deprecation import deprecate_func
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.exceptions import QiskitError
from qiskit.utils.optionals import HAS_TWEEDLEDUM
Expand All @@ -23,10 +24,15 @@
from .utils import tweedledum2qiskit


@HAS_TWEEDLEDUM.require_in_instance
class ClassicalFunction(ClassicalElement):
"""Represent a classical function and its logic network."""

@HAS_TWEEDLEDUM.require_in_instance
@deprecate_func(
since="1.4",
removal_timeline="in Qiskit 2.0",
additional_msg="Use `PhaseOracle` or `BitFlipOracle` instead",
)
def __init__(self, source, name=None):
"""Creates a ``ClassicalFunction`` from Python source code in ``source``.

Expand Down
8 changes: 7 additions & 1 deletion qiskit/circuit/classicalfunction/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Exceptions for ClassicalFunction compiler"""
"""Exceptions for ClassicalFunction compiler

.. warning::

This module is deprecated as of qiskit 1.4.0 version. It will be removed in the Qiskit 2.0 release.

"""

from qiskit.exceptions import QiskitError

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
deprecations_circuits:
- |
The :mod:`qiskit.circuit.classicalfunction` and with it
the :class:`.ClassicalFunction`, its related :func:`.classical_function`
and :class:`.BooleanExpression` have been deprecated as of Qiskit 1.4
and will be removed in Qiskit 2.0. This change is done to avoid a
dependency on the external library `tweedledum`, which is no longer
compatible with all of Qiskit's supported platforms and Python versions.
For a similar functionality please use the :class:`.PhaseOracle` which
is going to have a `tweedledum` independent implementation, and
the :class:`.BitFlipOracle` which will be added in Qiskit 2.0.
Comment on lines +11 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also add a code snippet as example if we want to be extra nice to the users 😄

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I second that, and maybe the release notes would be a perfect place to include an example on how to convert the phase oracle to the bit-flip oracle.

18 changes: 15 additions & 3 deletions test/python/circuit/library/test_phase_oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ class TestPhaseOracle(QiskitTestCase):
@unpack
def test_evaluate_bitstring(self, expression, input_bitstring, expected):
"""PhaseOracle(...).evaluate_bitstring"""
oracle = PhaseOracle(expression)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="BooleanExpression`` is deprecated as of qiskit 1.4",
):
oracle = PhaseOracle(expression)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PhaseOracle is not getting deprecated, it should not emit a deprecation warning. Instead we can filter the warning in the PhaseOracle code in 1.4 until we get the tweedledum-free version in 2.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4510aee

result = oracle.evaluate_bitstring(input_bitstring)
self.assertEqual(result, expected)

Expand All @@ -50,7 +54,11 @@ def test_evaluate_bitstring(self, expression, input_bitstring, expected):
@unpack
def test_statevector(self, expression, good_states):
"""Circuit generation"""
oracle = PhaseOracle(expression)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="BooleanExpression`` is deprecated as of qiskit 1.4",
):
oracle = PhaseOracle(expression)
num_qubits = oracle.num_qubits
circuit = QuantumCircuit(num_qubits)
circuit.h(range(num_qubits))
Expand All @@ -76,7 +84,11 @@ def test_statevector(self, expression, good_states):
@unpack
def test_variable_order(self, expression, var_order, good_states):
"""Circuit generation"""
oracle = PhaseOracle(expression, var_order=var_order)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="BooleanExpression`` is deprecated as of qiskit 1.4",
):
oracle = PhaseOracle(expression, var_order=var_order)
num_qubits = oracle.num_qubits
circuit = QuantumCircuit(num_qubits)
circuit.h(range(num_qubits))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ class TestBooleanExpression(QiskitTestCase):
@unpack
def test_evaluate(self, expression, input_bitstring, expected):
"""Test simulate"""
expression = BooleanExpression(expression)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="BooleanExpression`` is deprecated as of qiskit 1.4",
):
expression = BooleanExpression(expression)
result = expression.simulate(input_bitstring)
self.assertEqual(result, expected)

Expand All @@ -53,7 +57,11 @@ def test_evaluate(self, expression, input_bitstring, expected):
@unpack
def test_synth(self, expression, expected):
"""Test synth"""
expression = BooleanExpression(expression)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="BooleanExpression`` is deprecated as of qiskit 1.4",
):
expression = BooleanExpression(expression)
expr_circ = expression.synth()

new_creg = expr_circ._create_creg(1, "c")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ class TestOracleDecomposition(QiskitTestCase):

def test_grover_oracle(self):
"""grover_oracle.decomposition"""
oracle = compile_classical_function(examples.grover_oracle)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
oracle = compile_classical_function(examples.grover_oracle)
quantum_circuit = QuantumCircuit(5)
quantum_circuit.append(oracle, [2, 1, 0, 3, 4])

Expand Down
24 changes: 20 additions & 4 deletions test/python/classical_function_compiler/test_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,39 @@ def assertExceptionMessage(self, context, message):
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)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of Qiskit 1.4",
):
compile_classical_function(examples.id_bad_return)
self.assertExceptionMessage(context, "return type error")

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)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of Qiskit 1.4",
):
compile_classical_function(examples.id_no_type_arg)
self.assertExceptionMessage(context, "argument type is needed")

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)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of Qiskit 1.4",
):
compile_classical_function(examples.id_no_type_return)
self.assertExceptionMessage(context, "return type is needed")

def test_out_of_scope(self):
"""Trying to parse examples.out_of_scope raises ClassicalFunctionParseError"""
with self.assertRaises(ClassicalFunctionParseError) as context:
compile_classical_function(examples.out_of_scope)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of Qiskit 1.4",
):
compile_classical_function(examples.out_of_scope)
self.assertExceptionMessage(context, "out of scope: c")
6 changes: 5 additions & 1 deletion test/python/classical_function_compiler/test_simulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class TestSimulate(QiskitTestCase):
@data(*utils.example_list())
def test_(self, a_callable):
"""Tests LogicSimulate.simulate() on all the examples"""
network = compile_classical_function(a_callable)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(a_callable)
truth_table = network.simulate_all()
self.assertEqual(truth_table, utils.get_truthtable_from_function(a_callable))
12 changes: 10 additions & 2 deletions test/python/classical_function_compiler/test_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ class TestSynthesis(QiskitTestCase):

def test_grover_oracle(self):
"""Synthesis of grover_oracle example"""
oracle = compile_classical_function(examples.grover_oracle)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
oracle = compile_classical_function(examples.grover_oracle)
quantum_circuit = oracle.synth()

expected = QuantumCircuit(5)
Expand All @@ -42,7 +46,11 @@ def test_grover_oracle(self):

def test_grover_oracle_arg_regs(self):
"""Synthesis of grover_oracle example with arg_regs"""
oracle = compile_classical_function(examples.grover_oracle)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
oracle = compile_classical_function(examples.grover_oracle)
quantum_circuit = oracle.synth(registerless=False)

qr_a = QuantumRegister(1, "a")
Expand Down
42 changes: 35 additions & 7 deletions test/python/classical_function_compiler/test_typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,67 @@ class TestTypeCheck(QiskitTestCase):

def test_id(self):
"""Tests examples.identity type checking"""
network = compile_classical_function(examples.identity)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(examples.identity)
self.assertEqual(network.args, ["a"])
self.assertEqual(network.types, [{"Int1": "type", "a": "Int1", "return": "Int1"}])

def test_bool_not(self):
"""Tests examples.bool_not type checking"""
network = compile_classical_function(examples.bool_not)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(examples.bool_not)
self.assertEqual(network.args, ["a"])
self.assertEqual(network.types, [{"Int1": "type", "a": "Int1", "return": "Int1"}])

def test_id_assign(self):
"""Tests examples.id_assing type checking"""
network = compile_classical_function(examples.id_assing)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(examples.id_assing)
self.assertEqual(network.args, ["a"])
self.assertEqual(
network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}]
)

def test_bit_and(self):
"""Tests examples.bit_and type checking"""
network = compile_classical_function(examples.bit_and)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(examples.bit_and)
self.assertEqual(network.args, ["a", "b"])
self.assertEqual(
network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}]
)

def test_bit_or(self):
"""Tests examples.bit_or type checking"""
network = compile_classical_function(examples.bit_or)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(examples.bit_or)
self.assertEqual(network.args, ["a", "b"])
self.assertEqual(
network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}]
)

def test_bool_or(self):
"""Tests examples.bool_or type checking"""
network = compile_classical_function(examples.bool_or)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
network = compile_classical_function(examples.bool_or)
self.assertEqual(network.args, ["a", "b"])
self.assertEqual(
network.types, [{"Int1": "type", "a": "Int1", "b": "Int1", "return": "Int1"}]
Expand All @@ -88,5 +112,9 @@ def test_bit_not(self):
~False # -1
"""
with self.assertRaises(ClassicalFunctionCompilerTypeError) as context:
compile_classical_function(bad_examples.bit_not)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex="ClassicalFunction`` is deprecated as of qiskit 1.4",
):
compile_classical_function(bad_examples.bit_not)
self.assertExceptionMessage(context, "does not operate with Int1 type")
12 changes: 8 additions & 4 deletions test/python/visualization/test_circuit_text_drawer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1397,12 +1397,16 @@ def test_text_synth_no_registerless(self):
" └───┘",
]
)
with self.assertWarnsRegex(
DeprecationWarning,
expected_regex=r"classical_function\(\)`` is deprecated as of qiskit 1.4",
):

@classical_function
def grover_oracle(a: Int1, b: Int1, c: Int1) -> Int1:
return a and b and not c
@classical_function
def grover_oracle(a: Int1, b: Int1, c: Int1) -> Int1:
return a and b and not c

circuit = grover_oracle.synth(registerless=False)
circuit = grover_oracle.synth(registerless=False)
self.assertEqual(str(circuit_drawer(circuit, output="text", initial_state=True)), expected)


Expand Down