Skip to content

Commit d59e0c2

Browse files
authored
Remove dependance of BooleanExpression on tweedledum (Qiskit#13769)
* Remove dependance of BooleanExpression on tweedledum * Small fix for `BooleanExpression`: it still should inherit from `Gate` * Additional tests * Doc fixes and release notes * Refactoring; new boolean expression code moved to synthesis, old code restored. * Add tests for bit-flip oracle and bugfix for bit-flip oracle synthesis * Small fixes according to PR * Added a rudimentary truth table implementation to enable working implicitly with large number of bits * Bug fix related to truth table generation * Fixes according to PR review * Fixes according to PR review * Change BitFlipOracle to BitFlipOracleGate * Added PhaseOracleGate * Changes according to PR review
1 parent cc64dd7 commit d59e0c2

File tree

14 files changed

+1285
-144
lines changed

14 files changed

+1285
-144
lines changed

qiskit/circuit/library/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@
345345
QuantumVolume
346346
PhaseEstimation
347347
GroverOperator
348+
BitFlipOracleGate
349+
PhaseOracleGate
348350
PhaseOracle
349351
PauliEvolutionGate
350352
HamiltonianGate
@@ -670,6 +672,7 @@
670672
from .iqp import IQP, iqp, random_iqp
671673
from .phase_estimation import PhaseEstimation, phase_estimation
672674
from .grover_operator import GroverOperator, grover_operator
673-
from .phase_oracle import PhaseOracle
675+
from .phase_oracle import PhaseOracle, PhaseOracleGate
676+
from .bit_flip_oracle import BitFlipOracleGate
674677
from .overlap import UnitaryOverlap, unitary_overlap
675678
from .standard_gates import get_standard_gate_name_mapping
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Bit-flip Oracle object."""
14+
15+
from __future__ import annotations
16+
17+
from qiskit.circuit import Gate
18+
19+
from qiskit.synthesis.boolean.boolean_expression import BooleanExpression
20+
21+
22+
class BitFlipOracleGate(Gate):
23+
r"""Implements a bit-flip oracle
24+
25+
The Bit-flip Oracle Gate object constructs circuits for any arbitrary
26+
input logical expressions. A logical expression is composed of logical operators
27+
`&` (logical `AND`), `|` (logical `OR`),
28+
`~` (logical `NOT`), and `^` (logical `XOR`).
29+
as well as symbols for literals (variables).
30+
For example, `'a & b'`, and `(v0 | ~v1) & (~v2 & v3)`
31+
are both valid string representation of boolean logical expressions.
32+
33+
A bit-flip oracle for a boolean function `f(x)` performs the following
34+
quantum operation:
35+
36+
.. math::
37+
38+
|x\rangle|y\rangle \mapsto |x\rangle|f(x)\oplus y\rangle
39+
40+
For convenience, this oracle, in addition to parsing arbitrary logical expressions,
41+
also supports input strings in the `DIMACS CNF format
42+
<https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
43+
which is the standard format for specifying SATisfiability (SAT) problem instances in
44+
`Conjunctive Normal Form (CNF) <https://en.wikipedia.org/wiki/Conjunctive_normal_form>`__,
45+
which is a conjunction of one or more clauses, where a clause is a disjunction of one
46+
or more literals.
47+
See :meth:`qiskit.circuit.library.bit_flip_oracle.BitFlipOracleGate.from_dimacs_file`.
48+
49+
From 16 variables on, possible performance issues should be expected when using the
50+
default synthesizer.
51+
"""
52+
53+
def __init__(
54+
self,
55+
expression: str,
56+
var_order: list[str] | None = None,
57+
label: str | None = None,
58+
) -> None:
59+
"""
60+
Args:
61+
expression: A Python-like boolean expression.
62+
var_order: A list with the order in which variables will be created.
63+
(default: by appearance)
64+
label: A label for the gate to display in visualizations. Per default, the label is
65+
set to display the textual represntation of the boolean expression (truncated if needed)
66+
"""
67+
self.boolean_expression = BooleanExpression(expression, var_order=var_order)
68+
69+
if label is None:
70+
short_expr_for_name = (expression[:15] + "...") if len(expression) > 15 else expression
71+
label = short_expr_for_name
72+
73+
super().__init__(
74+
name="Bit-flip Oracle",
75+
num_qubits=self.boolean_expression.num_bits + 1,
76+
params=[],
77+
label=label,
78+
)
79+
80+
def _define(self):
81+
"""Defined by the synthesized bit-flip oracle"""
82+
self.definition = self.boolean_expression.synth(circuit_type="bit")
83+
84+
@classmethod
85+
def from_dimacs_file(cls, filename: str) -> BitFlipOracleGate:
86+
r"""Create a BitFlipOracleGate from the string in the DIMACS format.
87+
88+
It is possible to build a BitFlipOracleGate from a file in `DIMACS CNF format
89+
<https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
90+
which is the standard format for specifying SATisfiability (SAT) problem instances in
91+
`Conjunctive Normal Form (CNF) <https://en.wikipedia.org/wiki/Conjunctive_normal_form>`__,
92+
which is a conjunction of one or more clauses, where a clause is a disjunction of one
93+
or more literals.
94+
95+
The following is an example of a CNF expressed in the DIMACS format:
96+
97+
.. code:: text
98+
99+
c DIMACS CNF file with 3 satisfying assignments: 1 -2 3, -1 -2 -3, 1 2 -3.
100+
p cnf 3 5
101+
-1 -2 -3 0
102+
1 -2 3 0
103+
1 2 -3 0
104+
1 -2 -3 0
105+
-1 2 3 0
106+
107+
The first line, following the `c` character, is a comment. The second line specifies that
108+
the CNF is over three boolean variables --- let us call them :math:`x_1, x_2, x_3`, and
109+
contains five clauses. The five clauses, listed afterwards, are implicitly joined by the
110+
logical `AND` operator, :math:`\land`, while the variables in each clause, represented by
111+
their indices, are implicitly disjoined by the logical `OR` operator, :math:`\lor`. The
112+
:math:`-` symbol preceding a boolean variable index corresponds to the logical `NOT`
113+
operator, :math:`\lnot`. Character `0` (zero) marks the end of each clause. Essentially,
114+
the code above corresponds to the following CNF:
115+
116+
:math:`(\lnot x_1 \lor \lnot x_2 \lor \lnot x_3)
117+
\land (x_1 \lor \lnot x_2 \lor x_3)
118+
\land (x_1 \lor x_2 \lor \lnot x_3)
119+
\land (x_1 \lor \lnot x_2 \lor \lnot x_3)
120+
\land (\lnot x_1 \lor x_2 \lor x_3)`.
121+
122+
123+
Args:
124+
filename: A file in DIMACS format.
125+
126+
Returns:
127+
BitFlipOracleGate: A quantum gate with a bit-flip oracle.
128+
"""
129+
expr = BooleanExpression.from_dimacs_file(filename)
130+
return cls(expr)

qiskit/circuit/library/phase_oracle.py

Lines changed: 128 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,34 @@
1212

1313
"""Phase Oracle object."""
1414

15-
# Needed to avoid type hints from erroring when `classicalfunction` might not be available.
1615
from __future__ import annotations
1716

18-
from typing import Union, Callable, Optional, TYPE_CHECKING
17+
from qiskit.circuit import QuantumCircuit, Gate
1918

20-
from qiskit.circuit import QuantumCircuit
21-
from qiskit.utils import optionals as _optionals
19+
from qiskit.synthesis.boolean.boolean_expression import BooleanExpression
2220

23-
if TYPE_CHECKING:
24-
from qiskit.circuit.classicalfunction.boolean_expression import BooleanExpression
25-
from qiskit.circuit.classicalfunction.classical_element import ClassicalElement
2621

27-
28-
@_optionals.HAS_TWEEDLEDUM.require_in_instance
2922
class PhaseOracle(QuantumCircuit):
3023
r"""Phase Oracle.
3124
3225
The Phase Oracle object constructs circuits for any arbitrary
3326
input logical expressions. A logical expression is composed of logical operators
34-
`&` (`AND`), `|` (`OR`), `~` (`NOT`), and `^` (`XOR`).
27+
`&` (logical `AND`), `|` (logical `OR`),
28+
`~` (logical `NOT`), and `^` (logical `XOR`).
3529
as well as symbols for literals (variables).
3630
For example, `'a & b'`, and `(v0 | ~v1) & (~v2 & v3)`
3731
are both valid string representation of boolean logical expressions.
3832
33+
A phase oracle for a boolean function `f(x)` performs the following
34+
quantum operation:
35+
36+
.. math::
37+
38+
|x\rangle \mapsto (-1)^{f(x)}|x\rangle
39+
3940
For convenience, this oracle, in addition to parsing arbitrary logical expressions,
4041
also supports input strings in the `DIMACS CNF format
41-
<http://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
42+
<https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
4243
which is the standard format for specifying SATisfiability (SAT) problem instances in
4344
`Conjunctive Normal Form (CNF) <https://en.wikipedia.org/wiki/Conjunctive_normal_form>`__,
4445
which is a conjunction of one or more clauses, where a clause is a disjunction of one
@@ -50,40 +51,17 @@ class PhaseOracle(QuantumCircuit):
5051

5152
def __init__(
5253
self,
53-
expression: Union[str, ClassicalElement],
54-
synthesizer: Optional[Callable[[BooleanExpression], QuantumCircuit]] = None,
55-
var_order: list = None,
54+
expression: str,
55+
var_order: list[str] | None = None,
5656
) -> None:
57-
"""Creates a PhaseOracle object
58-
57+
"""
5958
Args:
6059
expression: A Python-like boolean expression.
61-
synthesizer: Optional. A function to convert a BooleanExpression into a QuantumCircuit
62-
If None is provided, Tweedledum's `pkrm_synth` with `phase_esop` will be used.
63-
var_order(list): A list with the order in which variables will be created.
60+
var_order: A list with the order in which variables will be created.
6461
(default: by appearance)
6562
"""
66-
from qiskit.circuit.classicalfunction.boolean_expression import BooleanExpression
67-
from qiskit.circuit.classicalfunction.classical_element import ClassicalElement
68-
69-
if not isinstance(expression, ClassicalElement):
70-
expression = BooleanExpression(expression, var_order=var_order)
71-
72-
self.boolean_expression = expression
73-
74-
if synthesizer is None:
75-
76-
def synthesizer(boolean_expression):
77-
from tweedledum.synthesis import pkrm_synth # pylint: disable=import-error
78-
from qiskit.circuit.classicalfunction.utils import tweedledum2qiskit
79-
80-
truth_table = boolean_expression._tweedledum_bool_expression.truth_table(
81-
output_bit=0
82-
)
83-
tweedledum_circuit = pkrm_synth(truth_table, {"pkrm_synth": {"phase_esop": True}})
84-
return tweedledum2qiskit(tweedledum_circuit)
85-
86-
oracle = expression.synth(synthesizer=synthesizer)
63+
self.boolean_expression = BooleanExpression(expression, var_order=var_order)
64+
oracle = self.boolean_expression.synth(circuit_type="phase")
8765

8866
super().__init__(oracle.num_qubits, name="Phase Oracle")
8967

@@ -107,7 +85,7 @@ def from_dimacs_file(cls, filename: str):
10785
r"""Create a PhaseOracle from the string in the DIMACS format.
10886
10987
It is possible to build a PhaseOracle from a file in `DIMACS CNF format
110-
<http://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
88+
<https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
11189
which is the standard format for specifying SATisfiability (SAT) problem instances in
11290
`Conjunctive Normal Form (CNF) <https://en.wikipedia.org/wiki/Conjunctive_normal_form>`__,
11391
which is a conjunction of one or more clauses, where a clause is a disjunction of one
@@ -147,7 +125,115 @@ def from_dimacs_file(cls, filename: str):
147125
Returns:
148126
PhaseOracle: A quantum circuit with a phase oracle.
149127
"""
150-
from qiskit.circuit.classicalfunction.boolean_expression import BooleanExpression
128+
expr = BooleanExpression.from_dimacs_file(filename)
129+
return cls(expr)
130+
131+
132+
class PhaseOracleGate(Gate):
133+
r"""Implements a phase oracle.
134+
135+
The Phase Oracle Gate object constructs circuits for any arbitrary
136+
input logical expressions. A logical expression is composed of logical operators
137+
`&` (logical `AND`), `|` (logical `OR`),
138+
`~` (logical `NOT`), and `^` (logical `XOR`).
139+
as well as symbols for literals (variables).
140+
For example, `'a & b'`, and `(v0 | ~v1) & (~v2 & v3)`
141+
are both valid string representation of boolean logical expressions.
142+
143+
A phase oracle for a boolean function `f(x)` performs the following
144+
quantum operation:
145+
146+
.. math::
147+
148+
|x\rangle \mapsto (-1)^{f(x)}|x\rangle
149+
150+
For convenience, this oracle, in addition to parsing arbitrary logical expressions,
151+
also supports input strings in the `DIMACS CNF format
152+
<https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
153+
which is the standard format for specifying SATisfiability (SAT) problem instances in
154+
`Conjunctive Normal Form (CNF) <https://en.wikipedia.org/wiki/Conjunctive_normal_form>`__,
155+
which is a conjunction of one or more clauses, where a clause is a disjunction of one
156+
or more literals. See :meth:`qiskit.circuit.library.phase_oracle.PhaseOracleGate.from_dimacs_file`.
157+
158+
From 16 variables on, possible performance issues should be expected when using the
159+
default synthesizer.
160+
"""
161+
162+
def __init__(
163+
self,
164+
expression: str,
165+
var_order: list[str] | None = None,
166+
label: str | None = None,
167+
) -> None:
168+
"""
169+
Args:
170+
expression: A Python-like boolean expression.
171+
var_order: A list with the order in which variables will be created.
172+
(default: by appearance)
173+
label: A label for the gate to display in visualizations. Per default, the label is
174+
set to display the textual represntation of the boolean expression (truncated if needed)
175+
"""
176+
self.boolean_expression = BooleanExpression(expression, var_order=var_order)
151177

178+
if label is None:
179+
short_expr_for_name = (expression[:15] + "...") if len(expression) > 15 else expression
180+
label = short_expr_for_name
181+
182+
super().__init__(
183+
name="Phase Oracle",
184+
num_qubits=self.boolean_expression.num_bits,
185+
params=[],
186+
label=label,
187+
)
188+
189+
def _define(self):
190+
"""Defined by the synthesized phase oracle"""
191+
self.definition = self.boolean_expression.synth(circuit_type="phase")
192+
193+
@classmethod
194+
def from_dimacs_file(cls, filename: str) -> PhaseOracleGate:
195+
r"""Create a PhaseOracle from the string in the DIMACS format.
196+
197+
It is possible to build a PhaseOracle from a file in `DIMACS CNF format
198+
<https://web.archive.org/web/20190325181937/https://www.satcompetition.org/2009/format-benchmarks2009.html>`__,
199+
which is the standard format for specifying SATisfiability (SAT) problem instances in
200+
`Conjunctive Normal Form (CNF) <https://en.wikipedia.org/wiki/Conjunctive_normal_form>`__,
201+
which is a conjunction of one or more clauses, where a clause is a disjunction of one
202+
or more literals.
203+
204+
The following is an example of a CNF expressed in the DIMACS format:
205+
206+
.. code:: text
207+
208+
c DIMACS CNF file with 3 satisfying assignments: 1 -2 3, -1 -2 -3, 1 2 -3.
209+
p cnf 3 5
210+
-1 -2 -3 0
211+
1 -2 3 0
212+
1 2 -3 0
213+
1 -2 -3 0
214+
-1 2 3 0
215+
216+
The first line, following the `c` character, is a comment. The second line specifies that
217+
the CNF is over three boolean variables --- let us call them :math:`x_1, x_2, x_3`, and
218+
contains five clauses. The five clauses, listed afterwards, are implicitly joined by the
219+
logical `AND` operator, :math:`\land`, while the variables in each clause, represented by
220+
their indices, are implicitly disjoined by the logical `OR` operator, :math:`\lor`. The
221+
:math:`-` symbol preceding a boolean variable index corresponds to the logical `NOT`
222+
operator, :math:`\lnot`. Character `0` (zero) marks the end of each clause. Essentially,
223+
the code above corresponds to the following CNF:
224+
225+
:math:`(\lnot x_1 \lor \lnot x_2 \lor \lnot x_3)
226+
\land (x_1 \lor \lnot x_2 \lor x_3)
227+
\land (x_1 \lor x_2 \lor \lnot x_3)
228+
\land (x_1 \lor \lnot x_2 \lor \lnot x_3)
229+
\land (\lnot x_1 \lor x_2 \lor x_3)`.
230+
231+
232+
Args:
233+
filename: A file in DIMACS format.
234+
235+
Returns:
236+
PhaseOracleGate: A quantum circuit with a phase oracle.
237+
"""
152238
expr = BooleanExpression.from_dimacs_file(filename)
153239
return cls(expr)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This code is part of Qiskit.
2+
#
3+
# (C) Copyright IBM 2025.
4+
#
5+
# This code is licensed under the Apache License, Version 2.0. You may
6+
# obtain a copy of this license in the LICENSE.txt file in the root directory
7+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
#
9+
# Any modifications or derivative works of this code must retain this
10+
# copyright notice, and modified files need to carry a notice indicating
11+
# that they have been altered from the originals.
12+
13+
"""Module containing boolean expression synthesis."""

0 commit comments

Comments
 (0)