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)