Tutorial: Implementar a Transformação Quantum Fourier no Q#
Nota
O Microsoft Quantum Development Kit (QDK Clássico) deixará de ser suportado após 30 de junho de 2024. Se for um programador de QDK existente, recomendamos que faça a transição para o novo Azure Quantum Development Kit (QDK Moderno) para continuar a desenvolver soluções quânticas. Para obter mais informações, veja Migrar o código Q# para o QDK Moderno.
Este tutorial mostra-lhe como escrever e simular um programa quântico básico que funciona em qubits individuais.
Embora tenha Q# sido criada principalmente como uma linguagem de programação de alto nível para programas quânticos de grande escala, também pode ser utilizada para explorar o nível inferior da programação quântica, ou seja, abordando diretamente qubits específicos. Especificamente, este tutorial analisa mais de perto a Transformação Quântico Fourier (QFT), uma subroutina que é parte integrante de muitos algoritmos quânticos maiores.
Neste tutorial, irá aprender a:
- Definir operações quânticas em Q#.
- Escrever o circuito Quantum Fourier Transform
- Simular uma operação quântica desde a alocação de qubits até à saída de medição.
- Observe como a função de onda simulada do sistema quântico evolui ao longo da operação.
Nota
Esta vista de nível inferior do processamento de informações quânticas é frequentemente descrita em termos de circuitos quânticos, que representam a aplicação sequencial de portas, ou operações, para qubits específicos de um sistema. Assim, as operações de qubit único e multi-qubit que aplicar sequencialmente podem ser facilmente representadas em diagramas de circuitos. Por exemplo, a transformação fourier quântica completa de três qubits utilizada neste tutorial tem a seguinte representação como circuito:
Dica
Se quiser acelerar o seu percurso de computação quântica, consulte Código com o Azure Quantum, uma funcionalidade exclusiva do site do Azure Quantum. Aqui, pode executar exemplos incorporados Q# ou os seus próprios Q# programas, gerar novo Q# código a partir das suas instruções, abrir e executar o código no VS Code para a Web com um clique e fazer perguntas sobre computação quântica ao Copilot.
Pré-requisitos
A versão mais recente do Visual Studio Code ou abra o VS Code na Web.
A versão mais recente da extensão do Azure Quantum Development Kit . Para obter detalhes sobre a instalação, veja Instalar o QDK Moderno no VS Code.
Se quiser utilizar o Jupyter Notebooks, também terá de instalar as extensões Python e Jupyter e o pacote Python mais recente
qsharp
. Para tal, abra um terminal e execute o seguinte comando:$ pip install --upgrade qsharp
Create um novo Q# ficheiro
- No VS Code, selecione Ficheiro > Novo Ficheiro de Texto
- Guarde o ficheiro como QFTcircuit.qs. Este ficheiro irá conter o Q# código do seu programa.
- Abra QFTcircuit.qs.
Escrever um circuito QFT em Q#
A primeira parte deste tutorial consiste na definição da Q# operação Perform3qubitQFT
, que executa a transformação quântica fourier em três qubits. A DumpMachine
função é utilizada para observar como a função de onda simulada do sistema de três qubits evolui ao longo da operação. Na segunda parte do tutorial, irá adicionar a funcionalidade de medição e comparar os estados pré e pós-medição dos qubits.
Irá criar a operação passo a passo. Copie e cole o código nas secções seguintes no ficheiro QFTcircuit.qs .
Pode ver o código completo Q# desta secção como referência.
Espaços de nomes para aceder a outras Q# operações
No seu Q# ficheiro, defina o espaço de nomes NamespaceQFT
, que é acedido pelo compilador.
Para que esta operação utilize operações existentes Q# , abra os espaços de nomes relevantes Microsoft.Quantum.*
.
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
// operations go here
}
Definir operações com argumentos e devoluções
Em seguida, defina a Perform3qubitQFT
operação:
operation Perform3qubitQFT() : Unit {
// do stuff
}
Por agora, a operação não aceita argumentos e devolve um Unit
objeto, que é análogo a regressar void
em C# ou numa cadeia de identificação vazia, Tuple[()]
, em Python.
Mais tarde, irá modificar a operação para devolver uma matriz de resultados de medição.
Alocar qubits
Na operação, aloque Q# um registo de três qubits com a use
palavra-chave. Com use
o , os qubits são alocados automaticamente no estado $\ket{0}$.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
Tal como nos cálculos quânticos reais, Q# não lhe permite aceder diretamente a estados qubit. No entanto, a DumpMachine
operação imprime o target estado atual da máquina, para que possa fornecer informações valiosas para depuração e aprendizagem quando utilizada em conjunto com o simulador de estado completo.
Aplicar operações de qubit único e controladas
Em seguida, aplique as operações que compõem a Perform3qubitQFT
própria operação. Q# já contém estas e muitas outras operações quânticas básicas no Microsoft.Quantum.Intrinsic
espaço de nomes.
A primeira operação aplicada é a H
operação (Hadamard) ao primeiro qubit:
Para aplicar uma operação a um qubit específico a partir de um registo (por exemplo, um único Qubit
de uma matriz Qubit[]
), utilize a notação de índice padrão.
Assim, a aplicação da H
operação ao primeiro qubit do registo qs
assume o formulário:
H(qs[0]);
Além de aplicar a H
operação a qubits individuais, o circuito QFT consiste principalmente em rotações controladas R1
. Uma R1(θ, <qubit>)
operação em geral deixa o componente $\ket{0}$ do qubit inalterado ao aplicar uma rotação de $e^{i\theta}$ ao componente $\ket{1}$.
Q# torna mais fácil condicionar a execução de uma operação num ou em múltiplos qubits de controlo. Em geral, a chamada é prefaciada com Controlled
e os argumentos de operação mudam da seguinte forma:
Op(<normal args>)
$\to$ Controlled Op([<control qubits>], (<normal args>))
Tenha em atenção que o argumento qubit de controlo tem de ser uma matriz, mesmo que seja para um único qubit.
As operações controladas no QFT são as R1
operações que atuam no primeiro qubit (e controladas pelo segundo e terceiro qubits):
No seu Q# ficheiro, chame estas operações com estas instruções:
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
A PI()
função é utilizada para definir as rotações em termos de radianos pi.
Aplicar operação SWAP
Depois de aplicar as operações relevantes H
e rotações controladas ao segundo e terceiro qubits, o circuito tem o seguinte aspeto:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
Por fim, aplica uma SWAP
operação ao primeiro e terceiro qubits para concluir o circuito. Isto é necessário porque a natureza da transformação quântico Fourier produz os qubits por ordem inversa, pelo que as trocas permitem uma integração totalmente integrada da subroutina em algoritmos maiores.
SWAP(qs[2], qs[0]);
Acabou de escrever as operações ao nível do qubit da transformação quantum Fourier na sua Q# operação:
Desalocar qubits
O último passo é ligar DumpMachine()
novamente para ver o estado pós-operação e desalocar os qubits. Os qubits estavam no estado $\ket{0}$ quando os atribuiu e precisam de ser repostos para o estado inicial com a ResetAll
operação.
Exigir que todos os qubits sejam explicitamente repostos para $\ket{0}$ é uma funcionalidade básica do Q#, uma vez que permite que outras operações conheçam o respetivo estado precisamente quando começam a utilizar esses mesmos qubits (um recurso escasso). Além disso, isto garante que não estão entrelaçados com outros qubits no sistema. Se a reposição não for efetuada no final de um use
bloco de alocação, poderá ser emitido um erro de runtime.
Adicione as seguintes linhas ao seu Q# ficheiro:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
A operação QFT completa
O Q# programa está concluído. O ficheiro QFTcircuit.qs deverá ter o seguinte aspeto:
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
operation Perform3qubitQFT() : Unit {
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
}
}
Executar o circuito QFT
Por enquanto, a Perform3qubitQFT
operação não devolve nenhum valor – a operação devolve Unit
o valor. Mais tarde, irá modificar a operação para devolver uma matriz de resultados de medição (Result[]
).
Ao executar um Q# programa, tem de adicionar um
EntryPoint
ao Q# ficheiro. Este atributo indica ao compilador que esta operação é o ponto de entrada do programa. Adicione a seguinte linha à parte superior do ficheiro Q# antes daPerform3qubitQFT
operação :@EntryPoint() operation Perform3qubitQFT() : Unit {
Antes de executar o programa, tem de definir o target perfil como Sem Restrições. Selecione Ver -> Paleta de Comandos, procure QIR, selecione Q#: Defina o perfil QIR target do Azure Quantum e, em seguida, selecione Q#: sem restrições.
Para executar o seu programa, selecione Executar Q# Ficheiro no menu pendente ícone de reprodução no canto superior direito ou prima Ctrl+F5. O programa executa a operação ou função marcada com o
@EntryPoint()
atributo no simulador predefinido.As
Message
saídas eDumpMachine
aparecem na consola de depuração.
Nota
Se o target perfil não estiver definido como Sem restrições, receberá um erro quando executar o programa.
Compreender a saída do circuito QFT
Quando chamado no simulador de estado completo, DumpMachine()
fornece estas múltiplas representações da função de onda do estado quântico.
Os estados possíveis de um sistema de $n$-qubit podem ser representados por $2^n$ estados de base computacional, cada um com um coeficiente complexo correspondente (uma amplitude e uma fase).
Os estados de base computacional correspondem a todas as cadeias binárias possíveis de comprimento $n$, ou seja, todas as combinações possíveis de estados de qubit $\ket{0}$ e $\ket{1}$, em que cada dígito binário corresponde a um qubit individual.
A primeira linha fornece um comentário com os IDs dos qubits correspondentes pela sua ordem significativa.
O qubit 2
sendo o "mais significativo" significa que, na representação binária do vetor de estado de base $\ket{i}$, o estado do qubit 2
corresponde ao dígito mais à esquerda.
Por exemplo, $\ket{6} = \ket{110}$ é composto por qubits 2
e 1
ambos em $\ket{1}$ e qubit 0
em $\ket{0}$.
As restantes linhas descrevem a amplitude de probabilidade de medir o vetor de estado de base $\ket{i}$ em formatos cartesianos e polares. A examinar a primeira linha do estado de entrada $\ket{000}$:
|0>:
Esta linha corresponde ao0
estado de base computacional (dado que o estado inicial após a alocação foi $\ket{000}$, espera-se que este seja o único estado com amplitude de probabilidade neste momento).1.000000 + 0.000000 i
: a amplitude de probabilidade no formato cartesiano.==
: oequal
sinal separa ambas as representações equivalentes.********************
: representação gráfica da magnitude. O número de*
é proporcional à probabilidade de medir este vetor de estado.[ 1.000000 ]
: o valor numérico da magnitude.---
: uma representação gráfica da fase da amplitude.[ 0.0000 rad ]
: o valor numérico da fase (em radianos).
Tanto a magnitude como a fase são apresentadas com uma representação gráfica. A representação de magnitude é simples: mostra uma barra de *
e quanto maior for a probabilidade, mais longa será a barra.
O resultado apresentado ilustra que as operações programadas transformaram o estado de
$$ \ket{\psi}_{initial} = \ket{000} $$
para
$$ \begin{align} \ket{\psi}_{final} &= \frac{1}{\sqrt{8}} \left( \ket{000} + \ket + \ket{001}{010} + \ket + \ket{011} + \ket{100} + \ket{101} + \ket + \ket{110} + \ket{111} \right) \\ &= \frac{1}{\sqrt{2^n}}\sum_{j=0}^{2^n-1} \ket{j}, \end{align} $$
que é precisamente o comportamento da transformação Fourier de três qubits.
Se estiver curioso sobre como outros estados de entrada são afetados, é recomendado experimentar a aplicação de outras operações de qubit antes da transformação.
Adicionar medidas ao circuito QFT
A apresentação da DumpMachine
função mostrou os resultados da operação, mas, infelizmente, uma pedra angular da mecânica quântica afirma que um sistema quântico real não pode ter tal DumpMachine
função.
Em vez disso, as informações são extraídas através de medições que, em geral, não só não fornecem informações sobre o estado quântico completo, como também podem alterar drasticamente o próprio sistema.
Existem muitos tipos de medições quânticas, mas o exemplo aqui foca-se nas medidas mais básicas: projectivas em qubits únicos. Após a medição numa determinada base (por exemplo, a base computacional $ { \ket{0}, \ket{1} } $), o estado do qubit é projetado para o estado de base medido, destruindo assim qualquer sobreposição entre os dois.
Modificar a operação QFT
Para implementar medições num Q# programa, utilize a M
operação, que devolve um Result
tipo.
Primeiro, modifique a Perform3QubitQFT
operação para devolver uma matriz de resultados de medição, Result[]
, em vez de Unit
.
operation Perform3QubitQFT() : Result[] {
Definir e inicializar a Result[]
matriz
Antes de alocar qubits, declare e vinculte uma matriz de três elementos (um Result
para cada qubit):
mutable resultArray = [Zero, size = 3];
A mutable
prefaciação de resultArray
palavras-chave permite que a variável seja modificada mais tarde no código, por exemplo, ao adicionar os resultados da medição.
Executar medições num for
ciclo e adicionar resultados à matriz
Após as operações de transformação QFT, insira o seguinte código:
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
A IndexRange
função chamada numa matriz (por exemplo, a matriz de qubits, qs
) devolve um intervalo sobre os índices da matriz.
Aqui, é utilizado no for
ciclo para medir sequencialmente cada qubit com a M(qs[i])
instrução .
Cada tipo medido Result
(ou Zero
One
) é adicionado à posição de índice correspondente em resultArray
com uma instrução update-and-reassign.
Nota
A sintaxe desta instrução é exclusiva de Q#, mas corresponde à reatribuição resultArray[i] <- M(qs[i])
de variáveis semelhante vista noutras linguagens, como F# e R.
A palavra-chave set
é sempre utilizada para reatribuir variáveis vinculadas com mutable
.
Devolver resultArray
Com os três qubits medidos e os resultados adicionados a resultArray
, é seguro repor e desalocar os qubits como anteriormente. Para devolver as medidas, insira:
return resultArray;
Executar o circuito QFT com as medidas
Agora, altere o posicionamento das DumpMachine
funções para exportar o estado antes e depois das medições.
O código final Q# deverá ter o seguinte aspeto:
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
operation Perform3QubitQFT() : Result[] {
mutable resultArray = [Zero, size = 3];
use qs = Qubit[3];
//QFT:
//first qubit:
H(qs[0]);
Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
SWAP(qs[2], qs[0]);
Message("Before measurement: ");
DumpMachine();
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
Message("After measurement: ");
DumpMachine();
ResetAll(qs);
Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");
return resultArray;
}
}
Dica
Lembre-se de guardar o seu ficheiro sempre que introduzir uma alteração ao código antes de o executar novamente.
Adicione um
EntryPoint
antes daPerform3qubitQFT
operação:@EntryPoint() operation Perform3qubitQFT() : Unit {
Defina o target perfil como Sem restrições. Clique no botão QIR: Base na parte inferior da janela do VS Code e selecione Sem restrições no menu pendente. Se o target perfil não estiver definido como Sem restrições, receberá um erro quando executar o programa.
Para executar o programa, selecione Executar Q# ficheiro no menu pendente do ícone de reprodução no canto superior direito ou prima Ctrl+5. O programa executa a operação ou função marcada com o
@EntryPoint()
atributo no simulador predefinido.As
Message
saídas eDumpMachine
são apresentadas na consola de depuração.
O resultado deverá ter um aspeto semelhante ao resultado:
Before measurement:
# wave function for qubits with ids (least to most significant): 0;1;2
|0>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|1>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|2>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|3>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|4>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|5>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|6>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
|7>: 0.353553 + 0.000000 i == *** [ 0.125000 ] --- [ 0.00000 rad ]
After measurement:
# wave function for qubits with ids (least to most significant): 0;1;2
|0>: 0.000000 + 0.000000 i == [ 0.000000 ]
|1>: 0.000000 + 0.000000 i == [ 0.000000 ]
|2>: 0.000000 + 0.000000 i == [ 0.000000 ]
|3>: 1.000000 + 0.000000 i == ******************** [ 1.000000 ] --- [ 0.00000 rad ]
|4>: 0.000000 + 0.000000 i == [ 0.000000 ]
|5>: 0.000000 + 0.000000 i == [ 0.000000 ]
|6>: 0.000000 + 0.000000 i == [ 0.000000 ]
|7>: 0.000000 + 0.000000 i == [ 0.000000 ]
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[One,One,Zero]
Este resultado ilustra algumas coisas diferentes:
- Comparar o resultado devolvido com a pré-medição
DumpMachine
, não ilustra claramente a sobreposição pós-QFT sobre os estados de base. Uma medição devolve apenas um estado de base único, com uma probabilidade determinada pela amplitude desse estado na função de onda do sistema. - A partir da pós-medição
DumpMachine
, verá que a medição altera o próprio estado, projetando-o da sobreposição inicial sobre os estados de base para o estado de base único que corresponde ao valor medido.
Se repetir esta operação muitas vezes, verá que as estatísticas de resultados começam a ilustrar a sobreposição igualmente ponderada do estado pós-QFT que dá origem a um resultado aleatório em cada captura. No entanto, para além de ineficiente e ainda imperfeito, isso só reproduziria as amplitudes relativas dos estados de base, e não as fases relativas entre eles. Este último não é um problema neste exemplo, mas verá que as fases relativas aparecem se lhe for dada uma entrada mais complexa para o QFT do que $\ket{000}$.
Utilizar as Q# operações para simplificar o circuito QFT
Como mencionado na introdução, grande parte do Q#poder depende do facto de lhe permitir abstrair as preocupações de lidar com qubits individuais.
Na verdade, se quiser desenvolver programas quânticos aplicáveis em larga escala, preocupar-se com o facto de uma H
operação ser executada antes ou depois de uma rotação específica apenas o atrasaria.
O Q# espaço de nomes Microsoft.Quantum.Canon
contém a ApplyQFT
operação, que pode utilizar e aplicar a qualquer número de qubits.
Para aceder à operação, adicione
open
aApplyQFT
instrução para oMicrosoft.Quantum.Canon
espaço de nomes no início do Q# ficheiro:open Microsoft.Quantum.Canon;
Substitua tudo desde o primeiro
H
aoSWAP
substituído por:ApplyQFT(qs);
Execute o Q# programa novamente e repare que o resultado é igual ao anterior.
Para ver o benefício real de utilizar Q# operações, altere o número de qubits para algo diferente
3
de :
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
Assim, pode aplicar o QFT adequado a qualquer número de qubits, sem ter de se preocupar com a confusão de novas H
operações e rotações em cada qubit.
Passos seguintes
Explore outros Q# tutoriais:
- O gerador quântico de números aleatórios mostra como escrever um Q# programa que gera números aleatórios a partir de qubits em sobreposição.
- O algoritmo de pesquisa de Grover mostra como escrever um Q# programa que utiliza o algoritmo de pesquisa de Grover.
- O entrelaçamento quântico mostra como escrever um Q# programa que manipula e mede qubits e demonstra os efeitos da sobreposição e do entrelaçamento.
- Os Quantum Katas são tutoriais personalizados e exercícios de programação destinados a ensinar os elementos da computação quântica e Q# da programação ao mesmo tempo.
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários