Skip to content

Commit

Permalink
Changes according to PR review
Browse files Browse the repository at this point in the history
  • Loading branch information
gadial committed Feb 24, 2025
1 parent 991ded1 commit 454ecc5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 128 deletions.
27 changes: 15 additions & 12 deletions qiskit/circuit/library/bit_flip_oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@


class BitFlipOracleGate(Gate):
r"""Bit-flip Oracle Gate.
r"""Implements a bit-flip oracle
The Bit-flip Oracle Gate object constructs circuits for any arbitrary
input logical expressions. A logical expression is composed of logical operators
Expand Down Expand Up @@ -54,32 +54,35 @@ def __init__(
self,
expression: str,
var_order: list[str] | None = None,
label: str | None = None,
) -> None:
"""
Args:
expression: A Python-like boolean expression.
var_order: A list with the order in which variables will be created.
(default: by appearance)
label: A label for the gate to display in visualizations. Per default, the label is
set to display the textual represntation of the boolean expression (truncated if needed)
"""
self.boolean_expression = BooleanExpression(expression, var_order=var_order)
self.oracle = self.boolean_expression.synth(circuit_type="bit")
short_expr_for_name = (expression[:15] + "...") if len(expression) > 15 else expression

if label is None:
short_expr_for_name = (expression[:15] + "...") if len(expression) > 15 else expression
label = short_expr_for_name

super().__init__(
name="Bit-flip Oracle",
num_qubits=self.oracle.num_qubits,
num_qubits=self.boolean_expression.num_bits + 1,
params=[],
label=short_expr_for_name,
label=label,
)

def _define(self):
"""
Defined by the synthesized bit-flip oracle
"""
self.definition = self.oracle
"""Defined by the synthesized bit-flip oracle"""
self.definition = self.boolean_expression.synth(circuit_type="bit")

@classmethod
def from_dimacs_file(cls, filename: str):
def from_dimacs_file(cls, filename: str) -> BitFlipOracleGate:
r"""Create a BitFlipOracleGate from the string in the DIMACS format.
It is possible to build a BitFlipOracleGate from a file in `DIMACS CNF format
Expand All @@ -105,9 +108,9 @@ def from_dimacs_file(cls, filename: str):
the CNF is over three boolean variables --- let us call them :math:`x_1, x_2, x_3`, and
contains five clauses. The five clauses, listed afterwards, are implicitly joined by the
logical `AND` operator, :math:`\land`, while the variables in each clause, represented by
their indices, are implicitly disjoined by the logical `OR` operator, :math:`lor`. The
their indices, are implicitly disjoined by the logical `OR` operator, :math:`\lor`. The
:math:`-` symbol preceding a boolean variable index corresponds to the logical `NOT`
operator, :math:`lnot`. Character `0` (zero) marks the end of each clause. Essentially,
operator, :math:`\lnot`. Character `0` (zero) marks the end of each clause. Essentially,
the code above corresponds to the following CNF:
:math:`(\lnot x_1 \lor \lnot x_2 \lor \lnot x_3)
Expand Down
27 changes: 15 additions & 12 deletions qiskit/circuit/library/phase_oracle.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def from_dimacs_file(cls, filename: str):


class PhaseOracleGate(Gate):
r"""Phase PhaseOracleGate.
r"""Implements a phase oracle.
The Phase Oracle Gate object constructs circuits for any arbitrary
input logical expressions. A logical expression is composed of logical operators
Expand Down Expand Up @@ -163,32 +163,35 @@ def __init__(
self,
expression: str,
var_order: list[str] | None = None,
label: str | None = None,
) -> None:
"""
Args:
expression: A Python-like boolean expression.
var_order: A list with the order in which variables will be created.
(default: by appearance)
label: A label for the gate to display in visualizations. Per default, the label is
set to display the textual represntation of the boolean expression (truncated if needed)
"""
self.boolean_expression = BooleanExpression(expression, var_order=var_order)
self.oracle = self.boolean_expression.synth(circuit_type="phase")
short_expr_for_name = (expression[:15] + "...") if len(expression) > 15 else expression

if label is None:
short_expr_for_name = (expression[:15] + "...") if len(expression) > 15 else expression
label = short_expr_for_name

super().__init__(
name="Phase Oracle",
num_qubits=self.oracle.num_qubits,
num_qubits=self.boolean_expression.num_bits,
params=[],
label=short_expr_for_name,
label=label,
)

def _define(self):
"""
Defined by the synthesized phase oracle
"""
self.definition = self.oracle
"""Defined by the synthesized phase oracle"""
self.definition = self.boolean_expression.synth(circuit_type="phase")

@classmethod
def from_dimacs_file(cls, filename: str):
def from_dimacs_file(cls, filename: str) -> PhaseOracleGate:
r"""Create a PhaseOracle from the string in the DIMACS format.
It is possible to build a PhaseOracle from a file in `DIMACS CNF format
Expand All @@ -214,9 +217,9 @@ def from_dimacs_file(cls, filename: str):
the CNF is over three boolean variables --- let us call them :math:`x_1, x_2, x_3`, and
contains five clauses. The five clauses, listed afterwards, are implicitly joined by the
logical `AND` operator, :math:`\land`, while the variables in each clause, represented by
their indices, are implicitly disjoined by the logical `OR` operator, :math:`lor`. The
their indices, are implicitly disjoined by the logical `OR` operator, :math:`\lor`. The
:math:`-` symbol preceding a boolean variable index corresponds to the logical `NOT`
operator, :math:`lnot`. Character `0` (zero) marks the end of each clause. Essentially,
operator, :math:`\lnot`. Character `0` (zero) marks the end of each clause. Essentially,
the code above corresponds to the following CNF:
:math:`(\lnot x_1 \lor \lnot x_2 \lor \lnot x_3)
Expand Down
10 changes: 9 additions & 1 deletion qiskit/synthesis/boolean/boolean_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@


