Source code for qolumbina.programs.deutsch_jozsa.dj
# This code is refactored by the Jupyter Notebook from
# https://github.com/Qiskit/textbook/blob/aebdd2bc86ddb7a79dd8441d52c839d312ffafbb/notebooks/ch-algorithms/deutsch-jozsa.ipynb
#
# The corresponding repository is
# https://github.com/Qiskit/textbook?tab=Apache-2.0-1-ov-file
#
# This program is adapted for use as a benchmark in controlled software testing experiments.
# Modifications made to the original code include (for Apache License 2.0):
# - Refactored to unify the inherent class as QuantumCircuit:
# - Exposed unified program interfaces.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
# initialization
import numpy as np
# importing Qiskit
from qiskit.circuit import QuantumCircuit, Gate
from typing import Literal
# ---------- benchmark registration ----------
from ..benchmark_registry import register_benchmark
from pathlib import Path
@register_benchmark(
Path(__file__).stem,
family=Path(__file__).resolve().parent.name,
description="Deutsch-Jozsa algorithm: Determine if a function is constant or balanced with a single oracle query.",
class_name="DeutschJozsa",
source={
"repo": "https://github.com/Qiskit/textbook?tab=Apache-2.0-1-ov-file",
"file": "notebooks/ch-algorithms/deutsch-jozsa.ipynb",
"sdk": "Qiskit",
"available_doc": True
},
testability_refactoring=[
"Structure reorganization",
"input validation",
],
)
def create_deutsch_jozsa(
case: Literal["balanced", "constant"],
input_qubits: int,
) -> QuantumCircuit:
return DeutschJozsa(
case=case,
input_qubits=input_qubits
)
[docs]
class DeutschJozsa(QuantumCircuit):
r"""
Deutsch-Jozsa algorithm circuit.
Acts on :math:`n` input qubits and :math:`1` auxiliary qubit
"""
def __init__(self, case: Literal["balanced", "constant"], input_qubits: int, name: str | None = None):
r"""
Args:
case: Type of oracle, either "balanced" or "constant".
input_qubits: Number of input qubits :math:`n`.
name: Optional name for the circuit.
Raises:
ValueError: If ``case`` is not ``"balanced"`` or ``"constant"``.
Note:
The oracle is randomly generated according to the specified ``case``.
"""
if case not in ["balanced", "constant"]:
raise ValueError("case must be either 'balanced' or 'constant'")
super().__init__(input_qubits + 1, name=name or "DJ")
self._case = case
self._num_qubits = input_qubits
# Build the circuit
self._build()
[docs]
def dj_oracle(self) -> Gate:
"""
Construct the oracle gate based on the specified case (balanced or constant).
"""
# We need to make a QuantumCircuit object to return
# This circuit has n+1 qubits: the size of the input,
# plus one output qubit
oracle_qc = QuantumCircuit(self._num_qubits + 1)
# First, let's deal with the case in which oracle is balanced
if self._case == "balanced":
# First generate a random number that tells us which CNOTs to
# wrap in X-gates:
b = np.random.randint(1, 2 ** self._num_qubits)
# Next, format 'b' as a binary string of length 'n', padded with zeros:
b_str = format(b, '0' + str(self._num_qubits) +'b')
# Next, we place the first X-gates. Each digit in our binary string
# corresponds to a qubit, if the digit is 0, we do nothing, if it's 1
# we apply an X-gate to that qubit:
for qubit in range(len(b_str)):
if b_str[qubit] == '1':
oracle_qc.x(qubit)
# Do the controlled-NOT gates for each qubit, using the output qubit
# as the target:
for qubit in range(self._num_qubits):
oracle_qc.cx(qubit, self._num_qubits)
# Next, place the final X-gates
for qubit in range(len(b_str)):
if b_str[qubit] == '1':
oracle_qc.x(qubit)
# Case in which oracle is constant
if self._case == "constant":
# First decide what the fixed output of the oracle will be
# (either always 0 or always 1)
output = np.random.randint(2)
if output == 1:
oracle_qc.x(self._num_qubits)
oracle_gate = oracle_qc.to_gate()
oracle_gate.name = "Oracle" # To show when we display the circuit
return oracle_gate
def _build(self) -> None:
oracle = self.dj_oracle()
n = self._num_qubits
dj_circuit = QuantumCircuit(n + 1)
# Set up the output qubit:
dj_circuit.x(n)
dj_circuit.h(n)
# And set up the input register:
for qubit in range(n):
dj_circuit.h(qubit)
# Let's append the oracle gate to our circuit:
dj_circuit.append(oracle, range(n+1))
# Finally, perform the H-gates again on the input register
for qubit in range(n):
dj_circuit.h(qubit)
# We don't add measurements here to allow for more flexible use of the circuit
# for i in range(n):
# dj_circuit.measure(i, i)
self.compose(dj_circuit, inplace=True)