Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este tutorial mostra como escrever e simular um programa quântico básico que opera em qubits individuais.
Embora Q# tenha sido criado principalmente como uma linguagem de programação de alto nível para programas quânticos de grande escala, ele também pode ser usado para explorar o nível inferior da programação quântica, ou seja, lidar diretamente com qubits específicos. Especificamente, este tutorial examina mais de perto a QFT (Transformada Quântica de Fourier), uma sub-rotina que é integral para muitos algoritmos quânticos maiores.
Neste tutorial, você aprenderá a:
- Defina operações quânticas no Q#.
- Escreva o circuito da Transformada Quântica de Fourier
- Simule uma operação quântica da alocação de qubit para a saída de medição.
- Observe como a função de onda simulada do sistema quântico evolui ao longo da operação.
Observação
Essa exibição de nível mais baixo do processamento de informações quânticas geralmente é descrita em termos de circuitos quânticos, que representam a aplicação sequencial de portões ou as operações a qubits específicos de um sistema. Assim, as operações de um e de vários qubits que aplicamos sequencialmente podem ser prontamente representadas em diagramas de circuito. Por exemplo, a transformada quântica de Fourier completa de três qubits usada neste tutorial tem a seguinte representação como um circuito:
Dica
Se você quiser acelerar seu percurso de computação quântica, confira o Código com o Azure Quantum, um recurso exclusivo do site do Microsoft Quantum. Aqui, você pode executar exemplos internos Q# ou seus próprios Q# programas, gerar novo Q# código de seus prompts, abrir e executar seu código no VS Code para a Web com um único clique e fazer perguntas ao Copilot sobre computação quântica.
Pré-requisitos
A versão mais recente do Visual Studio Code (VS Code) ou abrir o VS Code para a Web.
A versão mais recente da extensão do Azure Quantum Development Kit (QDK). Para obter detalhes da instalação, consulte Configurar a extensão do QDK.
Se você quiser usar o Jupyter Notebooks, precisará instalar as extensões Python e Jupyter no VS Code.
O pacote mais recente do Python
qdkcom o extrajupyter. Para instalá-los, abra um terminal e execute o seguinte comando:pip install --upgrade "qdk[jupyter]"
Criar um novo Q# arquivo
- No VS Code, abra o menu Arquivo e escolha Novo Arquivo de Texto.
- Salve o arquivo como QFTcircuit.qs. Esse arquivo contém o código Q# para seu programa.
- Abra QFTcircuit.qs.
Escreva um circuito QFT em Q#
A primeira parte deste tutorial consiste em definir a operação Q#Main, que executa a transformação quântica de Fourier em três qubits. A função DumpMachine será usada para observar como a wavefunction simulada de nosso sistema de três qubit evolui na operação. Na segunda parte do tutorial, você adiciona a funcionalidade de medida e compara os estados pré e pós-medida dos qubits.
Você constrói a operação passo a passo. Copie e cole o código nas seções a seguir no arquivo QFTcircuit.qs .
Você pode visualizar o Q# completo desta seção como referência.
Importar bibliotecas necessárias Q#
Dentro do arquivo Q# , importe os namespaces relevantes Std.* .
import Std.Diagnostics.*;
import Std.Math.*;
import Std.Arrays.*;
// operations go here
Definir operações com argumentos e devoluções
Em seguida, defina a operação Main:
operation Main() : Unit {
// do stuff
}
A Main operação nunca usa argumentos e, por enquanto, retorna um Unit objeto, que é análogo ao retorno void em C# ou uma tupla vazia, Tuple[()], em Python.
Posteriormente, você modificará a operação para retornar uma matriz de resultados de medida.
Alocar qubits
Dentro da Q# operação, aloque um registro de três qubits com a use palavra-chave. Com use, os qubits são alocados automaticamente no estado $\ket {0}$.
use qs = Qubit[3]; // allocate three qubits
Message("Initial state |000>:");
DumpMachine();
Como em cálculos quânticos reais, Q# não permite acessar diretamente os estados do qubit. No entanto, a operação DumpMachine imprime o estado atual do target computador, de modo que pode fornecer informações valiosas para depuração e aprendizado quando usada junto com o simulador de estado completo.
Aplique operações controladas e de qubit único
Em seguida, você aplica as operações que compõem a Main operação em si.
Q# já contém muitas dessas e outras operações quânticas básicas no Std.Intrinsic namespace.
Observação
Observe que Std.Intrinsic não foi importado no snippet de código anterior com os outros namespaces, pois é carregado automaticamente pelo compilador para todos os Q# programas.
A primeira operação aplicada é a H operação (Hadamard) no primeiro qubit:
Para aplicar uma operação a um qubit específico de um registro (por exemplo, um só Qubit de uma matriz Qubit[]), use a notação de índice padrão.
Portanto, aplicar a operação H ao primeiro qubit do registro qs assume a forma:
H(qs[0]);
Além de aplicar a operação H a qubits individuais, o circuito QFT consiste principalmente em rotações R1controladas. 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 extremamente fácil condicionar a execução de uma operação em um ou vários qubits de controle. Em geral, a chamada é simplesmente precedida com Controlled, e os argumentos da operação são alterados como abaixo:
Op(<normal args>) $\to$ Controlled Op([<control qubits>], (<normal args>))
Observe que os qubits de controle devem ser fornecidos como uma matriz, mesmo que sejam apenas um 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 arquivo Q#, chame essas 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 função PI() é usada para definir as rotações em termos de pi radianos.
Aplicar operação SWAP
Depois de aplicar as operações de H relevantes e rotações controladas ao segundo e terceiro qubits, o circuito terá esta aparência:
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
Por fim, você aplica uma SWAP operação ao primeiro e ao terceiro qubits para concluir o circuito. Essa operação é necessária porque a transformada de Fourier quântica gera os qubits em ordem inversa, portanto, as trocas permitem a integração perfeita da sub-rotina em algoritmos maiores.
SWAP(qs[2], qs[0]);
Agora, você terminou de escrever as operações em nível de qubit da Transformada Quântica de Fourier na operação Q#:
Desalocar qubits
A última etapa é chamar DumpMachine() novamente para ver o estado pós-operação e desalocar os qubits. Os qubits estavam no estado $\ket{0}$ quando você os alocou e precisa ser redefinido para o estado inicial usando a operação ResetAll.
Exigir que todos os qubits sejam explicitamente redefinidos para $\ket{0}$ é um recurso básico do Q#, pois permite que outras operações conheçam seu estado precisamente quando começam a usar esses mesmos qubits (um recurso escasso). Além disso, redefini-los garante que eles não estejam emaranhados com outros qubits no sistema. Se a redefinição não for executada no final de um bloco de alocação use, um erro de execução pode ocorrer.
Adicione as seguintes linhas ao seu arquivo Q#:
Message("After:");
DumpMachine();
ResetAll(qs); // deallocate qubits
A operação completa do QFT
O Q# programa está concluído. Seu arquivo QFTcircuit.qs agora deve ter esta aparência:
import Std.Diagnostics.*;
import Std.Math.*;
import Std.Arrays.*;
operation Main() : 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 Main operação não retorna nenhum valor - a operação retorna Unit valor. Posteriormente, você modificará a operação para retornar uma matriz de resultados de medida (Result[]).
- Para executar seu programa, escolha Executar Q# Arquivo no menu que precede
Mainou pressione Ctrl + F5. O programa executa aMainoperação no simulador padrão. - As
Messagesaídas andDumpMachineaparecem no console de depuração.
Se você estiver curioso sobre como outros estados de entrada são afetados, você será incentivado a experimentar a aplicação de outras operações de qubit antes da transformação.
Adicionar medições ao circuito QFT
A exibição da função DumpMachine 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 função DumpMachine.
Em vez disso, as informações são extraídas por meio de medidas, o que, em geral, não apenas falha em fornecer informações sobre o estado quântico completo, como também pode alterar drasticamente o próprio sistema.
Há muitos tipos de medidas quânticas, mas este exemplo se concentra nas medidas mais básicas: medições projetivas em qubits únicos. Na medida em uma determinada base (por exemplo, a base computacional $ { \ket{0}, \ket{1} } $), o estado qubit é projetado para o estado base que foi medido, destruindo qualquer sobreposição entre os dois.
Modificar a operação QFT
Para implementar medidas dentro de um programa Q#, use a operação M, que retorna um tipo Result.
Primeiro, modifique a operação Main para retornar uma matriz de resultados de medição, Result[] em vez de Unit.
operation Main() : Result[] {
Definir e inicializar a matriz Result[]
Antes de alocar qubits, declare e associe uma matriz de três elementos (uma Result para cada qubit):
mutable resultArray = [Zero, size = 3];
A palavra-chave mutable que precede resultArray permite que a variável seja reassociada posteriormente no código, por exemplo, ao adicionar os resultados da medição.
Executar medidas em um loop for e adicionar resultados à matriz
Após as operações de transformação QFT, insira o seguinte código:
for i in IndexRange(qs) {
resultArray w/= i <- M(qs[i]);
}
A função IndexRange chamada em uma matriz (por exemplo, a matriz de qubits, qs) retorna um intervalo sobre os índices da matriz.
Aqui, ele é usado no loop for para medir sequencialmente cada qubit usando a instrução M(qs[i]).
Cada tipo medido Result (Zero ou One) é então adicionado à posição de índice correspondente em resultArray com uma instrução update-and-reassign.
Observação
A sintaxe dessa instrução é exclusiva para Q#, mas corresponde à reatribuição de variável semelhante resultArray[i] <- M(qs[i]) vista em outras linguagens, como F# e R.
A palavra-chave set é sempre usada para reatribuir variáveis vinculadas usando mutable.
Retornar resultArray
Com todos os três qubits medidos e os resultados adicionados ao resultArray, é seguro redefinir e desalocar os qubits como antes. Para retornar as medições, insira:
return resultArray;
Execute o circuito QFT com as medições
Agora altere o posicionamento das funções DumpMachine para gerar como saída o estado antes e depois das medidas.
O código final Q# deve ter esta aparência:
import Std.Diagnostics.*;
import Std.Math.*;
import Std.Arrays.*;
operation Main() : 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) {
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 salvar seu arquivo toda vez que introduzir uma alteração no código antes de executá-lo novamente.
- Para executar seu programa, escolha Executar Q# no menu que precede
Mainou pressione Ctrl + F5. O programa executa aMainoperação no simulador padrão. - As
Messagesaídas andDumpMachineaparecem no console de depuração.
Sua saída deve ser semelhante a esta:
Before measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|000⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|001⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|010⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|011⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|100⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|101⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|110⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
|111⟩ | 0.3536+0.0000𝑖 | 12.5000% | 0.0000
After measurement:
Basis | Amplitude | Probability | Phase
-----------------------------------------------
|010⟩ | 1.0000+0.0000𝑖 | 100.0000% | 0.0000
Post-QFT measurement results [qubit0, qubit1, qubit2]:
[Zero, One, Zero]
Essa saída ilustra alguns pontos diferentes:
- Quando você compara o resultado retornado com a pré-medição
DumpMachine, ele claramente não ilustra a superposição pós-QFT sobre estados de base. Uma medida retorna apenas um estado de base, com uma probabilidade determinada pela amplitude desse estado na wavefunction do sistema. - Da pós-medida
DumpMachine, você verá que a medição altera o estado em si, projetando-a da sobreposição inicial até os estados de base e até o estado de base único que corresponde ao valor medido.
Se você repetir essa operação muitas vezes, verá que as estatísticas de resultados começam a ilustrar a superposição de mesmo peso do estado pós-QFT que dá origem a um resultado aleatório em cada imagem. No entanto, além de ineficientes e ainda imperfeitos, isso só reproduziria as amplitudes relativas dos estados de base, não as fases relativas entre eles. Este último não é um problema neste exemplo, mas você verá fases relativas aparecerem se receber uma entrada mais complexa para o QFT do que $\ket{000}$.
Use as Q# operações para simplificar o circuito QFT
Como mencionado na introdução, grande parte do poder de Q# está no fato de que ele permite abstrair as preocupações de lidar com qubits individuais.
Na verdade, se você quiser desenvolver programas quânticos que se aplicam em escala completa, se preocupar com o fato de que uma operação H vai antes ou depois de uma rotação específica servirá apenas para desacelerar o processo. O Azure Quantum fornece a ApplyQFT operação, que você pode usar e aplicar para qualquer número de qubits.
Substitua tudo, desde a primeira
Hoperação até aSWAPoperação, inclusive, por:ApplyQFT(qs);Seu código agora deve ficar assim
import Std.Diagnostics.*; import Std.Math.*; import Std.Arrays.*; operation Main() : Result[] { mutable resultArray = [Zero, size = 3]; use qs = Qubit[3]; //QFT: //first qubit: ApplyQFT(qs); Message("Before measurement: "); DumpMachine(); for i in IndexRange(qs) { resultArray w/= i <- M(qs[i]); } Message("After measurement: "); DumpMachine(); ResetAll(qs); Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: "); return resultArray; }Execute o Q# programa novamente e observe que a saída é a mesma de antes.
Para ver o benefício real do uso Q# de operações, altere o número de qubits para algo diferente de
3:
mutable resultArray = [Zero, size = 4];
use qs = Qubit[4];
//...
Assim, você pode aplicar o QFT adequado para qualquer número de qubits, sem ter que se preocupar em adicionar novas H operações e rotações em cada qubit.
Conteúdo relacionado
Explore outros tutoriais Q#:
- O gerador de números aleatórios quânticos mostra como escrever um Q# programa que gera números aleatórios a partir de qubits em superposição.
- O algoritmo de pesquisa de Grover mostra como escrever um Q# programa que usa o algoritmo de pesquisa de Grover.
- O emaranhamento quântico mostra como escrever um Q# programa que manipula e mede qubits e demonstra os efeitos da superposição e do emaranhamento.
- Os Katas Quânticos são tutoriais individualizados e exercícios de programação destinados a ensinar os elementos da computação quântica e Q# da programação ao mesmo tempo.