№ 1912Квантовые вычисленияУровень 3

Изменить скрытую строку Бернштейна–Вазирани

Оракул вычисляет $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)
Рисунок к задаче 1912