Skip to main content
IBM Quantum Platform

Algoritmo de Deutsch

O algoritmo de Deutsch resolve o problema de paridade para o caso especial em que n=1.n = 1. No contexto da computação quântica, esse problema às vezes é chamado de problema de Deutsch, e seguiremos essa nomenclatura nesta lição.

Para ser mais preciso, a entrada é representada por uma função f:ΣΣf:\Sigma \rightarrow \Sigma de um bit para um bit. Há quatro funções desse tipo:

af1(a)0010af2(a)0011af3(a)0110af4(a)0111\rule[-10mm]{0mm}{10mm} \begin{array}{c|c} a & f_1(a)\\ \hline 0 & 0\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_2(a)\\ \hline 0 & 0\\ 1 & 1 \end{array} \qquad \begin{array}{c|c} a & f_3(a)\\ \hline 0 & 1\\ 1 & 0 \end{array} \qquad \begin{array}{c|c} a & f_4(a)\\ \hline 0 & 1\\ 1 & 1 \end{array}

A primeira e a última dessas funções são constantes e as duas do meio são equilibradas, o que significa que os dois valores de saída possíveis para a função ocorrem o mesmo número de vezes à medida que percorremos as entradas. O problema de Deutsch é determinar a qual dessas duas categorias a função de entrada pertence: constante ou equilibrada.

Deutsch's problem

Entrada: uma função f:{0,1}{0,1}f:\{0,1\}\rightarrow\{0,1\} \ Saída: 00 se ff for constante, 11 se ff for equilibrado

Se considerarmos a função de entrada ff no problema de Deutsch como representando o acesso aleatório a uma cadeia de caracteres, estaremos pensando em uma cadeia de dois bits: f(0)f(1).f(0)f(1).

functionstringf100f201f310f411\begin{array}{cc} \mathsf{function} & \mathsf{string}\\ \hline f_1 & 00 \\ f_2 & 01 \\ f_3 & 10 \\ f_4 & 11 \end{array}

Quando visto dessa forma, o problema de Deutsch é calcular a paridade (ou, de forma equivalente, o OU exclusivo) dos dois bits.

Todo algoritmo de consulta clássico que resolve corretamente esse problema deve consultar ambos os bits: f(0)f(0) e f(1).f(1). Se soubermos que f(1)=1,f(1) = 1,, por exemplo, a resposta ainda poderá ser 00 ou 1,1,, dependendo de f(0)=1f(0) = 1 ou f(0)=0,f(0) = 0,, respectivamente. Todos os outros casos são semelhantes; conhecer apenas um dos dois bits não fornece nenhuma informação sobre sua paridade. Portanto, o circuito booleano descrito na seção anterior é o melhor que podemos fazer em termos do número de consultas necessárias para resolver esse problema.


Descrição do circuito quântico

O algoritmo de Deutsch resolve o problema de Deutsch usando uma única consulta, proporcionando, portanto, uma vantagem quantificável da computação quântica em relação à clássica. Essa pode ser uma vantagem modesta - uma consulta em vez de duas - mas temos que começar por algum lugar. Os avanços científicos às vezes têm origens aparentemente humildes.

Aqui está um circuito quântico que descreve o algoritmo de Deutsch:

Algoritmo de Deutsch

Análise

Para analisar o algoritmo de Deutsch, vamos rastrear a ação do circuito acima e identificar os estados dos qubits nos momentos sugeridos por essa figura:

Estados durante o algoritmo de Deutsch

O estado inicial é 10,\vert 1\rangle \vert 0 \rangle, e as duas operações Hadamard no lado esquerdo do circuito transformam esse estado em

π1=+=12(01)0+12(01)1.\vert \pi_1 \rangle = \vert - \rangle \vert + \rangle = \frac{1}{2} \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \vert 0\rangle + \frac{1}{2} \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \vert 1\rangle.

