Source code for uniqc.algorithms.core.circuits.deutsch_jozsa

"""Deutsch-Jozsa algorithm circuit and oracle builder.

The oracular convention here (per the design notes in the project README) is:

- :func:`deutsch_jozsa_oracle` returns a fresh ``Circuit`` (the oracle).
- :func:`deutsch_jozsa_circuit` accepts a *quantum-circuit* oracle as its
  argument and returns a fresh full-DJ ``Circuit`` (fragment style).
"""

__all__ = ["deutsch_jozsa_circuit", "deutsch_jozsa_oracle", "deutsch_jozsa_example"]

from uniqc._error_hints import format_enriched_message
from uniqc.circuit_builder import Circuit


[docs] def deutsch_jozsa_oracle( qubits: list[int], balanced: bool = True, target_bits: list[int] | None = None, ) -> Circuit: r"""Build a Deutsch-Jozsa oracle circuit. See module docstring; behaviour is unchanged from previous releases — this is already a fragment-style API (returns a fresh ``Circuit``). Args: qubits: Data-qubit indices (explicit list, no default). balanced: If ``True``, build a balanced oracle; otherwise constant. target_bits: Data-qubit indices (positions within *qubits*) that control the ancilla flip. Only used when *balanced* is ``True``. ``None`` means all data qubits. Returns: A new :class:`Circuit` containing the oracle gates. """ if not isinstance(qubits, list): raise TypeError(format_enriched_message("qubits must be a list of qubit indices", "circuit_validation")) if len(qubits) < 1: raise ValueError(format_enriched_message("qubits must contain at least 1 data qubit", "circuit_validation")) n_qubits = len(qubits) ancilla = max(qubits) + 1 oracle = Circuit() if not balanced: return oracle if target_bits is None: target_bits = list(range(n_qubits)) for idx in target_bits: if idx < 0 or idx >= n_qubits: raise ValueError( format_enriched_message( f"target_bit {idx} out of range for {n_qubits} data qubits", "circuit_validation" ) ) oracle.cnot(qubits[idx], ancilla) return oracle
def _build_dj_fragment( *, oracle: Circuit, qubits: list[int], ancilla: int | None = None, ) -> Circuit: if not isinstance(qubits, list): raise TypeError(format_enriched_message("qubits must be a list of qubit indices", "circuit_validation")) if len(qubits) < 1: raise ValueError(format_enriched_message("qubits must contain at least 1 data qubit", "circuit_validation")) n_data = len(qubits) if ancilla is None: ancilla = max(qubits) + 1 if oracle.qubit_num > 0 and oracle.qubit_num != n_data + 1: raise ValueError( format_enriched_message( f"Oracle acts on {oracle.qubit_num} qubits, expected {n_data + 1} (data + ancilla)", "circuit_validation", ) ) fragment = Circuit() for q in qubits: fragment.h(q) fragment.x(ancilla) fragment.h(ancilla) fragment.add_circuit(oracle) for q in qubits: fragment.h(q) fragment.measure(*qubits) return fragment
[docs] def deutsch_jozsa_circuit( oracle: Circuit | None = None, *, qubits: list[int] | None = None, ancilla: int | None = None, ) -> Circuit: r"""Build the Deutsch-Jozsa algorithm circuit fragment. .. code-block:: python ora = deutsch_jozsa_oracle(qubits=[0, 1, 2], balanced=True) circuit = deutsch_jozsa_circuit(ora, qubits=[0, 1, 2]) # returns Circuit Args: oracle: The oracle ``Circuit`` (positional or keyword). qubits: Data-qubit indices. ancilla: Ancilla qubit index. ``None`` means ``max(qubits) + 1``. Returns: A fresh :class:`Circuit` containing the full DJ circuit. """ if oracle is None: raise TypeError( format_enriched_message("deutsch_jozsa_circuit requires an oracle Circuit argument", "circuit_validation") ) return _build_dj_fragment(oracle=oracle, qubits=qubits, ancilla=ancilla)
[docs] def deutsch_jozsa_example() -> Circuit: """Return a 3-qubit balanced-DJ algorithm circuit for tests/docs.""" ora = deutsch_jozsa_oracle(qubits=[0, 1, 2], balanced=True) return deutsch_jozsa_circuit(ora, qubits=[0, 1, 2])