Deutsch-Jozsa アルゴリズムは、量子アルゴリズムが古典アルゴリズムに対して優位性を持つことを初めて明確に示したアルゴリズムのひとつです。ベンチマーク5で示したDeutschのアルゴリズムは1ビットでしたが、nビットに拡張されています。
問題設定
関数
関数 𝑓は、次のどちらかに必ず該当する:
- 定数関数(constant):すべての入力に対して出力が同じ(0または1のみ)
- バランス関数(balanced):全入力のうち半分は0、残り半分は1
古典的アプローチとの比較
- 古典的には、最悪の場合、全入力の半分以上の評価が必要。
- 量子アルゴリズム(Deutsch-Jozsa)では、わずか1回のオラクル呼び出しだけで判定可能。
- つまり、入力ビット数 𝑛に関係なく、1回の評価で完了する。
実装
CUDA-Qでの実装を見てみます。
まずはツール読み込み。今回もCUDA-Qのみで行ってみます。
# Import the CUDA-Q package and set the target to run on NVIDIA GPUs.
import cudaq
import random
from typing import List
cudaq.set_target("nvidia")
# Number of qubits for the Deutsch-Jozsa algorithm, the last qubit is an auxiliary qubit
qubit_count = 3
次に定数を作ります。
# Set the function to be "constant" or "balanced"
function_type = 'constant'
# Initialize fx depending on whether the function is constant or balanced
if function_type == 'constant':
# For a constant function, fx is either all 0's or all 1's
oracleType = 0 # 0 for constant
fx_value = random.choice([0, 1]) # Randomly pick 0 or 1
oracleValue = fx_value # In constant case, fx_value is passed, for balanced it's not used
fx = [fx_value] * (qubit_count - 1)
else:
# For a balanced function, half of fx will be 0's and half will be 1's
oracleType = 1
fx = [0] * ((qubit_count - 1) // 2) + [1] * ((qubit_count - 1) - (qubit_count - 1) // 2)
random.shuffle(fx) # Shuffle to randomize the positions of 0's and 1's
# If needed initialize fx, oracleType, and oracleValue manually
#oracleType = 0
#oracleValue = 0
#fx = [0,0]
print(f"Generated fx for function type = {function_type}: {fx}")
print ("oracleType = ", oracleType)
print ("oracleValue = ", oracleValue)
実際に計算して計測してみます。
# Define kernel
@cudaq.kernel
def kernel(fx: List[int], qubit_count: int, oracleType: int, oracleValue: int):
# Allocate two input qubits
input_qubits = cudaq.qvector(qubit_count-1)
# Allocate an auxiliary qubit (initially |0⟩)
auxiliary_qubit = cudaq.qubit()
# Prepare the auxiliary qubit
x(auxiliary_qubit)
h(auxiliary_qubit)
# Place the rest of the register in a superposition state
h(input_qubits)
# Logic for oracleType == 0 (constant oracle)
if oracleType == 0:
if oracleValue == 1:
# Apply X gate to the auxiliary qubit
x(auxiliary_qubit)
elif oracleValue == 0:
# Apply identity gate (do nothing)
pass
# Logic for oracleType == 1 (balanced oracle)
elif oracleType == 1:
for i in range(len(fx)):
if fx[i] == 1:
x.ctrl(input_qubits[i], auxiliary_qubit)
# Apply Hadamard to the input qubit again after querying the oracle
h(input_qubits)
# Measure the input qubit to yield if the function is constant or balanced.
mz(input_qubits)
print(cudaq.draw(kernel, fx, qubit_count, oracleType, oracleValue))
result = cudaq.sample(kernel, fx, qubit_count, oracleType, oracleValue, shots_count=1)
# Debugging: Print the raw result dictionary
print(f"Input qubits measurement outcome and frequency = {result}")
# Define the expected constant results for '00' and '11' for the number of input qubits
expected_constant_results = ['0' * (qubit_count - 1), '1' * (qubit_count - 1)]
# Check if either '00' or '11' (or their equivalent for more qubits) appears in the result
is_constant = any(result_key in result for result_key in expected_constant_results)
if is_constant:
print("The oracle function is constant.")
else:
print("The oracle function is balanced.")
結果としてチュートリアル通りできました。あまり重たい回路ではないので、GPUでもすぐに計算ができます。
速度を測定するというより、量子のアルゴリズムを理解するためのチュートリアルかと思います。