(Como sempre, estamos seguindo a convenção de ordenação de qubits do Qiskit, que coloca o qubit superior à direita e o qubit inferior à esquerda) Pode parecer pouco intuitivo escrever esse estado de produto parcialmente distribuído (deixando os estados do qubit 1 sem fatoração), mas isso tornará nossas expressões posteriores mais compactas.

Em seguida, a porta UfU_f é executada. De acordo com a definição da porta UfU_f, o valor da função ff para o estado clássico do qubit superior/direito é XORed no qubit inferior/esquerdo, o que transforma π1\vert \pi_1\rangle no estado

π2=12(0f(0)1f(0))0+12(0f(1)1f(1))1.\vert \pi_2 \rangle = \frac{1}{2} \bigl( \vert 0 \oplus f(0) \rangle - \vert 1 \oplus f(0) \rangle \bigr) \vert 0 \rangle + \frac{1}{2} \bigl( \vert 0 \oplus f(1) \rangle - \vert 1 \oplus f(1) \rangle \bigr) \vert 1 \rangle.

Podemos simplificar essa expressão observando que a fórmula

0a1a=(1)a(01)\vert 0 \oplus a\rangle - \vert 1 \oplus a\rangle = (-1)^a \bigl( \vert 0\rangle - \vert 1\rangle \bigr)

funciona para ambos os valores possíveis aΣ.a\in\Sigma. De forma mais explícita, os dois casos são os seguintes.

0010=01=(1)0(01)0111=10=(1)1(01)\begin{aligned} \vert 0 \oplus 0\rangle - \vert 1 \oplus 0\rangle & = \vert 0 \rangle - \vert 1 \rangle = (-1)^0 \bigl( \vert 0\rangle - \vert 1\rangle \bigr)\\ \vert 0 \oplus 1\rangle - \vert 1 \oplus 1\rangle & = \vert 1 \rangle - \vert 0\rangle = (-1)^1 \bigl( \vert 0\rangle - \vert 1\rangle \bigr) \end{aligned}

Assim, podemos alternativamente expressar π2\vert\pi_2\rangle da seguinte forma:

π2=12(1)f(0)(01)0+12(1)f(1)(01)1=((1)f(0)0+(1)f(1)12).\begin{aligned} \vert\pi_2\rangle & = \frac{1}{2} (-1)^{f(0)} \bigl( \vert 0 \rangle - \vert 1 \rangle \bigr) \vert 0 \rangle + \frac{1}{2} (-1)^{f(1)} \bigl( \vert 0 \rangle - \vert 1 \rangle \bigr) \vert 1 \rangle \\ & = \vert - \rangle \biggl( \frac{(-1)^{f(0)} \vert 0\rangle + (-1)^{f(1)} \vert 1\rangle}{\sqrt{2}}\biggr). \end{aligned}

Algo interessante acabou de acontecer! Embora a ação da porta UfU_f nos estados de base padrão deixe o qubit superior/direito sozinho e faça o XOR do valor da função no qubit inferior/mais à esquerda, aqui vemos que o estado do qubit superior/direito mudou (em geral) enquanto o estado do qubit inferior/mais à esquerda permanece o mesmo - especificamente no estado \vert - \rangle antes e depois da execução da porta UfU_f. Esse fenômeno é conhecido como retorno de fase, e teremos mais informações sobre ele em breve.

Com uma simplificação final, que é puxar o fator de (1)f(0)(-1)^{f(0)} para fora da soma, obtemos essa expressão do estado π2\vert\pi_2\rangle :

