Изменить скрытую строку Бернштейна–Вазирани
Оракул вычисляет $f_s(x)=s\cdot x\pmod2$, где скалярное произведение — сумма попарных произведений битов по модулю 2. Алгоритм готовит вход $|0\rangle^{\otimes n}$ и вспомогательный кубит $|-\rangle$, применяет H ко всем входам, вызывает оракул, снова применяет H к входам и измеряет их.
1. Используйте исходный виджет, чтобы увидеть алгоритм в действии для разных оракулов. Кнопки H⊗n, Oracle и Clear выполняют шаги и сброс; первые два аргумента задают число входных кубитов и скрытую строку. Предшествующий пример автора начинает с bv_widget(2,"11"), а упражнение — с bv_widget(3,"011",hide_oracle=False). Проследите шаги для обоих исходных примеров и изменяемых аргументов.
2. Приведённая исходная реализация алгоритма Бернштейна–Вазирани использует s=011. Измените её для s=1011. Совпадают ли результаты с ожиданиями? Объясните.
3. Измените ту же реализацию для s=11101101. Совпадают ли результаты с ожиданиями? Объясните.
Рисунок — исходная схема для s=011. Исходный виджет предназначен для Jupyter с пакетом qiskit_textbook; его полная функция сохранена ниже. Наблюдаемые аппаратные результаты исходник отличает от идеального симулятора из-за ошибок оборудования.
Original complete simulator baseline:
# initialization
import matplotlib.pyplot as plt
import numpy as np
# importing Qiskit
from qiskit import IBMQ, Aer
from qiskit.providers.ibmq import least_busy
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, transpile
# import basic plot tools
from qiskit.visualization import plot_histogram
n = 3 # number of qubits used to represent s
s = '011' # the hidden binary string
# We need a circuit with n qubits, plus one auxiliary qubit
# Also need n classical bits to write the output to
bv_circuit = QuantumCircuit(n+1, n)
# put auxiliary in state |->
bv_circuit.h(n)
bv_circuit.z(n)
# Apply Hadamard gates before querying the oracle
for i in range(n):
bv_circuit.h(i)
# Apply barrier
bv_circuit.barrier()
# Apply the inner-product oracle
s = s[::-1] # reverse s to fit qiskit's qubit ordering
for q in range(n):
if s[q] == '0':
bv_circuit.i(q)
else:
bv_circuit.cx(q, n)
# Apply barrier
bv_circuit.barrier()
#Apply Hadamard gates after querying the oracle
for i in range(n):
bv_circuit.h(i)
# Measurement
for i in range(n):
bv_circuit.measure(i, i)
bv_circuit.draw()
# use local simulator
aer_sim = Aer.get_backend('aer_simulator')
shots = 1024
results = aer_sim.run(bv_circuit).result()
answer = results.get_counts()
plot_histogram(answer)Original widget calls:
from qiskit_textbook.widgets import bv_widget
bv_widget(2, "11")
from qiskit_textbook.widgets import bv_widget
bv_widget(3, "011", hide_oracle=False)Original bv_widget function:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ipywidgets as widgets
from IPython.display import display, clear_output, Math
from qiskit.visualization import plot_bloch_vector
from numpy import sqrt, cos, sin, pi
import numexpr
import re
from qiskit_textbook.widgets._helpers import _pre, _img
def bv_widget(nqubits, hidden_string, display_ancilla=False, hide_oracle=True):
if nqubits < 1:
print("nqubits must be 1 or greater, setting to 1.")
nqubits = 1
if nqubits < len(hidden_string):
difference = len(hidden_string) - nqubits
hidden_string = hidden_string[difference:]
print("Error: s is too long, trimming the first %i bits and using '%s' instead." % (difference, hidden_string))
import numpy as np
from qiskit_textbook.tools import num_to_latex, array_to_latex
from qiskit import QuantumCircuit, Aer, execute
backend = Aer.get_backend('statevector_simulator')
nqubits += 1
if hide_oracle:
oracle_qc = QuantumCircuit(nqubits)
q = 0
for char in hidden_string:
if char == "1":
oracle_qc.cx(q,nqubits-1)
q += 1
oracle_gate = oracle_qc.to_gate()
qc = QuantumCircuit(nqubits)
qc.h(nqubits-1)
qc.z(nqubits-1)
class Message():
def __init__(self):
if display_ancilla:
self.ops = "|{-}\\rangle\\otimes|" + "0"*(nqubits-1) + "\\rangle"
self.vec = "|{-}\\rangle\\otimes|" + "0"*(nqubits-1) + "\\rangle"
else:
self.ops = "|" + "0"*(nqubits-1) + "\\rangle"
self.vec = "|" + "0"*(nqubits-1) + "\\rangle"
msg = Message()
def vec_in_braket(vec, nqubits):
scalfac = ""
tensorfac = ""
state = ""
# Factor out separable 'output' qubit if possible
if nqubits > 1:
vfirst = vec[:2**nqubits//2]
vlast = vec[2**nqubits//2:]
if np.allclose(vfirst, 0):
vec = vlast
tensorfac += "|1\\rangle"
nqubits -= 1
elif np.allclose(vlast, 0):
vec = vfirst
tensorfac += "|0\\rangle"
nqubits -= 1
elif np.allclose(vfirst, vlast):
vec = vfirst*np.sqrt(2)
tensorfac += "|{+}\\rangle"
nqubits -= 1
elif np.allclose(vfirst, -vlast):
vec = vfirst*np.sqrt(2)
tensorfac += "|{-}\\rangle"
nqubits -= 1
if np.allclose(np.abs(vec), np.abs(vec[0])):
scalfac = num_to_latex(vec[0])
vec = vec/vec[0]
for i in range(len(vec)):
if not np.isclose(vec[i], 0):
basis = format(i, 'b').zfill(nqubits)
if not np.isclose(vec[i], 1):
if np.isclose(vec[i], -1):
if state.endswith("+ "):
state = state[:-2]
state += "-"
else:
state += num_to_latex(vec[i])
state += "|" + basis +"\\rangle + "
state = state.replace("j", "i")
state = state[:-2]
if len(state) > 5000:
return "\\text{(Too large to display)}"
if scalfac != "" or (tensorfac != "" and len(state)>(9+nqubits) and display_ancilla):
state = ("(%s)" % state)
if scalfac != "":
state = scalfac + state
if tensorfac != "" and display_ancilla:
state = tensorfac + "\otimes" + state
return state
def hadamards(qc, nqubits):
for q in range(nqubits-1):
qc.h(q)
def oracle(qc, nqubits):
if hide_oracle:
qc.append(oracle_gate, range(nqubits))
else:
qc.barrier()
q = 0
for char in hidden_string:
if char == "1":
qc.cx(q,nqubits-1)
q += 1
qc.barrier()
def update_output():
statevec = execute(qc, backend).result().get_statevector()
msg.vec = vec_in_braket(statevec, nqubits)
html_math.value = "$$ %s = %s $$" % (msg.ops, msg.vec)
image.value = qc.draw('mpl')
def on_hads_click(b):
hadamards(qc, nqubits)
if display_ancilla:
msg.ops = "|{-}\\rangle\\otimes H^{\\otimes n}" + msg.ops[18:]
else:
msg.ops = "H^{\\otimes n}" + msg.ops
update_output()
def on_oracle_click(b):
oracle(qc, nqubits)
if display_ancilla:
msg.ops = "|{-}\\rangle\\otimes U_f" + msg.ops[18:]
else:
msg.ops = "U_f" + msg.ops
update_output()
def on_clear_click(b):
for i in range(len(qc.data)-2):
qc.data.pop()
msg.__init__()
update_output()
hads_btn = widgets.Button(description="H⊗ⁿ")
hads_btn.on_click(on_hads_click)
oracle_btn = widgets.Button(description="Oracle")
oracle_btn.on_click(on_oracle_click)
clear_btn = widgets.Button(description="Clear")
clear_btn.on_click(on_clear_click)
hbox = widgets.HBox([hads_btn, oracle_btn, clear_btn])
html_math = widgets.HTMLMath()
html_math.value = "$$ %s = %s $$" % (msg.ops, msg.vec)
image = _img()
image.value = qc.draw('mpl')
display(hbox, html_math, image.widget)