class TruthTable:
"""A simple implementation of a truth table for a boolean function"""
"""A simple implementation of a truth table for a boolean function
The truth table is built from a callable which takes an assignment, which
is a tuple of boolean values of a fixed given length (the number of bits
of the truth table) and returns a boolean value.
For a number of bits at most `EXPLICIT_REP_THRESHOLD` the values of the table
are explicitly computed and stored. Otherwise, the values are computed on the fly
and stored in a dictionary."""

EXPLICIT_REP_THRESHOLD = (
12 # above this number of bits, do not explicitly save the values in a list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ upgrade_circuits:
The interface of :class:`.PhaseOracle` was simplified; it no longer
accepts a `synthesizer` parameter, and the `expression` parameter
can only be a string; `ClassicalElement` has been deprecated.
can only be a string; `ClassicalElement` has been deprecated in Qiskit 1.4.
:class:`.PhaseOracle` is used exactly as before:
.. code-block:: python
from qiskit.circuit.library.phase_oracle import PhaseOracle
Expand All @@ -37,7 +38,7 @@ features_circuits:
(except the `evaluate_bitstring` method). Bit-flip oracle gate
synthesizes a bit flip oracle instead
of a phase flip oracle, meaning it acts on one additional qubit
and can be seen a applying a controlled X operation, where the
and can be seen as applying a controlled X operation, where the
control is the value of the expression encoded by the oracle.
.. code-block:: python
Expand Down
148 changes: 47 additions & 101 deletions test/python/circuit/library/test_phase_and_bitflip_oracles.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@


@ddt
class TestPhaseOracle(QiskitTestCase):
"""Test phase oracle object."""
class TestPhaseOracleAndGate(QiskitTestCase):
"""Test phase oracle and phase oracle gate objects."""

@data(
("x | x", "1", True),
Expand All @@ -50,87 +50,27 @@ def test_evaluate_bitstring(self, expression, input_bitstring, expected):
@unpack
def test_statevector(self, expression, truth_table):
"""Circuit generation"""
oracle = PhaseOracle(expression)
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))
good_states = [i for i in range(len(states)) if truth_table[i] == "1"]
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)

@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)


@ddt
class TestPhaseOracleGate(QiskitTestCase):
"""Test phase oracle object."""

@data(
("x | x", "01"),
("~x", "10"),
("x & y", "0001"),
("x & ~y", "0100"),
("(x0 & x1 | ~x2) ^ x4", "1111000100001110"),
("x & y ^ ( ~z1 | z2)", "1110000111101110"),
)
@unpack
def test_statevector(self, expression, truth_table):
"""Circuit generation"""
oracle = PhaseOracleGate(expression)
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))
good_states = [i for i in range(len(states)) if truth_table[i] == "1"]
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)
for use_gate in [True, False]:
oracle = PhaseOracleGate(expression) if use_gate else PhaseOracle(expression)
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))
good_states = [i for i in range(len(states)) if truth_table[i] == "1"]
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]
with self.subTest(use_gate=use_gate):
self.assertListEqual(expected_valid, result_valid)
self.assertListEqual(expected_invalid, result_invalid)

@data(
("((A & C) | (B & D)) & ~(C & D)", None, [3, 7, 12, 13]),
Expand All @@ -139,24 +79,30 @@ def test_statevector(self, expression, truth_table):
@unpack
def test_variable_order(self, expression, var_order, good_states):
"""Circuit generation"""
oracle = PhaseOracleGate(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)
for use_gate in [True, False]:
oracle = (
PhaseOracleGate(expression, var_order=var_order)
if use_gate
else 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]
with self.subTest(use_gate=use_gate):
self.assertListEqual(expected_valid, result_valid)
self.assertListEqual(expected_invalid, result_invalid)


@ddt
Expand Down

0 comments on commit 454ecc5

Please sign in to comment.