π2=(1)f(0)(0+(1)f(0)f(1)12)={(1)f(0)+if f(0)f(1)=0(1)f(0)if f(0)f(1)=1.\begin{aligned} \vert\pi_2\rangle & = (-1)^{f(0)} \vert - \rangle \biggl( \frac{\vert 0\rangle + (-1)^{f(0) \oplus f(1)} \vert 1\rangle}{\sqrt{2}}\biggr) \\ & = \begin{cases} (-1)^{f(0)} \vert - \rangle \vert + \rangle & \text{if $f(0) \oplus f(1) = 0$}\\[1mm] (-1)^{f(0)} \vert - \rangle \vert - \rangle & \text{if $f(0) \oplus f(1) = 1$}. \end{cases} \end{aligned}

Observe que, nessa expressão, temos f(0)f(1)f(0) \oplus f(1) no expoente de 1-1 em vez de f(1)f(0),f(1) - f(0),, que é o que poderíamos esperar de um ponto de vista puramente algébrico, mas obtemos o mesmo resultado de qualquer maneira. Isso ocorre porque o valor (1)k(-1)^k para qualquer número inteiro kk depende apenas do fato de kk ser par ou ímpar.

A aplicação da porta Hadamard final ao qubit superior nos deixa com o estado

π3={(1)f(0)0if f(0)f(1)=0(1)f(0)1if f(0)f(1)=1,\vert \pi_3 \rangle = \begin{cases} (-1)^{f(0)} \vert - \rangle \vert 0 \rangle & \text{if $f(0) \oplus f(1) = 0$}\\[1mm] (-1)^{f(0)} \vert - \rangle \vert 1 \rangle & \text{if $f(0) \oplus f(1) = 1$}, \end{cases}

o que leva ao resultado correto com probabilidade 11 quando o qubit direito/mais alto é medido.


Observações adicionais sobre o recuo de fase

Antes de prosseguir, vamos examinar a análise acima de um ângulo ligeiramente diferente que pode esclarecer o fenômeno do recuo de fase.

Primeiro, observe que a fórmula a seguir funciona para todas as opções de bits b,cΣ.b,c\in\Sigma.

bc=Xcb\vert b \oplus c\rangle = X^c \vert b \rangle

Isso pode ser verificado verificando-o para os dois valores possíveis c=0c = 0 e c=1c = 1 :

b0=b=Ib=X0bb1=¬b=Xb=X1b.\begin{aligned} \vert b \oplus 0 \rangle & = \vert b\rangle = \mathbb{I} \vert b \rangle = X^0 \vert b \rangle\\ \vert b \oplus 1 \rangle & = \vert \neg b\rangle = X \vert b \rangle = X^1 \vert b \rangle. \end{aligned}

Usando essa fórmula, vemos que

Uf(ba)=bf(a)a=(Xf(a)b)aU_f \bigl(\vert b\rangle \vert a \rangle\bigr) = \vert b \oplus f(a) \rangle \vert a \rangle = \bigl(X^{f(a)}\vert b \rangle\bigr) \vert a \rangle

para cada escolha de bits a,bΣ.a,b\in\Sigma. Como essa fórmula é verdadeira para b=0b=0 e b=1,b=1,, vemos por linearidade que

Uf(ψa)=(Xf(a)ψ)aU_f \bigl( \vert \psi \rangle \vert a \rangle \bigr) = \bigl(X^{f(a)}\vert \psi \rangle\bigr) \vert a \rangle

para todos os vetores de estado do qubit ψ,\vert \psi\rangle, e, portanto

Uf(a)=(Xf(a))a=(1)f(a)a.U_f \bigl( \vert - \rangle \vert a \rangle \bigr) = \bigl(X^{f(a)} \vert - \rangle \bigr) \vert a \rangle = (-1)^{f(a)} \vert - \rangle \vert a \rangle.

O segredo para que isso funcione é que X=.X\vert - \rangle = - \vert - \rangle. Em termos matemáticos, o vetor \vert - \rangle é um vetor próprio da matriz XX com valor próprio 1.-1.

Discutiremos os vetores próprios e os valores próprios com mais detalhes na próxima lição sobre Estimativa de fase e fatoração, em que o fenômeno de retrocesso de fase é generalizado para outras operações unitárias.

Tendo em mente que os escalares flutuam livremente por meio de produtos tensoriais, encontramos uma maneira alternativa de raciocinar como a operação UfU_f transforma π1\vert \pi_1\rangle em π2\vert \pi_2\rangle na análise acima:

π2=Uf(+)=12Uf(0)+12Uf(1)=((1)f(0)0+(1)f(1)12).\begin{aligned} \vert \pi_2 \rangle & = U_f \bigl( \vert - \rangle \vert + \rangle \bigr)\\ & = \frac{1}{\sqrt{2}} U_f \bigl(\vert - \rangle \vert 0\rangle \bigr) + \frac{1}{\sqrt{2}} U_f \bigl(\vert - \rangle \vert 1\rangle \bigr)\\ & = \vert - \rangle \biggl( \frac{(-1)^{f(0)} \vert 0\rangle + (-1)^{f(1)} \vert 1\rangle}{\sqrt{2}}\biggr). \end{aligned}

Implementação no Qiskit

Agora vamos ver como podemos implementar o algoritmo de Deutsch no Qiskit. Começaremos com uma verificação de versão e, em seguida, faremos as importações necessárias apenas para essa implementação. Para as implementações de outros algoritmos a seguir, realizaremos as importações necessárias separadamente para aumentar a modularidade.

from qiskit import __version__

print(__version__)

Output:

2.1.1
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

Primeiro, definiremos um circuito quântico que implementa uma porta de consulta para uma das quatro funções f1,f_1, f2,f_2, f3,f_3, ou f4f_4 de um bit para um bit descritas anteriormente. Como já mencionamos, a implementação de portas de consulta não é realmente uma parte do algoritmo de Deutsch em si; aqui, essencialmente, estamos apenas mostrando uma maneira de preparar a entrada, na forma de uma implementação de circuito de uma porta de consulta.

def deutsch_function(case: int):
    # This function generates a quantum circuit for one of the 4 functions
    # from one bit to one bit

    if case not in [1, 2, 3, 4]:
        raise ValueError("`case` must be 1, 2, 3, or 4.")

    f = QuantumCircuit(2)
    if case in [2, 3]:
        f.cx(0, 1)
    if case in [3, 4]:
        f.x(1)
    return f

Podemos ver a aparência de cada circuito usando o método draw . Aqui está o circuito para a função f3.f_3.

display(deutsch_function(3).draw(output="mpl"))

Output:

Output of the previous code cell

Em seguida, criaremos o circuito quântico real para o algoritmo de Deutsch, substituindo a porta de consulta por uma implementação de circuito quântico fornecida como argumento. Em breve, conectaremos um dos quatro circuitos definidos pela função deutsch_function que definimos anteriormente. As barreiras são incluídas para mostrar a separação visual entre a implementação da porta de consulta e o restante do circuito.

def compile_circuit(function: QuantumCircuit):
    # Compiles a circuit for use in Deutsch's algorithm.

    n = function.num_qubits - 1
    qc = QuantumCircuit(n + 1, n)

    qc.x(n)
    qc.h(range(n + 1))

    qc.barrier()
    qc.compose(function, inplace=True)
    qc.barrier()

    qc.h(range(n))
    qc.measure(range(n), range(n))

    return qc

Novamente, podemos ver a aparência do circuito usando o método draw .

display(compile_circuit(deutsch_function(3)).draw(output="mpl"))

Output:

Output of the previous code cell

Por fim, criaremos uma função que executa o circuito definido anteriormente uma vez e produz o resultado apropriado: "constante" ou "balanceado"

def deutsch_algorithm(function: QuantumCircuit):
    # Determine if a one-bit function is constant or balanced.

    qc = compile_circuit(function)

    result = AerSimulator().run(qc, shots=1, memory=True).result()
    measurements = result.get_memory()
    if measurements[0] == "0":
        return "constant"
    return "balanced"

Agora podemos executar o algoritmo de Deutsch em qualquer uma das quatro funções definidas acima.

f = deutsch_function(3)
display(deutsch_algorithm(f))

Output:

'balanced'
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.