Você está na página 1de 548

Contents

Documentação do Azure Quantum


Azure Quantum (versão prévia)
Visão geral
Introdução ao Azure Quantum
Introdução
Destinos
Prepare o seu ambiente
Workspaces
Criar workspaces quânticos com o portal do Azure
Gerenciar workspaces quânticos com a CLI do Azure
Inícios rápidos
Computação quântica
Guia de início rápido do IonQ
Guia de início rápido do Honeywell
Optimization
Guia de início rápido do Microsoft QIO
Guia de início rápido do 1QBit
Guias de instruções
Computação quântica
Criar e executar aplicativos Q# no Azure Quantum
Enviar trabalhos
Enviar trabalhos com a CLI do Azure
Enviar trabalhos com Jupyter notebooks para Q#
Enviar trabalhos com o Python
Optimization
Instalar e usar o SDK do Python para otimização
Solução de problemas dos solucionadores de otimização
Recursos
Glossário (Azure Quantum)
Problemas comuns
Referência
Provedores
IonQ
Provedor e metas do IonQ
Política de suporte do IonQ
Honeywell
Provedor e metas do Honeywell
Política de suporte do Honeywell
1QBit
Provedor e metas do 1QBit
Política de suporte do 1QBit
Microsoft QIO
Visão geral
Otimização simulada
Deformação paralela
Pesquisa tabu
Monte Carlo Quântico
SDK do Python
Azure.Quantum
Workspace
Azure.Quantum.Optimization
Referência
Trabalho
Problema
ProblemType
Entradas do solucionador
Termo
Uso
Expressar um problema
Aplicar um solucionador
Entender os resultados do solucionador
Gerenciamento de trabalho
Reutilizar as definições de problema
Resolver problemas de longa execução
Autenticar-se usando uma entidade de serviço
Quantum Development Kit
Visão geral
Introdução à computação quântica
Introdução à otimização
Principais conceitos da computação quântica
Noções básicas sobre a computação quântica
Computadores e simuladores quantum
O que são o Q# e o QDK?
Álgebra linear para computação quântica
Principais conceitos sobre a otimização
O que é a QIO (otimização inspirada em quantum)?
Aprenda as noções básicas
Introdução
Configurar o QDK
Computação quântica
Introdução
Introdução ao QDK
Aplicativos do Q#
Q# com Jupyter Notebooks
Q # com Python
Q# com .NET
Q # com o Binder
Atualizar o QDK
Tutoriais
Gerador de número quântico aleatório
Explorar o entrelaçamento com Q#
Implementar o algoritmo de pesquisa de Grover
Programação em nível de qubit
Aprenda Q# com o Quantum Katas
Exemplos de código em Q#
Conceitos
Histórico e contexto sobre a computação quântica
Vetores e matrizes
Conceitos avançados sobre matriz
O qubit
Vários qubits
Notação dirac
Medições Pauli
Circuitos do Quantum
Oracles Quantum
Teoria do algoritmo de Grover
Glossário
Optimization
Introdução
Instalar e usar o SDK do Python para otimização
Conceitos
Qual solucionador você deve usar?
Funções de custo
Modelo de Ising
Otimização binária (QUBOs e PUBOs)
Tutoriais
Exemplos de uso dos solucionadores de otimização do Azure Quantum
Recursos
Desenvolva suas habilidades com o MS Learn
Contribuir para o QDK
Relatar bugs
Como aprimorar a documentação
Abrir solicitações de pull
Código de contribuição
Exemplos de contribuição
Guia de Estilo
Princípios de design da API
Notas sobre a versão do QDK
Leitura adicional
Guia do usuário do Q#
Visão Geral/Conteúdo
Programas Q#
Maneiras de executar um programa em Q#
Testando e depurando
Guia da linguagem Q#
Visão geral
Estrutura do programa
Implementação do programa
Namespaces
Declarações de tipo
Declarações que podem ser chamadas
Declarações de especialização
Comentários
Instruções
Instruções em Q#
Escopos de associação
Chamar instruções
Devoluções e término
Declaração de variável e reatribuição
Iterações
Loops condicionais
Ramificação condicional
Conjugações
Gerenciamento de memória quântica
Expressões
Expressões em Q#
Precedência e capacidade de associação
Operadores
Expressões de cópia e atualização
Expressões condicionais
Expressões comparativas
Expressões lógicas
Expressões bit a bit
Expressões aritméticas
Concatenações
Modificadores e combinadores
Aplicativo parcial
Aplicativo Functor
Expressões de acesso de item
Expressões contextuais
Literais de valor e valores padrão
Sistema de tipos
Sistema de tipo no Q#
Tipos de dados do Quantum
Imutabilidade
Operações e funções
Equivalência de tupla singleton
Subdigitação e variância
Parametrizações de tipo
Gramática
Simuladores e avaliadores de recursos
Visão geral
Simulador de estado completo
Estimador de recursos
Simulador de rastreamento
Verificador de entradas distintas
Qubits invalidados usam o verificador
Contador de operações primitivas
Contador de profundidade
Contador de largura
Simulador do Toffoli
Bibliotecas do Q#
Visão geral
Bibliotecas padrão
O prelúdio
Matemática clássica
Conversões de tipo
Fluxo de controle de ordem superior
Estruturas e modelagem de dados
Algoritmos quantum
Diagnósticos
Caracterização
Correção de erro
Aplicativos
Licenciamento de OSS
Usando bibliotecas adicionais do Q#
Biblioteca de química do Quantum
Instalação e validação
Conceitos de química do Quantum
Dinâmica do Quantum
Modelos do Quantum para sistemas eletrônicos
Segunda compartimentalização
Simetrias de integrais moleculares
Representação de Jordan-Wigner
Simular a dinâmica hamiltoniana
Teoria Hartree-Fock
Funções de onda correlacionadas
Invocar a biblioteca de química
Obter estimativas do nível de energia
Carregar um Hamiltonian do arquivo
Obter contagens de recursos
Ponta a ponta com NWChem
Esquema
Esquema de química de quantum Broombridge
Especificação v0.2
Especificação v0.1
Biblioteca de aprendizado de máquina quântico
Introdução ao aprendizado de máquina quântico
Classificação básica
Criar seu classificador
Carregar seus conjuntos de dados
Glossário (QML)
Biblioteca de numéricos do Quantum
Usar a biblioteca de numéricos
Referência rápida
IQ# magic commands
Introdução ao Azure Quantum (versão prévia)
19/05/2021 • 5 minutes to read

O Azure Quantum é um serviço Microsoft Azure que você pode usar para executar programas de computação
quântica ou resolver problemas de otimização na nuvem. Usando as ferramentas e os SDKs do Azure Quantum,
você pode criar programas quânticos e executá-los em diferentes simuladores e computadores quânticos.

Computação quântica
O serviço Azure Quantum oferece acesso a provedores diferentes de dispositivos de computação quântica e
permite que você execute seus programas quânticos em Q# em hardware real. O Azure Quantum também
oferece a opção de executar algoritmos em computadores quânticos simulados para testar seu código.
O Azure Quantum dá acesso a dispositivos de íons presos por meio dos provedores IonQ e Honeywell .

Optimization
O Azure Quantum dá acesso a um amplo conjunto de algoritmos de otimização de ponta desenvolvidos pela
Microsoft e por seus parceiros. Você pode usar algoritmos de otimização clássicos, incluindo alguns inspirados
pela física padrão, bem como algoritmos QIO (otimização inspirada em quantum).
A QIO usa algoritmos baseados em princípios quânticos para aumentar a velocidade e a precisão. O Azure
Quantum dá suporte a QIO para ajudar os desenvolvedores a aproveitar o poder das novas técnicas quânticas
atualmente, sem esperar pelo hardware quântico.
Os algoritmos de otimização estão disponíveis para serem executados em uma variedade de soluções de silício
de computação clássica, como CPU, FPGA, GPU ou silício personalizado.

Workspace do Quantum
Para usar o serviço do Azure Quantum, adicione um recurso de workspace do Azure Quantum à sua assinatura
do Azure no portal do Azure. Um recurso de workspace do Quantum, ou simplesmente workspace, é uma
coleção de ativos associados à execução de aplicativos quânticos ou de otimização. Uma das propriedades
configuradas em um workspace é um recurso de Conta de Armazenamento do Azure, em que o Azure Quantum
armazena seus programas quânticos e problemas de otimização para acesso.

Provedores e destinos
Outra propriedade configurada no workspace é o provedor que você deseja usar para executar programas
nesse workspace. Um só provedor pode expor um ou mais destinos , que podem ser hardwares ou simuladores
quânticos, responsáveis por executar seu programa.
Por padrão, o Azure Quantum adiciona o provedor Microsoft QIO a todos os workspaces, e você pode adicionar
outros provedores ao criar o workspace ou a qualquer momento posteriormente. Para obter mais informações,
confira o provedor do Microsoft QIO.
Cobrança do provedor
Cada provedor extra adicionado a um workspace requer um plano de cobrança, que define como esse provedor
cobra pelo uso. Cada provedor pode ter diferentes planos de cobrança e métodos disponíveis. Para obter mais
informações, confira a documentação no provedor que você deseja adicionar.
Você só pode selecionar um plano de cobrança para cada provedor em um só workspace; no entanto, você pode
adicionar vários workspaces à sua assinatura do Azure.

Trabalhos
Ao executar um programa quântico ou resolver um problema de otimização no Azure Quantum, você cria e
executa um trabalho . As etapas para criar e executar um trabalho dependem do tipo de trabalho, bem como do
provedor e do destino que você configurou para o workspace. No entanto, todos os trabalhos têm as seguintes
propriedades em comum:

P RO P RIEDA DE DESC RIÇ Ã O

ID Um identificador exclusivo para o trabalho. Ele deve ser


único dentro do workspace.

Provedor Quem você deseja que execute seu trabalho. Por exemplo, o
provedor do Microsoft QIO ou um provedor de terceiros.

Target (destino) Onde você deseja executar seu trabalho. Por exemplo, no
hardware quântico exato ou no simulador quântico oferecido
pelo provedor.

Nome Um nome definido pelo usuário para ajudar a organizar seus


trabalhos.

Parâmetros Parâmetros de entrada opcionais para destinos. Confira a


documentação do destino selecionado para obter uma
definição dos parâmetros disponíveis.

Depois de criar um trabalho, vários metadados estão disponíveis sobre seu estado e o histórico de execuções.

Ciclo de vida do trabalho


Normalmente, você cria trabalhos usando um dos SDKs do Quantum (por exemplo, o SDK do Python ou o QDK
(Quantum development kit)). Depois de escrever o programa quântico ou expressar o problema de QIO, você
pode selecionar um destino e enviar seu trabalho.
Este diagrama mostra o fluxo de trabalho básico depois que você envia seu trabalho:

Primeiro, o Azure Quantum carrega o trabalho para a Conta de Armazenamento do Azure que você configurou
no workspace. Em seguida, o trabalho é adicionado à fila de trabalhos para o provedor que você especificou no
trabalho. Então o Azure Quantum baixa o programa e o converte para o provedor. O provedor processa o
trabalho e retorna a saída para o Armazenamento do Azure, em que ele fica disponível para download.
Monitoramento de trabalhos
Depois de enviar um trabalho, você deve sondar o status do trabalho. Os trabalhos têm os seguintes estados
possíveis:

STAT US DESC RIÇ Ã O

em espera O trabalho está aguardando a execução. Alguns trabalhos


executarão tarefas de pré-processamento no estado de
espera. waiting é sempre o primeiro estado. No entanto,
um trabalho pode ser movido para o estado executing
para você observá-lo em waiting .

em execução O destino está executando o trabalho no momento.

succeeded O trabalho foi bem-sucedido e a saída está disponível. Esse é


um estado final.

falhou O trabalho falhou e as informações de erro estão


disponíveis. Esse é um estado final.

cancelado O usuário solicitou o cancelamento da execução do trabalho.


Esse é um estado final. Para obter mais informações, confira
Cancelamento de trabalho neste artigo.

Os estados succeeded , failed e cancelled são considerados estados finais . Quando um trabalho está em
um desses estados, não ocorre mais nenhuma atualização e os dados de saída do trabalho correspondentes não
são alterados.
Este diagrama mostra as possíveis transições de estado de trabalho:

Depois que um trabalho for concluído com êxito, ele exibirá um link para os dados de saída em sua Conta de
Armazenamento do Azure. A forma como você acessa esses dados depende do SDK ou da ferramenta usada
para enviar o trabalho.
Cancelamento de trabalho
Quando um trabalho ainda não está em um estado final (por exemplo, succeeded , failed ou cancelled ), você
pode solicitar que ele seja cancelado. Todos os provedores cancelarão o trabalho se ele estiver no estado
waiting . No entanto, nem todos os provedores dão suporte ao cancelamento se o seu trabalho estiver no
estado executing .
NOTE
Se você cancelar um trabalho depois que ele começar a ser executado, sua conta ainda poderá ser cobrada por um valor
parcial ou total para esse trabalho. Confira a documentação de cobrança para o provedor selecionado.

Próximas etapas
Quando você estiver pronto para começar, comece criando um workspace do Azure Quantum.
Destinos no Azure Quantum
01/05/2021 • 2 minutes to read

Este artigo apresenta os diferentes tipos de destinos disponíveis no Azure Quantum e no QDK (Quantum
Development Kit). Os destinos no Azure Quantum podem ser os solucionadores para problemas de otimização
ou dispositivos quânticos (físicos ou simulados) que você pode usar para executar aplicativos quânticos Q#.
Atualmente, o Azure Quantum inclui os seguintes tipos de destinos:

Solucionadores de otimização
O Azure Quantum oferece destinos de otimização para resolver problemas de otimização binária em CPUs
clássicas ou hardware acelerado em FPGA (matrizes de porta programável no campo), GPUs ou annealers de
hardware.
Para obter mais informações sobre otimização, consulte solucionadores de otimização.

Dispositivos quânticos
O Azure Quantum também oferece uma variedade de soluções quânticas, como diferentes dispositivos de
hardware e simuladores quânticos. No momento, devido ao estágio inicial de desenvolvimento da área, esses
dispositivos possuem algumas limitações e requisitos para programas executados neles. O Quantum
Development Kit e o Azure Quantum acompanharão esses requisitos em segundo plano para que você possa
executar seus programas em Q# nos destinos do Azure Quantum.
QPU (unidades de processamento Quantum): perfis diferentes
Uma QPU (unidade de processamento Quantum) é um processador físico ou simulado que contém um número
de qubits interconectados que podem ser manipulados para computar algoritmos quânticos. É o componente
central de um computador quântico.
Os dispositivos quânticos ainda são uma tecnologia emergente e nem todos podem executar todo o código Q#.
Sendo assim, você precisa manter algumas restrições em mente ao desenvolver programas para destinos
diferentes. Atualmente, o Azure Quantum e o QDK gerenciam três perfis diferentes para QPUs:
Completo: este perfil pode executar qualquer programa em Q# dentro dos limites de memória para QPUs
(unidades de processamento quântico) simuladas ou para o número de qubits do hardware quântico físico.
Sem Fluxo de Controle: este perfil pode executar qualquer programa em Q# que não exija o uso dos
resultados de medições de qubits para controlar o fluxo do programa. Em um programa em Q# direcionado
para esse tipo de QPU, os valores do tipo Result não dão suporte à comparação de igualdade.
Feedback de Medição Básica: este perfil tem capacidade limitada de usar os resultados das medições de
qubits para controlar o fluxo do programa. Em um programa em Q# direcionado para esse tipo de QPU, você
só pode comparar valores do tipo Result como parte das condições dentro das instruções if nas
operações. Os blocos condicionais correspondentes não podem conter instruções return ou set .

Próximas etapas
É possível encontrar uma lista completa dos destinos do Azure Quantum no artigo lista de destinos do Azure
Quantum.
Prepare seu ambiente para usar o Azure Quantum
pelo prompt de comando
28/04/2021 • 2 minutes to read

O Azure Quantum usa a extensão CLI do Azure quantum para permitir o envio de programas em Q# por meio
da linha de comando. Este guia fornece as etapas para instalar e configurar a extensão de CLI do Azure no seu
sistema para uso com o Azure Quantum.

Pré-requisitos
Antes de instalar a extensão de CLI do Azure quantum , verifique se os seguintes pacotes estão instalados:
O Microsoft Quantum Development Kit
A versão mais recente da CLI do Azure (versão 2.5.0 ou superior)

Instalação
Para instalar a extensão de CLI do Azure quantum , abra um prompt de comando e execute o seguinte comando:

az extension add -n quantum

Desinstalar a extensão
Para desinstalar a extensão de CLI do Azure quantum , execute o seguinte comando:

az extension remove -n quantum

Atualizar a extensão
Se precisar atualizar uma instalação existente da extensão de CLI do Azure quantum , você poderá executar:

az extension update -n quantum

NOTE
Se você já tiver instalado uma versão de pré-lançamento desta extensão ou não tiver certeza sobre a instalação atual,
poderá desinstalá-la e instalá-la novamente usando as instruções acima.

Próximas etapas
Agora que você instalou as ferramentas para usar o Azure Quantum, você pode aprender a enviar trabalhos.
Para usuários de otimização
Saiba como enviar trabalhos para o Azure Quantum para resolver problemas de otimização.
Para usuários de computação quântica
Saiba como criar aplicativos em Q# e executá-los no Azure Quantum.
Criar workspaces do Azure Quantum usando o
portal do Azure
13/05/2021 • 2 minutes to read

Neste guia, aprenda a criar workspaces do Azure Quantum e os Grupos de Recursos e Contas de
Armazenamento necessários usando o portal do Azure, e comece a executar seus aplicativos quantum no Azure
Quantum.

Pré-requisitos
Para usar o serviço do Azure Quantum, você precisará de uma assinatura ativa do Azure. Para criar uma
assinatura do Azure, vá até a página de inscrição gratuita do Azure e clique no botão verde Iniciar
gratuitamente para iniciar o processo de criação de uma assinatura do Azure.

NOTE
Você precisará inserir as informações de cobrança (por exemplo, um cartão de crédito válido) para criar a assinatura
gratuita do Azure.

Depois de ativar sua assinatura do Azure, continue com a próxima seção Criar um workspace do Azure
Quantum .

Criar um workspace do Azure Quantum


Para usar o serviço do Azure Quantum, adicione um recurso de workspace do Azure Quantum à sua assinatura
do Azure no portal do Azure. Um recurso de workspace do Azure Quantum, ou simplesmente workspace, é uma
coleção de ativos associados à execução de aplicativos quantum ou de otimização.
Para abrir o portal do Azure, vá até https://portal.azure.com e siga estas etapas:

NOTE
Se você acabou de criar a assinatura do Azure, receberá uma solicitação para confirmar seu endereço de email para usar o
serviço.
1. Selecione Criar um recurso e pesquise por Azure Quantum . Na página de resultados, você verá um
bloco para o serviço do Azure Quantum (versão prévia) .

2. Selecione Azure Quantum (versão prévia) e selecione Criar . Isso abrirá um formulário para a criação
de um workspace.

3. Preencha os detalhes de seu workspace:


Assinatura: a assinatura que você deseja associar a esse workspace.
Grupo de recursos: o grupo de recursos ao qual você deseja atribuir esse workspace.
Nome: o nome do workspace.
Região: a região do workspace.
Conta de armazenamento : a conta de armazenamento do Azure para armazenar seus trabalhos e
resultados. Caso você ainda não tenha uma conta de armazenamento, selecione Criar uma nova
conta de armazenamento e preencha os campos necessários. Para essa versão prévia,
recomendamos usar os valores padrão.
NOTE
Você deve ser um proprietário do grupo de recursos selecionado para criar uma conta de armazenamento. Para
obter mais informações sobre como funcionam os grupos de recursos no Azure, confira Controlar e organizar os
recursos do Azure com o Azure Resource Manager.

4. Depois de preencher as informações, selecione a guia Provedores para adicionar os provedores ao


workspace. Um provedor fornece acesso a um serviço quântico, que pode ser um hardware quântico, um
simulador quântico ou um serviço de otimização.

NOTE
Por padrão, o Azure Quantum adiciona o provedor QIO da Microsoft para todos os workspaces.

5. Depois de adicionar os provedores que você deseja usar, selecione Revisar + criar .
6. Examine as configurações e aprove os Termos e Condições de Uso dos provedores selecionados. Se tudo
estiver correto, selecione Criar para criar seu workspace.
NOTE
Os preços do Azure Quantum variam por provedor. Consulte as informações na guia Provedores do seu workspace do
Azure Quantum, no portal do Azure, para obter os preços atualizados.

Próximas etapas
Agora que você criou o workspace, aprenda sobre os diferentes destinos para executar os algoritmos quantum
no Azure Quantum.
Gerenciar workspaces do Quantum com a CLI do
Azure
13/05/2021 • 3 minutes to read

Neste guia, aprenda a criar workspaces no Azure Quantum e os Grupos de Recursos e Contas de
Armazenamento necessárias usando a CLI do Azure (Interface de Linha de Comando do Azure) e comece a
executar seus aplicativos Quantum no Azure Quantum.

Pré-requisitos
Para usar o serviço do Azure Quantum, você precisará de:
Uma conta e uma assinatura do Azure ativas. Para saber mais, confira o módulo Criar uma conta Azure no
Microsoft Learn.
O CLI do Azure.
Os utilitários necessários para usar o Azure Quantum (inclui a extensão quantum para a CLI do Azure).
Um grupo de recursos do Azure em que o workspace do Quantum residirá.
Uma conta de armazenamento no grupo de recursos a ser associada ao workspace do Quantum. Vários
workspaces podem ser associados à mesma conta.

Configuração do ambiente
1. Faça logon no Azure usando suas credenciais.

az login

2. Caso você tenha mais de uma assinatura associada à sua conta do Azure, especifique a assinatura que
deseja usar.

az account set -s <Your subscription ID>

3. Se esta for a primeira vez que você cria workspaces do Quantum em sua assinatura, registre o provedor
de recursos com este comando:

az provider register --namespace Microsoft.Quantum

Criar um workspace do Azure Quantum


Para criar um novo workspace do Azure Quantum, você precisa saber:
O local ou o nome da região do Azure em que o recurso residirá. Você pode usar a lista de regiões e seus
códigos do gerenciador de recursos compatíveis com a ferramenta CLI do Azure (por exemplo, westus ).
Nome do grupo de recursos associado ao novo workspace. (Por exemplo, MyResourceGroup ).
Uma conta de armazenamento no mesmo grupo de recursos e assinatura do workspace do Quantum. É
possível criar uma nova conta de armazenamento a partir da ferramenta CLI do Azure. (Por exemplo,
MyStorageAccount )
O nome do workspace do Quantum a ser criado. (Por exemplo, MyQuantumWorkspace )
Em seguida, crie o workspace usando o comando a seguir, seguindo os exemplos anteriores:

az quantum workspace create -l westus -g MyResourceGroup -w MyQuantumWorkspace -a MyStorageAccount

Por padrão, o novo workspace será criado contendo apenas o provedor QIO Básico da Microsoft. É possível
modificar o workspace a qualquer momento para adicionar ou remover outros provedores. Para isso, use o
portal do Azure e edite o workspace.
Como alternativa, crie o workspace de uma forma mais avançada e especifique os provedores usando
diretamente a CLI.

Especificar provedores adicionais durante a criação de um workspace


do Azure Quantum
1. Para recuperar a lista de provedores Quantum disponíveis, use o seguinte comando (usando westus
como local de exemplo):

az quantum offerings list -l westus -o table

2. Depois de determinar o provedor e o SKU a serem incluídos no workspace, revise os termos usando esse
comando, assumindo que Myproviderid e MySKU são valores de exemplo:

az quantum offerings show-terms -l westus -p MyProviderId -k MySKU

3. A saída do comando acima inclui um campo booliano accepted que mostra se os termos deste provedor
já foram aceitos ou não, bem como um link para os termos de licença a serem analisados. Se você decidir
aceitar esses termos, use o comando a seguir para registrar seu aceite.

az quantum offerings accept-terms -l westus -p MyProviderId -k MySKU

4. Depois de revisar e aceitar todos os termos e condições necessários, crie seu workspace especificando
uma lista de combinações de provedor/SKU separadas por vírgulas, como no exemplo abaixo:

az quantum workspace create -l westus -g MyResourceGroup -w MyQuantumWorkspace -a MyStorageAccount -r


"MyProvider1/MySKU1, MyProvider2/MySKU2"

Excluir um workspace do Quantum


Se você souber o nome e o grupo de recursos do workspace do Quantum que deseja excluir, use o seguinte
comando (usando os mesmos nomes que o exemplo acima):

az quantum workspace delete -g MyResourceGroup -w MyQuantumWorkspace

TIP
Se você não se lembrar do nome exato, visualize a lista completa de workspaces do Quantum de sua assinatura usando
az quantum workspace list -o table .
Depois de excluir o workspace, ele ainda continuará aparecendo na lista enquanto está sendo excluído na
nuvem. No entanto, a propriedade provisioningState do workspace será alterada imediatamente para indicar
que está sendo excluída. É possível visualizar essas informações executando as etapas a seguir:

az quantum workspace show -g MyResourceGroup -w MyQuantumWorkspace

NOTE
Caso você tenha usado o comando anterior az quantum workspace set para especificar um workspace padrão do
Quantum, chame o comando sem os parâmetros para excluir (e limpar) o workspace padrão.

az quantum workspace delete

Próximas etapas
Agora que você sabe criar e excluir workspace, pode aprender sobre os diferentes destinos para executar
algoritmos de Quantum no Azure Quantum.
Guia de início rápido do IonQ para Azure Quantum
19/05/2021 • 7 minutes to read

Saiba como usar o Azure Quantum para executar problemas de Q# na QPU ou no simulador do IonQ.

Pré-requisitos
Para concluir este tutorial, você precisa de uma assinatura do Azure. Se você não tiver uma assinatura do
Azure, crie uma conta gratuita antes de começar.
Neste guia, usaremos o Visual Studio Code que você pode baixar e usar gratuitamente.

Instalar o QDK (Quantum development kit) e outros recursos


Para escrever um programa Q# e executá-lo com o IonQ, você precisará instalar estes recursos:
1. Instale a extensão do Microsoft QDK para VS Code.
2. Instale a CLI do Azure.
3. Instale a extensão da CLI quantum para a CLI do Azure.

az extension add -n quantum

Criar um workspace do Azure Quantum


Para usar o serviço do Azure Quantum, adicione um recurso de workspace do Azure Quantum à sua assinatura
do Azure no portal do Azure. Um recurso de workspace do Azure Quantum, ou simplesmente workspace, é uma
coleção de ativos associados à execução de aplicativos quânticos ou de otimização.
Para abrir o portal do Azure, vá até https://portal.azure.com e siga estas etapas:

Observação: este é um link especial que permite que você crie um workspace no portal do Azure. Sem usar
o link, você poderá ver os workspaces existentes, mas não poderá criar outros.

1. Selecione Criar um recurso e pesquise por Azure Quantum . Na página de resultados, você verá um
bloco para o serviço do Azure Quantum (versão prévia) .
2. Selecione Azure Quantum (versão prévia) e selecione Criar . Isso abrirá um formulário para a criação
de um workspace.

3. Preencha os detalhes de seu workspace:


Assinatura: a assinatura que você deseja associar a esse workspace.
Grupo de recursos: o grupo de recursos ao qual você deseja atribuir esse workspace.
Nome: o nome do workspace.
Região: a região do workspace.
Conta de armazenamento : a conta de armazenamento do Azure para armazenar seus trabalhos e
resultados. Caso você ainda não tenha uma conta de armazenamento, selecione Criar uma nova
conta de armazenamento e preencha os campos necessários. Para essa versão prévia,
recomendamos usar os valores padrão.
NOTE
Você deve ser um proprietário do grupo de recursos selecionado para criar uma conta de armazenamento. Para
obter mais informações sobre como funcionam os grupos de recursos no Azure, confira Controlar e organizar os
recursos do Azure com o Azure Resource Manager.

4. Depois de preencher as informações, selecione a guia Provedores para adicionar os provedores ao


workspace. Um provedor fornece acesso a um serviço quântico, que pode ser um hardware quântico, um
simulador quântico ou um serviço de otimização.

NOTE
Se você não vir o provedor IonQ, talvez ainda não tenha acesso à sua versão prévia. Se você recebeu um email
com boas-vindas para a versão prévia do IonQ, mas não consegue ver seu provedor, crie um tíquete com o
Suporte do Azure.

NOTE
Por padrão, o serviço Azure Quantum adiciona o provedor Microsoft QIO a todos os workspaces.
5. Adicione pelo menos o provedor IonQ e clique em Examinar + criar .
6. Examine as configurações e aprove os Termos e Condições de Uso dos provedores selecionados. Se tudo
estiver correto, selecione Criar para criar seu workspace.

NOTE
Os preços do Azure Quantum variam por provedor. Consulte as informações na guia Provedores do seu workspace do
Azure Quantum, no portal do Azure, para obter os preços atualizados.

Configurar seu projeto e escrever seu programa


Em seguida, vamos abrir o Visual Studio Code e criar um projeto Q#.
1. No VS Code, abra o menu Exibir e selecione Paleta de Comandos .
2. Digite Q#: Create New Project .
3. Selecione Aplicativo de console autônomo .
4. Selecione um diretório para manter seu projeto, por exemplo, seu diretório base. Insira QuantumRNG
como o nome do projeto e, em seguida, selecione Criar Projeto .
5. Na janela que aparece na parte inferior, selecione Abrir novo projeto .
6. Você deverá ver dois arquivos: o arquivo de projeto e Program.qs , que contém o código inicial. Abra
Program.qs .
7. Comece abrindo o arquivo QuantumRNG.csproj e adicionando a propriedade ExecutionTarget , que
dará feedback em tempo de design sobre a compatibilidade do seu programa para o hardware do IonQ.

<Project Sdk="Microsoft.Quantum.Sdk/0.14.2011120240">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ExecutionTarget>ionq.qpu</ExecutionTarget>
</PropertyGroup>
</Project>
1. Substitua o conteúdo de Program.qs pelo programa:

namespace QuantumRNG {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Canon;

@EntryPoint()
operation GenerateRandomBits() : Result[] {
use qubits = Qubit[4];
ApplyToEach(H, qubits);
return MultiM(qubits);
}
}

NOTE
Se você quiser saber mais sobre esse código de programa, recomendamos Criar seu primeiro programa Q# usando o
Quantum development kit.

Preparar a CLI do AZ
Em seguida, vamos preparar seu ambiente para executar o programa em relação ao workspace que você criou.
1. Use quantum workspace set para selecionar o workspace que você criou acima como padrão. Observe
que você também precisa especificar o grupo de recursos em que o criou, por exemplo:

az quantum workspace set -g MyResourceGroup -w MyWorkspace -o table

Location Name ResourceGroup


----------- --------------------------------- --------------------------------
westus ws-yyyyyy rg-yyyyyyyyy

2. No workspace, há diferentes destinos disponíveis dos provedores que você adicionou quando o criou.
Você pode exibir uma lista de todos os destinos disponíveis com o comando
az quantum target list -o table :

az quantum target list -o table

Provider Target-id Status Average Queue Time


---------- ---------------------------------------------- --------- --------------------
ionq ionq.qpu Available 0
ionq ionq.simulator Available 0

NOTE
Quando você envia um trabalho para o Azure Quantum, ele espera em uma fila até que o provedor esteja pronto
para executar seu programa. A coluna tempo médio de fila do comando lista de destino mostra por quanto
tempo os trabalhos de execução recentemente esperaram na fila. Isso pode dar uma ideia de quanto tempo você
terá que aguardar.

Simular o programa
Antes de executar um programa em um hardware real, recomendamos simulá-lo primeiro (se possível, com
base no número de qubits necessários), para ajudar a garantir que seu algoritmo esteja fazendo o que você
deseja. Felizmente, o IonQ fornece um simulador ideal que você pode usar.

NOTE
Você também pode simular programas Q# localmente usando o Simulador de Estado Completo.

Execute o programa com az quantum execute --target-id ionq.simulator -o table . Esse comando compilará seu
programa, enviará para o Azure Quantum e aguardará até que o IonQ termine de simular o programa. Uma vez
feito isso, ele gerará um histograma, que deverá ser parecido com o do exemplo abaixo:

az quantum execute --target-id ionq.simulator -o table

.........
Result Frequency
--------- ----------- -------------------------
[0,0,0,0] 0.06250000 ▐█ |
[1,0,0,0] 0.06250000 ▐█ |
[0,1,0,0] 0.06250000 ▐█ |
[1,1,0,0] 0.06250000 ▐█ |
[0,0,1,0] 0.06250000 ▐█ |
[1,0,1,0] 0.06250000 ▐█ |
[0,1,1,0] 0.06250000 ▐█ |
[1,1,1,0] 0.06250000 ▐█ |
[0,0,0,1] 0.06250000 ▐█ |
[1,0,0,1] 0.06250000 ▐█ |
[0,1,0,1] 0.06250000 ▐█ |
[1,1,0,1] 0.06250000 ▐█ |
[0,0,1,1] 0.06250000 ▐█ |
[1,0,1,1] 0.06250000 ▐█ |
[0,1,1,1] 0.06250000 ▐█ |
[1,1,1,1] 0.06250000 ▐█ |

Isso mostra uma frequência igual para cada um dos 16 estados possíveis para medir 4 qubits, que é o que
esperamos de um simulador ideal. Isso significa que estamos prontos para executá-lo na QPU.

Executar o programa em um hardware


Para executar o programa no hardware, usaremos o comando de envio de trabalho assíncrono
az quantum job submit . Assim como o comando execute , ele vai compilar e enviar seu programa, mas não
esperará até que a execução seja concluída. Recomendamos esse padrão de execução em hardware porque
pode ser necessário esperar um pouco para que o trabalho seja concluído. Para ter uma ideia do tempo de
espera, você pode executar az quantum target list -o table conforme descrito acima.

az quantum job submit --target-id ionq.qpu -o table

Name Id Status Target Submission time


---------- ------------------------------------ -------- -------- ---------------------------------
QuantumRNG 5aa8ce7a-25d2-44db-bbc3-87e48a97249c Waiting ionq.qpu 2020-10-22T22:41:27.8855301+00:00

A tabela acima mostra que o trabalho foi enviado e está aguardando sua vez de ser executado. Para verificar o
status, use o comando az quantum job show , substituindo o parâmetro job-id com saída de ID pelo comando
anterior:
az quantum job show -o table --job-id 5aa8ce7a-25d2-44db-bbc3-87e48a97249c

Name Id Status Target Submission time


---------- ------------------------------------ -------- -------- ---------------------------------
QuantumRNG 5aa8ce7a-25d2-44db-bbc3-87e48a97249c Waiting ionq.qpu 2020-10-22T22:41:27.8855301+00:00

Eventualmente, você verá que Status na tabela acima é alterado para Succeeded . Depois disso, você pode
obter os resultados do trabalho executando az quantum job output :

az quantum job output -o table --job-id 5aa8ce7a-25d2-44db-bbc3-87e48a97249c

Result Frequency
--------- ----------- -------------------------
[0,0,0,0] 0.05200000 ▐█ |
[1,0,0,0] 0.07200000 ▐█ |
[0,1,0,0] 0.05000000 ▐█ |
[1,1,0,0] 0.06800000 ▐█ |
[0,0,1,0] 0.04600000 ▐█ |
[1,0,1,0] 0.06000000 ▐█ |
[0,1,1,0] 0.06400000 ▐█ |
[1,1,1,0] 0.07600000 ▐██ |
[0,0,0,1] 0.04800000 ▐█ |
[1,0,0,1] 0.06200000 ▐█ |
[0,1,0,1] 0.07400000 ▐█ |
[1,1,0,1] 0.08000000 ▐██ |
[0,0,1,1] 0.05800000 ▐█ |
[1,0,1,1] 0.06800000 ▐█ |
[0,1,1,1] 0.05200000 ▐█ |
[1,1,1,1] 0.07000000 ▐█ |

O histograma que você recebe pode ser um pouco diferente do mostrado acima, mas você deve descobrir que
os estados geralmente são observados com a mesma frequência.

Próximas etapas
Este guia de início rápido demonstrou como começar a executar programas Q# na QPU e no simulador do IonQ.
Para obter mais informações sobre a oferta IonQ, confira o Provedor do IonQ.
Recomendamos que você continue seu percurso aprendendo mais sobre os diferentes tipos de destinos no
Azure Quantum, que determinarão quais programas Q# você pode executar em um determinado provedor.
Você também pode estar interessado em aprender a enviar trabalhos do Q# com Jupyter Notebooks ou com
Python.
Procurando mais amostras para executar? Confira o diretório de amostras.
Por fim, se você gostaria de saber mais sobre como escrever programas em Q#, confira a Documentação do
Microsoft Quantum.
Início rápido do Honeywell para Azure Quantum
19/05/2021 • 6 minutes to read

Saiba como usar o Azure Quantum para executar problemas de Q# na QPU ou no simulador do Honeywell.

Pré-requisitos
Para concluir este tutorial, você precisa de uma assinatura do Azure. Se você não tiver uma assinatura do
Azure, crie uma conta gratuita antes de começar.
Neste guia, usaremos o Visual Studio Code que você pode baixar e usar gratuitamente.

Instalar o Quantum development kit e outros recursos


Para escrever um programa Q# e executá-lo com o Honeywell, você precisará instalar estes recursos:
1. Instale a extensão do Microsoft QDK para VS Code.
2. Instale a CLI do Azure.
3. Instale a extensão da CLI quantum para a CLI do Azure.

az extension add -n quantum

Criar um workspace do Azure Quantum


Para usar o serviço do Azure Quantum, adicione um recurso de workspace do Azure Quantum à sua assinatura
do Azure no portal do Azure. Um recurso de workspace do Azure Quantum, ou simplesmente workspace, é uma
coleção de ativos associados à execução de aplicativos quânticos ou de otimização.
Para abrir o portal do Azure, vá até https://portal.azure.com e siga estas etapas:
1. Selecione Criar um recurso e pesquise por Azure Quantum . Na página de resultados, você verá um
bloco para o serviço do Azure Quantum (versão prévia) .
2. Selecione Azure Quantum (versão prévia) e selecione Criar . Isso abrirá um formulário para a criação
de um workspace.

3. Preencha os detalhes de seu workspace:


Assinatura: a assinatura que você deseja associar a esse workspace.
Grupo de recursos: o grupo de recursos ao qual você deseja atribuir esse workspace.
Nome: o nome do workspace.
Região: a região do workspace.
Conta de armazenamento : a conta de armazenamento do Azure para armazenar seus trabalhos e
resultados. Caso você ainda não tenha uma conta de armazenamento, selecione Criar uma nova
conta de armazenamento e preencha os campos necessários. Para essa versão prévia,
recomendamos usar os valores padrão.

NOTE
Você deve ser um proprietário do grupo de recursos selecionado para criar uma conta de armazenamento. Para
obter mais informações sobre como funcionam os grupos de recursos no Azure, confira Controlar e organizar os
recursos do Azure com o Azure Resource Manager.
4. Depois de preencher as informações, selecione a guia Provedores para adicionar os provedores ao
workspace. Um provedor fornece acesso a um serviço quântico, que pode ser um hardware quântico, um
simulador quântico ou um serviço de otimização.

NOTE
Se você não vir o provedor Honeywell, talvez ainda não tenha acesso à sua versão prévia. Se você recebeu um
email com boas-vindas para a versão prévia do Honeywell, mas não consegue ver seu provedor, crie um tíquete
com o Suporte do Azure.

NOTE
Por padrão, o serviço Azure Quantum adiciona o provedor Microsoft QIO a todos os workspaces.

5. Adicione pelo menos o provedor Honeywell e clique em Examinar + criar .


6. Examine as configurações e aprove os Termos e Condições de Uso dos provedores selecionados. Se tudo
estiver correto, selecione Criar para criar seu workspace.
NOTE
Os preços do Azure Quantum variam por provedor. Consulte as informações na guia Provedores do seu workspace do
Azure Quantum, no portal do Azure, para obter os preços atualizados.

Configurar seu projeto e escrever seu programa


Em seguida, vamos abrir o Visual Studio Code e criar um projeto Q#.
1. No VS Code, abra o menu Exibir e selecione Paleta de Comandos .
2. Digite Q#: Create New Project .
3. Selecione Aplicativo de console autônomo .
4. Selecione um diretório para manter seu projeto, por exemplo, seu diretório base. Insira QuantumRNG
como o nome do projeto e selecione Criar Projeto .
5. Na janela que aparece na parte inferior, selecione Abrir novo projeto .
6. Você deverá ver dois arquivos: o arquivo de projeto e Program.qs , que contém o código inicial. Abra
Program.qs .
7. Comece abrindo o arquivo QuantumRNG.csproj e adicionando a propriedade ExecutionTarget , que
dará feedback em tempo de design sobre a compatibilidade do seu programa para o hardware do
Honeywell.

<Project Sdk="Microsoft.Quantum.Sdk/0.14.2011120240">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ExecutionTarget>honeywell.hqs-lt-1.0</ExecutionTarget>
</PropertyGroup>
</Project>

1. Substitua o conteúdo de Program.qs pelo programa:

namespace QuantumRNG {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Canon;

@EntryPoint()
operation GenerateRandomBits() : Result[] {
use qubits = Qubit[4];
ApplyToEach(H, qubits);
return MultiM(qubits);
}
}

NOTE
Se você quiser saber mais sobre esse código de programa, confira como Criar seu primeiro programa Q# usando o
Quantum development kit.

Preparar a CLI do AZ
Em seguida, vamos preparar seu ambiente para executar o programa em relação ao workspace que você criou.
1. Use quantum workspace set para selecionar o workspace que você criou acima como padrão. Observe
que você também precisa especificar o grupo de recursos em que o criou, por exemplo:

az quantum workspace set -g MyResourceGroup -w MyWorkspace -o table

Location Name ResourceGroup


----------- --------------------------------- --------------------------------
westus ws-yyyyyy rg-yyyyyyyyy

2. No workspace, há diferentes destinos disponíveis dos provedores que você adicionou quando o criou.
Você pode exibir uma lista de todos os destinos disponíveis com o comando
az quantum target list -o table :

az quantum target list -o table

Provider Target-id Status Average Queue Time


---------- ---------------------------------------------- --------- --------------------
honeywell honeywell.hqs-lt-1.0 Available 0
honeywell honeywell.hqs-lt-1.0-apival Available 0

NOTE
Quando você envia um trabalho para o Azure Quantum, ele espera em uma fila até que o provedor esteja pronto
para executar seu programa. A coluna tempo médio de fila do comando lista de destino mostra por quanto
tempo os trabalhos de execução recentemente tiveram que esperar na fila. Isso pode dar uma ideia de quanto
tempo você terá que aguardar.

Executar o programa em um hardware


Para executar o programa no hardware, usaremos o comando de envio de trabalho assíncrono
az quantum job submit . Assim como o comando execute , ele vai compilar e enviar seu programa, mas não
esperará até que a execução seja concluída. Recomendamos esse padrão de execução em hardware porque
pode ser necessário esperar um pouco para que o trabalho seja concluído. Para ter uma ideia do tempo de
espera, você pode executar az quantum target list -o table conforme descrito acima.

az quantum job submit --target-id honeywell.hqs-lt-1.0 -o table

Name Id Status Target Submission time


---------- ------------------------------------ -------- -------- --------------------------
-------
QuantumRNG b4d17c63-2119-4d92-91d9-c18d1a07e08f Waiting honeywell.hqs-lt-1.0 2020-01-
12T22:41:27.8855301+00:00

A tabela acima mostra que o trabalho foi enviado e está aguardando sua vez de ser executado. Para verificar o
status, use o comando az quantum job show , substituindo o parâmetro job-id com saída de ID pelo comando
anterior:
az quantum job show -o table --job-id b4d17c63-2119-4d92-91d9-c18d1a07e08f

Name Id Status Target Submission time


---------- ------------------------------------ -------- -------- ---------------------------------
QuantumRNG b4d17c63-2119-4d92-91d9-c18d1a07e08f Waiting honeywell.hqs-lt-1.0 2020-10-
22T22:41:27.8855301+00:00

Eventualmente, você verá que Status na tabela acima é alterado para Succeeded . Depois disso, você pode
obter os resultados do trabalho executando az quantum job output :

az quantum job output -o table --job-id b4d17c63-2119-4d92-91d9-c18d1a07e08f

Result Frequency
--------- ----------- -------------------------
[0,0,0,0] 0.05200000 ▐█ |
[1,0,0,0] 0.07200000 ▐█ |
[0,1,0,0] 0.05000000 ▐█ |
[1,1,0,0] 0.06800000 ▐█ |
[0,0,1,0] 0.04600000 ▐█ |
[1,0,1,0] 0.06000000 ▐█ |
[0,1,1,0] 0.06400000 ▐█ |
[1,1,1,0] 0.07600000 ▐██ |
[0,0,0,1] 0.04800000 ▐█ |
[1,0,0,1] 0.06200000 ▐█ |
[0,1,0,1] 0.07400000 ▐█ |
[1,1,0,1] 0.08000000 ▐██ |
[0,0,1,1] 0.05800000 ▐█ |
[1,0,1,1] 0.06800000 ▐█ |
[0,1,1,1] 0.05200000 ▐█ |
[1,1,1,1] 0.07000000 ▐█ |

O histograma que você recebe pode ser um pouco diferente do mostrado acima, mas você deve descobrir que
os estados geralmente são observados com a mesma frequência.

Próximas etapas
Este guia de início rápido demonstrou como começar a executar programas Q# na QPU e no simulador do
Honeywell. Para obter mais informações sobre as ofertas Honeywell, confira a documentação do Provedor do
Honeywell.
Recomendamos que você continue seu percurso aprendendo mais sobre os diferentes tipos de destinos no
Azure Quantum, que determinarão quais programas Q# você pode executar em um determinado provedor.
Você também pode estar interessado em aprender a enviar trabalhos do Q# com Jupyter Notebooks ou com
Python.
Procurando mais amostras para executar? Confira o diretório de amostras.
Por fim, se você gostaria de saber mais sobre como escrever programas em Q#, confira a Documentação do
Microsoft Quantum.
Guia de início rápido de otimização do Microsoft
QIO para Azure Quantum
19/05/2021 • 8 minutes to read

Saiba como usar o Microsoft QIO no Azure Quantum para resolver um problema de otimização binária simples.

Pré-requisitos
Para concluir este tutorial, você precisa de uma assinatura do Azure. Se você não tiver uma assinatura do
Azure, crie uma conta gratuita antes de começar.

Criar um workspace do Azure Quantum


Para usar o serviço do Azure Quantum, adicione um recurso de workspace do Azure Quantum à sua assinatura
do Azure no portal do Azure. Um recurso de workspace do Azure Quantum, ou simplesmente workspace, é uma
coleção de ativos associados à execução de aplicativos quânticos ou de otimização.
Para abrir o portal do Azure, vá até https://portal.azure.com e siga estas etapas:
1. Selecione Criar um recurso e pesquise por Azure Quantum . Na página de resultados, você verá um
bloco para o serviço do Azure Quantum (versão prévia) .

2. Selecione Azure Quantum (versão prévia) e selecione Criar . Isso abrirá um formulário para a criação
de um workspace.
3. Preencha os detalhes de seu workspace:
Assinatura: a assinatura que você deseja associar a esse workspace.
Grupo de recursos: o grupo de recursos ao qual você deseja atribuir esse workspace.
Nome: o nome do workspace.
Região: a região do workspace.
Conta de armazenamento : a conta de armazenamento do Azure para armazenar seus trabalhos e
resultados. Caso você ainda não tenha uma conta de armazenamento, selecione Criar uma nova
conta de armazenamento e preencha os campos necessários. Para essa versão prévia,
recomendamos usar os valores padrão.

NOTE
Você deve ser um proprietário do grupo de recursos selecionado para criar uma conta de armazenamento. Para
obter mais informações sobre como funcionam os grupos de recursos no Azure, confira Controlar e organizar os
recursos do Azure com o Azure Resource Manager.

4. Depois de preencher as informações, selecione a guia Provedores para adicionar os provedores ao


workspace. Um provedor fornece acesso a um serviço quântico, que pode ser um hardware quântico, um
simulador quântico ou um serviço de otimização.
5. Verifique se o provedor Microsoft QIO está habilitado (por padrão) e, em seguida, clique em Examinar +
criar .
6. Examine a configuração que você selecionou e, se tudo estiver correto, clique em Criar para criar seu
workspace.

NOTE
Os preços do Azure Quantum variam por provedor. Consulte as informações na guia Provedores do seu workspace do
Azure Quantum, no portal do Azure, para obter os preços atualizados.

Definir o problema de otimização


Neste guia, você vai resolver um exemplo de otimização simples para começar a usar os serviços de otimização
do Azure Quantum. Este guia de início rápido é baseado no exemplo de carregamento de remessa.
Suponha que haja duas entregas prontas para serem carregadas com contêineres e uma lista de contêineres de
pesos variados a serem atribuídos a cada remessa. O objetivo do problema de otimização é atribuir contêineres
a cada remessa de modo que o peso seja distribuído da maneira mais uniforme possível entre ambas as
remessas.
A função de custo para esse problema de otimização é semelhante ao seguinte:
$$ H^{2} = \Large(\sum_{i \in A \cup B} w_{i} x_{i})^{2} $$
Essa função de custo tem as seguintes propriedades:
Se todos os contêineres estiverem em uma remessa, a função estará em seu valor mais alto, refletindo que
essa é a solução menos ideal
Se os contêineres forem perfeitamente equilibrados, o valor da soma dentro do quadrado será ${0}$ – a
função tem seu valor mais baixo. Essa solução é ideal.
A meta é encontrar a configuração que gera o menor valor possível de $H^2$.
NOTE
Para obter uma explicação detalhada do cenário do problema e como a função de custo é construída, confira o exemplo
e/ou o módulo do Microsoft Learn associado.

Instalar o SDK do Python para otimização


Para implementar uma solução, primeiro verifique se você tem o SDK do Python para otimização instalado em
seu computador. Se você ainda não instalou o SDK do Python para otimização, siga estas etapas:
1. Instale o Python 3.6 ou posterior caso ainda não tenha feito isso.
2. Instale o PIP e verifique se você tem a versão 19.2 ou superior .
3. Instale o pacote azure-quantum do Python.

pip install --upgrade azure-quantum --pre


pip install azure-storage-blob --upgrade

Crie um objeto Workspace em seu código Python e faça logon


Agora, crie um arquivo Python ou Jupyter Notebook, importe o módulo Workspace do azure.quantum e crie um
objeto Workspace . Isso é o que você usará para enviar nosso problema de otimização para o Azure Quantum. O
valor de resource_id pode ser encontrado na página do portal do Azure para o workspace que você criou.
Depois que o objeto Workspace é criado, você faz logon usando o comando workspace.login() .

from azure.quantum import Workspace

# Copy the settings for your workspace below


workspace = Workspace (
subscription_id = "", # Add your subscription_id
resource_group = "", # Add your resource_group
name = "", # Add your workspace name
location = "" # Add your workspace location (for example, "westus")
)
workspace.login()

Na primeira vez que você executar esse código em seu dispositivo, uma janela poderá ser aberta no navegador
padrão solicitando suas credenciais.

Gerar os termos para o problema


Em seguida, você precisa transformar a representação matemática do problema em código. Como lembrete,
essa é a aparência da função de custo:
$$ H^{2} = \Large(\sum_{i \in A \cup B} w_{i} x_{i})^{2} $$
Abaixo, você pode ver o código necessário para gerar os termos ( Term ) da função de custo:
from typing import List
from azure.quantum.optimization import Problem, ProblemType, Term

def createProblemForContainerWeights(containerWeights: List[int]) -> List[Term]:

terms: List[Term] = []

# Expand the squared summation


for i in range(len(containerWeights)):
for j in range(len(containerWeights)):
if i == j:
# Skip the terms where i == j as they can be disregarded:
# w_i∗ w_j∗ x_i∗ x_j = w_i*w_j∗ (x_i)^2 = w_i∗ w_j
# for x_i = x_j, x_i ∈ {1, -1}
continue

terms.append(
Term(
c = containerWeights[i] * containerWeights[j],
indices = [i, j]
)
)

return terms

NOTE
Para obter uma explicação detalhada de como essa função é derivada, confira o exemplo de envio ou o módulo do
Microsoft Learn para otimização.

Criar uma instância de Problem


Agora que você tem um modo de gerar os termos para o problema, vamos fornecer um exemplo específico e
criar a função de custo:

# This array contains a list of the weights of the containers:


containerWeights = [1, 5, 9, 21, 35, 5, 3, 5, 10, 11]

# Create the Terms for this list of containers:


terms = createProblemForContainerWeights(containerWeights)

A próxima etapa é criar uma instância de um Problem para enviar ao solucionador do Azure Quantum:

# Create the Problem to submit to the solver:


problem = Problem(name="Ship Loading Problem", problem_type=ProblemType.ising, terms=terms)

Acima, você pode ver que forneceu os seguintes parâmetros:


name : o nome do problema, usado para identificar o trabalho no portal do Azure mais tarde
problem_type : nessa instância, você escolheu uma representação ising para o problema devido à maneira
como definimos a função de custo, no entanto, você poderia escolher uma representação pubo como
alternativa.
terms : esses são os termos que definem a função de custo que você gerou anteriormente.

Enviar seu problema ao Azure Quantum


A seguir, você enviará a instância Problem ao Azure Quantum.

from azure.quantum.optimization import ParallelTempering

# Instantiate a solver instance to solve the problem


solver = ParallelTempering(workspace, timeout=100) # timeout in seconds

# Optimize the problem


result = solver.optimize(problem)

Aqui, você criou uma instância de um solucionador ParallelTempering para o problema. Você poderia ter
escolhido outros solucionadores de otimização Microsoft QIO (por exemplo, SimulatedAnnealing ) sem precisar
alterar mais linhas de código. Para ver uma lista dos solucionadores disponíveis, vá para a página de referência.
O tipo Problem é o parâmetro comum para todos os solucionadores do Azure Quantum.
Em seguida, você chama solver.optimize() e fornece o problem como o argumento. Isso envia o problema de
maneira síncrona ao Azure Quantum e retorna um dicionário de valores Python para salvar a variável result
para análise na próxima etapa.

Você também pode enviar problemas de modo assíncrono. Para obter mais informações, você pode ir para
o guia para resolver problemas de execução prolongada.

Leitura de resultados
A etapa final é transformar o resultado retornado chamando solver.optimize() para algo legível por humanos.
O código a seguir usa a configuração dict retornada pelo serviço e imprime uma lista de atribuições de
contêiner:

def printResultSummary(result):
# Print a summary of the result
shipAWeight = 0
shipBWeight = 0
for container in result['configuration']:
containerAssignment = result['configuration'][container]
containerWeight = containerWeights[int(container)]
ship = ''
if containerAssignment == 1:
ship = 'A'
shipAWeight += containerWeight
else:
ship = 'B'
shipBWeight += containerWeight

print(f'Container {container} with weight {containerWeight} was placed on Ship {ship}')

print(f'\nTotal weights: \n\tShip A: {shipAWeight} tonnes \n\tShip B: {shipBWeight} tonnes')

printResultSummary(result)

A saída deve parecer com esta:


Container 0 with weight 1 was placed on Ship A
Container 1 with weight 5 was placed on Ship B
Container 2 with weight 9 was placed on Ship A
Container 3 with weight 21 was placed on Ship A
Container 4 with weight 35 was placed on Ship B
Container 5 with weight 5 was placed on Ship B
Container 6 with weight 3 was placed on Ship B
Container 7 with weight 5 was placed on Ship B
Container 8 with weight 10 was placed on Ship A
Container 9 with weight 11 was placed on Ship A

Total weights:
Ship A: 52 tonnes
Ship B: 53 tonnes

Próximas etapas
Durante este guia de início rápido, você viu um exemplo de ponta a ponta de como fazer uma função de custo
matemático, representá-la em código, enviá-la para o Azure Quantum e analisar os resultados. Para saber mais
sobre a oferta Microsoft QIO no Azure Quantum, confira a documentação do Provedor do Microsoft QIO.
Para obter informações mais detalhadas sobre o problema de otimização de envio, confira os seguintes
recursos:
Exemplo de carregamento de remessa
Módulo do Microsoft Learn
Depois de explorar o exemplo de carregamento de remessa em mais detalhes, talvez você considere útil lidar
com o exemplo de agendamento de oficina de trabalho mais complexo. O módulo Microsoft Learn associado
pode ser encontrado aqui.
Início rápido de otimização do 1QBit para o Azure
Quantum
19/05/2021 • 8 minutes to read

Saiba como usar o 1QBit no Azure Quantum para resolver problemas complexos de otimização.

Pré-requisitos
Para concluir este tutorial, você precisa de uma assinatura do Azure. Se você não tiver uma assinatura do
Azure, crie uma conta gratuita antes de começar.

Criar um workspace do Azure Quantum


Para usar o serviço do Azure Quantum, adicione um recurso de workspace do Azure Quantum à sua assinatura
do Azure no portal do Azure. Um recurso de workspace do Azure Quantum, ou simplesmente workspace, é uma
coleção de ativos associados à execução de aplicativos quânticos ou de otimização.
Para abrir o portal do Azure, vá até https://portal.azure.com e siga estas etapas:
1. Selecione Criar um recurso e pesquise por Azure Quantum . Na página de resultados, você verá um
bloco para o serviço do Azure Quantum (versão prévia) .

2. Selecione Azure Quantum (versão prévia) e selecione Criar . Isso abrirá um formulário para a criação
de um workspace.
3. Preencha os detalhes de seu workspace:
Assinatura: a assinatura que você deseja associar a esse workspace.
Grupo de recursos: o grupo de recursos ao qual você deseja atribuir esse workspace.
Nome: o nome do workspace.
Região: a região do workspace.
Conta de armazenamento : a conta de armazenamento do Azure para armazenar seus trabalhos e
resultados. Caso você ainda não tenha uma conta de armazenamento, selecione Criar uma nova
conta de armazenamento e preencha os campos necessários. Para essa versão prévia,
recomendamos usar os valores padrão.

NOTE
Você deve ser um proprietário do grupo de recursos selecionado para criar uma conta de armazenamento. Para
obter mais informações sobre como funcionam os grupos de recursos no Azure, confira Controlar e organizar os
recursos do Azure com o Azure Resource Manager.

4. Depois de preencher as informações, selecione a guia Provedores para adicionar os provedores ao


workspace. Um provedor fornece acesso a um serviço quântico, que pode ser um hardware quântico, um
simulador quântico ou um serviço de otimização.
NOTE
Por padrão, o serviço Azure Quantum adiciona o provedor Microsoft QIO a todos os workspaces.

5. Adicione pelo menos o provedor de plataforma de otimização 1Qloud e clique em Examinar + criar .
6. Examine a configuração que você selecionou e, se tudo estiver correto, clique em Criar para criar seu
workspace.

NOTE
Os preços do Azure Quantum variam por provedor. Consulte as informações na guia Provedores do seu workspace do
Azure Quantum, no portal do Azure, para obter os preços atualizados.

Definir o problema de otimização


Neste guia, você vai resolver um exemplo de otimização simples para começar a usar os serviços de otimização
do Azure Quantum. Este guia de início rápido é baseado no exemplo de carregamento de remessa.
Suponha que haja duas entregas prontas para serem carregadas com contêineres e uma lista de contêineres de
pesos variados a serem atribuídos a cada remessa. O objetivo do problema de otimização é atribuir contêineres
a cada remessa de modo que o peso seja distribuído da maneira mais uniforme possível entre ambas as
remessas.
A função de custo para esse problema de otimização é semelhante ao seguinte:
$$ H^{2} = \Large(\sum_{i \in A \cup B} w_{i} x_{i})^{2} $$
Essa função de custo tem as seguintes propriedades:
Se todos os contêineres estiverem em uma remessa, a função estará em seu valor mais alto, refletindo que
essa é a solução menos ideal
Se os contêineres forem perfeitamente equilibrados, o valor da soma dentro do quadrado será ${0}$ – a
função tem seu valor mais baixo. Essa solução é ideal.
A meta é encontrar a configuração que gera o menor valor possível de $H^2$.

NOTE
Para obter uma explicação detalhada do cenário do problema e como a função de custo é construída, confira o exemplo
e/ou o módulo do Microsoft Learn associado.

Instalar o SDK do Python para otimização


Para implementar uma solução, primeiro verifique se você tem o SDK do Python de otimização instalado em seu
computador. Se você ainda não instalou o SDK do Python de otimização, siga estas etapas:
1. Instale o Python 3.6 ou posterior caso ainda não tenha feito isso.
2. Instale o PIP e verifique se você tem a versão 19.2 ou superior .
3. Instale o pacote azure-quantum do Python.

pip install --upgrade azure-quantum

Crie um objeto Workspace em seu código Python e faça logon


Agora, crie um arquivo Python ou Jupyter Notebook, importe o módulo Workspace do azure.quantum e crie um
objeto Workspace . Isso é o que você usará para enviar nosso problema de otimização para o Azure Quantum. O
valor de resource_id pode ser encontrado na página do portal do Azure para o workspace do Quantum que
você criou.
Depois que o objeto Workspace é criado, você faz logon usando o comando workspace.login() .

from azure.quantum import Workspace

# Copy the settings for your workspace below


workspace = Workspace(
resource_id = "" # add the Resource ID of the Azure Quantum workspace you created
)
workspace.login()

Na primeira vez que você executar esse código em seu dispositivo, uma janela poderá ser aberta no navegador
padrão solicitando suas credenciais.

Gerar os termos para o problema


Em seguida, você precisa transformar a representação matemática do problema em código. Como lembrete,
essa é a aparência da função de custo:
$$ H^{2} = \Large(\sum_{i \in A \cup B} w_{i} x_{i})^{2} $$
Abaixo, você pode ver o código necessário para gerar os termos ( Term ) da função de custo:

from typing import List


from azure.quantum.optimization import Problem, ProblemType, Term

def createProblemForContainerWeights(containerWeights: List[int]) -> List[Term]:

terms: List[Term] = []

# Expand the squared summation


for i in range(len(containerWeights)):
for j in range(len(containerWeights)):
if i == j:
# Skip the terms where i == j as they can be disregarded:
# w_i∗ w_j∗ x_i∗ x_j = w_i*w_j∗ (x_i)^2 = w_i∗ w_j
# for x_i = x_j, x_i ∈ {1, -1}
continue

terms.append(
Term(
w = containerWeights[i] * containerWeights[j],
indices = [i, j]
)
)

return terms

NOTE
Para obter uma explicação detalhada de como essa função é derivada, confira o exemplo de envio ou o módulo do
Microsoft Learn para otimização.

Criar uma instância de Problem


Agora que você tem um modo de gerar os termos para o problema, vamos fornecer um exemplo específico e
criar a função de custo:

# This array contains a list of the weights of the containers:


containerWeights = [1, 5, 9, 21, 35, 5, 3, 5, 10, 11]

# Create the Terms for this list of containers:


terms = createProblemForContainerWeights(containerWeights)

A próxima etapa é criar uma instância de um Problem para enviar ao solucionador do Azure Quantum:

# Create the Problem to submit to the solver:


problem = Problem(name="Ship Loading Problem", problem_type=ProblemType.ising, terms=terms)

Acima, você pode ver que forneceu os seguintes parâmetros:


name : o nome do problema, usado para identificar o trabalho no portal do Azure mais tarde
problem_type : nessa instância, você escolheu uma representação ising para o problema devido à maneira
como definimos a função de custo, no entanto, você poderia escolher uma representação pubo como
alternativa.
terms : esses são os termos que definem a função de custo que você gerou anteriormente.

Enviar seu problema ao Azure Quantum


A seguir, você enviará a instância Problem ao Azure Quantum.

from azure.quantum.optimization.oneqbit import PathRelinkingSolver

# Instantiate a solver instance to solve the problem


solver = PathRelinkingSolver(workspace)

# Optimize the problem


result = solver.optimize(problem)

Aqui, você criou uma instância de um solucionador PathRelinkingSolver para o problema. Você também
poderia ter escolhido outros solucionadores de 1QBit (por exemplo, TabuSearch ) sem precisar alterar mais
linhas de código. Para ver uma lista dos solucionadores disponíveis, vá para a página de referência.
O tipo Problem é o parâmetro comum para todos os solucionadores do Azure Quantum.
Em seguida, você chama solver.optimize() e fornece o problem como o argumento. Isso envia o problema de
maneira síncrona ao Azure Quantum e retorna um dicionário de valores Python para salvar a variável result
para análise na próxima etapa.

Leitura de resultados
A etapa final é transformar o resultado retornado chamando solver.optimize() para algo legível por humanos.
O código a seguir usa a configuração dict retornada pelo serviço e imprime uma lista de atribuições de
contêiner:

def printResultSummary(result):
# Print a summary of the result
shipAWeight = 0
shipBWeight = 0
for container in result['configuration']:
containerAssignment = result['configuration'][container]
containerWeight = containerWeights[int(container)]
ship = ''
if containerAssignment == 1:
ship = 'A'
shipAWeight += containerWeight
else:
ship = 'B'
shipBWeight += containerWeight

print(f'Container {container} with weight {containerWeight} was placed on Ship {ship}')

print(f'\nTotal weights: \n\tShip A: {shipAWeight} tonnes \n\tShip B: {shipBWeight} tonnes')

printResultSummary(result['solutions'][0])

A saída deve parecer com esta:


Container 0 with weight 1 was placed on Ship A
Container 1 with weight 5 was placed on Ship B
Container 2 with weight 9 was placed on Ship A
Container 3 with weight 21 was placed on Ship A
Container 4 with weight 35 was placed on Ship B
Container 5 with weight 5 was placed on Ship B
Container 6 with weight 3 was placed on Ship B
Container 7 with weight 5 was placed on Ship B
Container 8 with weight 10 was placed on Ship A
Container 9 with weight 11 was placed on Ship A

Total weights:
Ship A: 52 tonnes
Ship B: 53 tonnes

Próximas etapas
Durante este guia de início rápido, você viu um exemplo de ponta a ponta de como fazer uma função de custo
matemático, representá-la em código, enviá-la para o Azure Quantum e analisar os resultados. Para saber mais
sobre a oferta de 1QBit no Azure Quantum, confira a documentação do provedor de 1QBit.
Para obter informações mais detalhadas sobre o problema de otimização de envio, confira os seguintes
recursos:
Exemplo de carregamento de remessa
Módulo do Microsoft Learn
Depois de explorar o exemplo de carregamento de remessa em mais detalhes, talvez você considere útil lidar
com o exemplo de agendamento de oficina de trabalho mais complexo. O módulo Microsoft Learn associado
pode ser encontrado aqui.
Criar e executar aplicativos Q# no Azure Quantum
19/05/2021 • 4 minutes to read

Este guia descreve o processo para criar um aplicativo Q# e executá-lo nos diferentes destinos de computação
quântica disponíveis no Azure Quantum. Para ver quais tipos de destinos de computação quântica o Azure
Quantum oferece, confira o artigo Destinos no Azure Quantum.

Criar e executar aplicativos para destinos de perfil completos


Os destinos de perfil completos podem executar qualquer programa Q#, o que significa que você pode
escrever programas sem restrições de funcionalidade. O Azure Quantum não fornece nenhum destino com esse
perfil ainda, mas você pode experimentar qualquer programa Q# localmente usando o avaliador de estado
completo ou o estimador de recursos do QDK.
Se você precisar de ajuda para configurar seu ambiente para executar os programas do Q# localmente, confira
Introdução ao QDK.
Você também pode explorar diferentes amostras de código Q# para executar localmente com o QDK.

Criar e executar aplicativos para destinos de perfil Sem Fluxo de


Controle
Destinos do perfil Sem Fluxo de Controle podem executar uma ampla variedade de aplicativos Q#, com a
restrição de que eles não podem usar resultados de medidas qubit para controlar o fluxo do programa. Mais
especificamente, valores do tipo Result não dão suporte à comparação de igualdade.
Por exemplo, essa operação NÃO pode ser executada em um destino Sem Fluxo de Controle:

operation SetQubitState(desired : Result, q : Qubit) : Result {


if (desired != M(q)) {
X(q);
}
}

Tentar executar essa operação em um destino Sem Fluxo de Controle falhará porque avaliará uma comparação
entre dois resultados ( desired != M(q) ) para controlar o fluxo de computação com uma instrução if .

NOTE
No momento, há uma limitação adicional para esse tipo de destino de perfil: não é possível aplicar operações em qubits
que foram medidos, mesmo que você não use os resultados para controlar o fluxo do programa. Essa limitação não é
inerente a esse tipo de perfil, mas é circunstancial para a situação da versão prévia limitada.

Atualmente, não há destinos Sem Fluxo de Controle disponível para o Azure Quantum:
Provedor : IonQ
Simulador de IonQ ( ionq.simulator )
IonQ QPU: ( ionq.qpu )
Criar aplicativos para destinos IonQ
Siga estas etapas nesta seção para criar um aplicativo a ser executado em destinos IonQ.
Pré-requisitos
Instale o QDK.
Um workspace Quantum com IonQ listado como um provedor. Para criar um workspace, confira Criar um
workspace do Azure Quantum.
Etapas
1. Crie um aplicativo Q# usando o modelo de projeto Q#.
2. Abra o arquivo *.csproj em um editor de texto (por exemplo, VS Code) e edite o arquivo para:
Verifique se o projeto aponta para a versão mais recente do QDK. Você pode verificar a versão mais
recente nas Notas sobre a versão do QDK oficiais.
Adicione uma linha especificando o destino:
IonQ QPU: <ExecutionTarget>ionq.qpu</ExecutionTarget>
Simulador de IonQ: <ExecutionTarget>ionq.simulator</ExecutionTarget>
Seu arquivo *.csproj deve ter a seguinte aparência:

<Project Sdk="Microsoft.Quantum.Sdk/X.XX.XXXXXXXX">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ExecutionTarget>ionq.qpu</ExecutionTarget>
</PropertyGroup>

</Project>

em que X.XX.XXXXXXXX é um espaço reservado para o número da versão mais recente do QDK.
3. Escreva o programa Q#, tendo em mente que não é possível comparar os resultados da medição para
controlar o fluxo do programa.
4. Compile e execute o programa localmente usando os destinos locais do QDK. Eles informarão se o
aplicativo Q# pode ser executado em destinos do IonQ verificando o cumprimento de restrições Sem
Fluxo de Controle e calculando os recursos necessários.
Você pode executar o programa Q# localmente usando o simulador de estado completo do QDK
usando o comando dotnet run . Como você selecionou o ExecutionTarget no arquivo *.csproj , a
saída do console avisará se você criou um arquivo que não é compatível com o perfil Sem Fluxo de
Controle.
Você pode usar o resources estimator para estimar quais recursos seu programa Q# exige para ser
executado. Você invoca o avaliador de recursos com o comando:
dotnet run --simulator ResourcesEstimator .

5. Quando o programa Q# estiver pronto, envie o trabalho para o Azure Quantum usando seu ambiente
preferido com a ID de destino ionq.qpu para o QPU e ionq.simulator para o simulador.

Para obter mais informações sobre como enviar trabalhos para o Azure Quantum, confira:
Envie trabalhos ao Azure Quantum usando a CLI do Azure.
Envie trabalhos para o Azure Quantum usando o Python.
Enviar trabalhos para o Azure Quantum usando Jupyter Notebooks Q#

Criar e executar aplicativos para destinos de Feedback de Medição


Básica
Os destinos básicos do perfil de Feedback de Medição Básica podem executar uma ampla variedade de
aplicativos Q#, com a restrição de que você só pode comparar valores de tipo Result como parte das
condições dentro de instruções if em operações. Os blocos condicionais correspondentes não podem conter
instruções return ou set . Esse tipo de perfil pressupõe um aprimoramento em relação a nenhum perfil de
fluxo de controle, mas ainda está sujeito a algumas limitações.
No momento, o Azure Quantum não hospeda nenhum destino com esse perfil, no entanto, estamos planejando
disponibilizar alguns durante a revisão limitada.

Próximas etapas
Agora que você sabe como criar aplicativos Q#, pode obter mais detalhes sobre como enviar trabalhos para
o Azure Quantum.
Você também pode experimentar os diferentes exemplos que disponibilizamos ou tentamos enviar os
próprios projetos.
Enviar trabalhos para o Quantum do Azure com a
interface de linha de comando
01/05/2021 • 4 minutes to read

Este guia mostrará como enviar trabalhos para o Azure Quantum usando a CLI (interface de linha de comando).

Pré-requisitos
Verifique se os seguintes itens estão instalados em seu computador:
Um espaço de trabalho do Azure Quantum em sua assinatura do Azure. Para criar um espaço de trabalho,
consulte Criar um espaço de trabalho no Azure Quantum.
A versão mais recente do Quantum Development Kit.
O CLI do Azure.
Os utilitários necessários para usar o Azure Quantum (inclui a quantum extensão para o CLI do Azure).

Enviar um trabalho para o Quantum do Azure com o CLI do Azure


Estas etapas mostram como usar o CLI do Azure para executar um aplicativo Q # e selecionar um destino dos
diferentes provedores do seu espaço de trabalho do azure Quantum.

NOTE
Um provedor é um serviço Quantum de parceiro que consiste em hardware Quantum, um simulador ou um serviço de
otimização.

1. Faça logon no Azure usando suas credenciais.

az login

NOTE
Caso você tenha mais de uma assinatura associada à sua conta do Azure, você deve especificar a assinatura que deseja
usar. Isso pode ser feito com o comando az account set -s <Your subscription ID> .

1. É possível ver todos os espaços de trabalho do azure Quantum em sua assinatura com o seguinte
comando:

az quantum workspace list

2. É possível usar quantum workspace set para selecionar um espaço de trabalho padrão que deseja usar
para listar e enviar trabalhos. Observe que você também precisa especificar o grupo de recursos em que
o criou, por exemplo:
az quantum workspace set -g MyResourceGroup -w MyWorkspace -o table

Location Name ResourceGroup


----------- --------------------------------- --------------------------------
westus ws-yyyyyy rg-yyyyyyyyy

TIP
É possível verificar o estado de trabalho atual com o comando az quantum workspace show -o table .

1. No workspace do Azure Quantum, há diferentes destinos disponíveis dos provedores que você adicionou
quando o criou. Você pode exibir uma lista de todos os destinos disponíveis com o comando
az quantum target list -o table :

az quantum target list -o table

Provider Target-id Status Average Queue Time


---------- ---------------------------------------------- --------- --------------------
Microsoft microsoft.paralleltempering-parameterfree.cpu Available 0
Microsoft microsoft.paralleltempering.cpu Available 0
Microsoft microsoft.simulatedannealing-parameterfree.cpu Available 0
Microsoft microsoft.simulatedannealing.cpu Available 0
Microsoft microsoft.paralleltempering.fpga Available 0
Microsoft microsoft.simulatedannealing.fpga Available 0
ionq ionq.qpu Available 0
ionq ionq.simulator Available 0

2. Para enviar um novo trabalho, navegue até o diretório que contém o projeto usando a linha de comando
e envie seu trabalho. Use o comando az quantum job submit . Você precisa especificar o destino no qual
deseja executar o trabalho, por exemplo:

az quantum job submit --target-id MyProvider.MyTarget


Id State Target Submission time
------------------------------------ ------- ------------------- ---------------------------------
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy Waiting MyProvider.MyTarget 2020-06-12T14:20:18.6109317+00:00

O console produzirá as informações do trabalho, incluindo o ID do trabalho.


3. É possível usar o ID do trabalho para acompanhar seu status:

az quantum job show -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy -o table


Id State Target Submission time
------------------------------------ --------- ------------------- -------------------------------
--
yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy Succeeded MyProvider.MyTarget 2020-06-
12T14:20:19.819981+00:00

TIP
Para ver todos os trabalhos no espaço de trabalho, use o comando az quantum job list -o table .

1. Quando o trabalho for concluído, exiba os resultados do trabalho com o comando az quantum job output
:
az quantum job output -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy -o table

Result Frequency
-------- ----------- -------------------------
[0,0] 0.00000000 |
[1,0] 0.50000000 ▐███████████ |
[0,1] 0.25000000 ▐█████ |
[1,1] 0.25000000 ▐█████ |

TIP
Para enviar um trabalho de forma síncrona, por exemplo, aguardando a conclusão do trabalho e mostrando os resultados,
use o comando az quantum execute --target-id MyProvider.MyTarget .

Exemplo
Escreva seu aplicativo Quantum
Primeiro, você precisa do aplicativo de Quantum Q # que deseja executar no Azure Quantum.

TIP
Se esta for a primeira vez que você cria os aplicativos da Quantum do Q #, consulte nosso Módulo de Microsoft Learn.

Nesse caso, usaremos um gerador de bit aleatório de Quantum simples. Crie um projeto Q # e substitua o
conteúdo de Program.qs pelo seguinte código:

namespace RandomBit {

open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Measurement;

@EntryPoint()
operation GenerateRandomBit() : Result {
use q = Qubit();
H(q);
return MResetZ(q);
}
}

Observe que o atributo @EntryPoint informa Q # qual operação executar quando o programa é iniciado.
Enviar o trabalho
Neste exemplo, vamos usar IonQ como o provedor e o ionq.simulator como o destino. Para enviar o trabalho
para o espaço de trabalho padrão selecionado no momento, use o comando az quantum job submit :

IMPORTANT
Verifique se a versão do SDK do Quantum do *.csproj arquivo é 0.11.2006.403 ou superior. Caso contrário, isso
pode causar um erro de compilação.
az quantum job submit --target-id ionq.simulator --job-name ExampleJob -o table

Name Id Status Target Submission time


----- ------------------------------------ -------- -------------- ---------------------------------
ExampleJob yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy Waiting ionq.simulator 2020-06-
17T17:07:07.3484901+00:00

Quando o trabalho for concluído (ou seja, quando estiver em um estado bem-sucedido), ,use o comando
az quantum job output para exibir os resultados:

az quantum job output -j yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy -o table

Result Frequency
-------- ----------- -------------------------
[0,0] 0.50000000 ▐███████████ |
[0,1] 0.50000000 ▐███████████ |

A saída exibe um histograma com a frequência com que um resultado específico foi medido. No exemplo acima,
o resultado [0,1] foi observado 50% dos horários.
Opcionalmente, é possível usar o comando az quantum execute como um atalho para enviar e retornar os
resultados de uma execução.

az quantum execute --target-id ionq.simulator --job-name ExampleJob2 -o table


.....
Result Frequency
-------- ----------- -------------------------
[0,0] 0.50000000 ▐███████████ |
[0,1] 0.50000000 ▐███████████ |

Observe que o simulador IonQ fornece as probabilidades de obter cada saída se executarmos o algoritmo um
número infinito de vezes. Nesse caso, vemos que cada estado tem uma probabilidade de 50% de ser medido.

TIP
Você também pode verificar o status dos seus trabalhos no portal do Azure.

Próximas etapas
Agora que você sabe como enviar trabalhos para o Azure Quantum, você pode tentar executar os diferentes
exemplos que disponibilizamos ou tentamos enviar seus próprios projetos.
Enviar trabalhos para o Azure Quantum com o Q#
Jupyter notebooks
01/05/2021 • 2 minutes to read

Este documento fornece um guia básico para enviar e executar aplicativos do Q# no Azure Quantum usando o
Q# Jupyter notebooks.

Pré-requisitos
Um espaço de trabalho do Azure Quantum em sua assinatura do Azure. Para criar um espaço de trabalho,
consulte Criar um espaço de trabalho no Azure Quantum.

Instalação
Siga estas etapas para instalar o Jupyter Notebook e a versão atual do fernel IQ#, que capacita as experiências
do Jupyter Notebook Q# e Python.
1. Instale o Miniconda ou o Anaconda.
2. Abra um prompt do Anaconda.
Ou, se preferir usar o PowerShell ou o pwsh: abra um shell, execute conda init powershell e feche-o e
abra-o novamente.
3. Crie e ative um ambiente de conda chamado qsharp-env com os pacotes necessários (incluindo o
Jupyter Notebook e o IQ#) executando os seguintes comandos:

conda create -n qsharp-env -c quantum-engineering qsharp notebook

conda activate qsharp-env

4. Execute python -c "import qsharp" no mesmo terminal para confirmar a instalação e preencher o cache
do pacote local com todos os componentes necessários do QDK.
Agora você está configurado para usar o Q# Jupyter notebooks e integração Q# para executar programas
quânticos no Azure Quantum.

Computação quântica com o Q# Jupyter notebooks


1. Execute jupyter notebook a partir do terminal onde seu ambiente Conda está ativado. Isso inicia o
servidor do bloco de anotações e abre o Jupyter em um navegador.
2. Crie seu notebook Q# (por meio de New → Q# ) e escreva seu programa Q#.
3. Se você nunca usou Q# com Jupyter, siga as etapas em Criar seu primeiro Notebook Q#.
4. Escreva suas operações do Q# diretamente no bloco de anotações. A execução das células compilará o
código de Q# e relatará se há erros.
Por exemplo, é possível escrever uma operação Q# parecida com esta:
operation GenerateRandomBit() : Result {
use q = Qubit();
H(q);
let r = M(q);
Reset(q);
return r;
}

5. Depois de definir as operações de Q#, use os comandos mágicos %azure.* para se conectar e enviar
trabalhos para o Azure Quantum. Você usará o ID de recurso do seu espaço de trabalho do Azure
Quantum para se conectar. (O ID do recurso pode ser encontrada em sua página do espaço de trabalho
no portal do Azure.)
Se o seu espaço de trabalho foi criado em uma região do Azure diferente do " oeste dos EUA ", você
também precisará especificá-lo como o location parâmetro para %azure.connect .
Por exemplo, os seguintes comandos se conectarão a um espaço de trabalho do Azure Quantum e
executarão uma operação no ionq.simulator destino:

%azure.connect "/subscriptions/.../Microsoft.Quantum/Workspaces/WORKSPACE_NAME" location="West


US"

%azure.target ionq.simulator

%azure.execute GenerateRandomBit

em que GenerateRandomBit é a operação Q# que você já definiu no bloco de anotações.


6. Depois de enviar um trabalho, é possível verificar seu status com o comando %azure.status ou exibir
seus resultados com o comando %azure.output . É possível exibir uma lista de todos os seus trabalhos
com o comando %azure.jobs .
Algumas dicas úteis ao usar o Q# Jupyter Notebooks:
Use o comando %lsmagic para ver todos os comandos mágicos disponíveis, incluindo aqueles para o
Azure Quantum.
Informações detalhadas de uso de qualquer comando mágico podem ser exibidas simplesmente
acrescentando um ? ao comando, por exemplo, %azure.connect? .
A documentação para comandos mágicos também está disponível online: %azure.connect, %azure.target,
%azure.submit, %azure.execute, %azure.status, %azure.output, %azure.jobs

Próximas etapas
Agora que você sabe como enviar trabalhos para o Azure Quantum, você pode tentar executar os diferentes
exemplos que disponibilizamos ou tentamos enviar seus próprios projetos. Em particular, você pode exibir um
exemplo escrito inteiramente em um notebook Q# Jupyter.
Enviar trabalhos para o Azure Quantum com o
Python
01/05/2021 • 3 minutes to read

Este documento fornece um guia básico para enviar e executar aplicativos do Q# no Azure Quantum usando o
Python.

Pré-requisitos
Um espaço de trabalho do Azure Quantum em sua assinatura do Azure. Para criar um espaço de trabalho,
consulte Criar um espaço de trabalho no Azure Quantum.

Instalação
Siga estas etapas para instalar o Jupyter Notebook e a versão atual do fernel IQ#, que capacita as experiências
do Jupyter Notebook Q# e Python.
1. Instale o Miniconda ou o Anaconda.
2. Abra um prompt do Anaconda.
Ou, se preferir usar o PowerShell ou o pwsh: abra um shell, execute conda init powershell e feche-o e
abra-o novamente.
3. Crie e ative um ambiente de conda chamado qsharp-env com os pacotes necessários (incluindo o
Jupyter Notebook e o IQ#) executando os seguintes comandos:

conda create -n qsharp-env -c quantum-engineering qsharp notebook

conda activate qsharp-env

4. Execute python -c "import qsharp" no mesmo terminal para confirmar a instalação e preencher o cache
do pacote local com todos os componentes necessários do QDK.
Agora você está configurado para usar a integração Python e Q# para executar programas quânticos no Azure
Quantum.

Computação quântica com Q# e Python


1. O ambiente do Python no ambiente Conda que você criou anteriormente já inclui o qsharp pacote do
Python. Verifique se você está executando o script Python de um terminal no qual esse ambiente Conda
está ativado.
2. Se você nunca usou o Q# com Python, siga as etapas em Criar seu primeiro programa Q# com Python.
3. Grave as operações de Q# em um arquivo *.qs . Durante a execução import qsharp em Python, o kernel
IQ # detectará automaticamente todos os arquivos .qs na mesma pasta, os compilará e relatará quaisquer
erros. Se a compilação for bem-sucedida, essas operações Q# ficarão disponíveis para uso diretamente
de dentro do Python.
Por exemplo, o conteúdo do seu arquivo .qs poderia ser semelhante a este:
namespace Test {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Canon;

operation GenerateRandomBits(n : Int) : Result[] {


use qubits = Qubit[n];
ApplyToEach(H, qubits);
return MultiM(qubits);
}
}

4. Crie um script Python na mesma pasta que o arquivo *.qs . A funcionalidade do Azure Quantum está
disponível executando import qsharp.azure e, em seguida, chamando os comandos do Python para
interagir com o Azure Quantum. Para referência, consulte a Lista completa de qsharp.azure comandos
do Python. Você precisará do ID de recurso do seu espaço de trabalho do Azure Quantum para se
conectar. (O ID do recurso pode ser encontrada em sua página do espaço de trabalho no portal do Azure.)
Se o seu espaço de trabalho foi criado em uma região do Azure diferente do " oeste dos EUA ", você
também precisará especificá-lo como o location parâmetro para qsharp.azure.connect() .
Por exemplo, o seu script Python pode ser parecido com isto:

import qsharp
import qsharp.azure
from Test import GenerateRandomBits

qsharp.azure.connect(
resourceId="/subscriptions/.../Microsoft.Quantum/Workspaces/WORKSPACE_NAME",
location="West US")
qsharp.azure.target("ionq.simulator")
result = qsharp.azure.execute(GenerateRandomBits, n=3, shots=1000, jobName="Generate three random
bits")
print(result)

em que GenerateRandomBits é a operação Q# em um espaço nominal Test definido em seu *.qs


arquivo, n=3 é o parâmetro a ser passado para essa operação, shots=1000 (opcional) especifica o
número de repetições a serem executadas e jobName="Generate three random bits" (opcional) é um nome
de trabalho personalizado para identificar o trabalho no espaço de trabalho do Azure Quantum.
5. Execute o script Python executando o comando python test.py , em que test.py é o nome do seu
arquivo Python. Se for bem-sucedido, você deverá ver os resultados do trabalho exibidos para o terminal.
Por exemplo:

{'[0,0,0]': 0.125, '[1,0,0]': 0.125, '[0,1,0]': 0.125, '[1,1,0]': 0.125, '[0,0,1]': 0.125, '[1,0,1]':
0.125, '[0,1,1]': 0.125, '[1,1,1]': 0.125}

6. Para exibir os detalhes de todos os trabalhos em seu espaço de trabalho do Azure Quantum, execute o
comando qsharp.azure.jobs() :
>>> qsharp.azure.jobs()
[{'id': 'f4781db6-c41b-4402-8d7c-5cfce7f3cde4', 'name': 'GenerateRandomNumber 3 qubits', 'status':
'Succeeded', 'provider': 'ionq', 'target': 'ionq.simulator', 'creation_time': '2020-07-
17T21:45:43.4405253Z', 'begin_execution_time': '2020-07-17T21:45:54.09Z', 'end_execution_time':
'2020-07-17T21:45:54.101Z'}, {'id': '1b03cc74-b5d5-4ffa-81db-465f08ae6cd0', 'name':
'GenerateRandomBit', 'status': 'Succeeded', 'provider': 'ionq', 'target': 'ionq.simulator',
'creation_time': '2020-07-21T19:44:17.1065156Z', 'begin_execution_time': '2020-07-21T19:44:25.85Z',
'end_execution_time': '2020-07-21T19:44:25.858Z'}]

7. Para exibir o status detalhado de um trabalho específico, passe o ID do trabalho para


qsharp.azure.status() ou qsharp.azure.output() , por exemplo:

>>> qsharp.azure.status('1b03cc74-b5d5-4ffa-81db-465f08ae6cd0')
{'id': '1b03cc74-b5d5-4ffa-81db-465f08ae6cd0', 'name': 'GenerateRandomBit', 'status': 'Succeeded',
'provider': 'ionq', 'target': 'ionq.simulator',
'creation_time': '2020-07-21T19:44:17.1065156Z', 'begin_execution_time': '2020-07-21T19:44:25.85Z',
'end_execution_time': '2020-07-21T19:44:25.858Z'}

>>> qsharp.azure.output('1b03cc74-b5d5-4ffa-81db-465f08ae6cd0')
{'0': 0.5, '1': 0.5}

Próximas etapas
Agora que você sabe como enviar trabalhos para o Azure Quantum, você pode tentar executar os diferentes
exemplos que disponibilizamos ou tentamos enviar seus próprios projetos.
Instalar e usar o SDK do Python para otimização
18/05/2021 • 3 minutes to read

Este guia fornece uma visão geral básica de como instalar e usar o SDK do Python para otimização.

Pré-requisitos
Um espaço de trabalho do Azure Quantum criado em sua assinatura do Azure. Para criar um espaço de
trabalho, consulte Criar um espaço de trabalho no Azure Quantum.

Instalação do SDK do Python para otimização


O SDK do Python é distribuído como o pacote azure-quantum PyPI. Para instalar o pacote, você precisará seguir
as etapas abaixo:
1. Instale o Python 3.6 ou mais recente.
2. Instale o Pip, o instalador de pacote do Python e verifique se você tem a versão 19.2 ou superior
3. Instalar o pacote azure-quantum do Python:

pip install azure-quantum

Instalação de Jupyter Notebooks


Você também pode optar por interagir com a otimização do Azure Quantum usando Jupyter Notebooks. Para
isso, você precisará:
1. Instalar o SDK do Python para otimização (conforme descrito na seção anterior)
2. Instalar Jupyter Notebooks
3. No terminal de sua escolha, use o seguinte comando para iniciar um novo Jupyter Notebook:

jupyter notebook

Isso abrirá uma nova janela do navegador (ou uma nova guia) mostrando o Painel do Notebook, um tipo
de painel de controle que permite que você (entre outras coisas) selecione qual bloco de anotações deve
ser aberto.
4. No modo de exibição de navegador, selecione o botão suspenso no canto superior direito e selecione
Python 3 na lista. Isso deve criar um novo notebook.

Exemplo de uso
Se optar por resolver problemas de otimização usando Jupyter Notebooks ou um script Python, depois de
instalar os pré-requisitos das seções anteriores, você poderá seguir as instruções abaixo para executar um
problema de teste.

Conectar-se a um espaço de trabalho do Azure Quantum


Um Workspace representa o espaço de trabalho do Azure Quantum que você criou anteriormente e é a interface
principal para interagir com o serviço.

from typing import List


from azure.quantum.optimization import Term
from azure.quantum import Workspace

workspace = Workspace (
subscription_id = "", # Add your subscription_id
resource_group = "", # Add your resource_group
name = "", # Add your workspace name
location = "" # Add your workspace location (for example, "westus")
)

workspace.login()

Ao executar o comando workspace.login() pela primeira vez, você verá o seguinte exibido no console:

To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code [RANDOM-
CODE] to authenticate.

Depois de entrar em sua conta do Azure, suas credenciais serão armazenadas em cache para que você não
precise repetir esse processo em execuções futuras.

Expressar e resolver um problema simples


Para apresentar um problema simples a ser resolvido, crie uma instância de em Problem e defina problem_type
como ProblemType.ising ou ProblemType.pubo . Para obter mais informações, confira ProblemType .

from azure.quantum.optimization import Problem, ProblemType, Term, ParallelTempering

problem = Problem(name="My First Problem", problem_type=ProblemType.ising)

Em seguida, crie uma matriz de objetos Term e os adicione ao Problem :

terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0]),
Term(c=9, indices=[2,1]),
Term(c=2, indices=[3,0]),
Term(c=-4, indices=[3,1]),
Term(c=4, indices=[3,2])
]

problem.add_terms(terms=terms)

NOTE
Há várias maneiras de fornecer termos para o problema, e nem todos os termos devem ser adicionados ao mesmo
tempo.

Em seguida, estamos prontos para aplicar um solucionador . Neste exemplo, usaremos uma versão sem
parâmetros da deformação paralela. Você pode encontrar a documentação sobre esse solucionador e os outros
solucionadores disponíveis na Referência do provedor QIO da Microsoft.
solver = ParallelTempering(workspace, timeout=100)

result = solver.optimize(problem)
print(result)

Esse método enviará o problema para o Azure Quantum para otimização e esperará que ele seja resolvido de
forma síncrona. Você verá uma saída semelhante à seguinte na janela do seu terminal ou Jupyter Notebook:

{'configuration': {'0': 1, '1': 1, '2': -1, '3': 1}, 'cost': -32.0}

Próximas etapas
Documentação
Visão geral do solucionador
Expressando problemas e fornecendo termos
Interpretando resultados do solucionador
Gerenciamento de trabalho
Solucionar problemas de longa execução (envio de problema assíncrono)
Reutilizar as definições de problema
Autenticação com uma entidade de serviço
Referência de solucionadores para solucionadores de QIO da Microsoft
Exemplos e aprendizagem de ponta a ponta
Repositório de exemplos de QIO
Introdução
1QBit
QIO da Microsoft
Enviar exemplo de problema de carregamento
Módulo do Microsoft Learn de ponta a ponta
Código de exemplo
Problema de exemplo de agendamento da loja de trabalhos
Módulo do Microsoft Learn de ponta a ponta
Código de exemplo
Solucionando problemas de erros do usuário em
solucionadores de otimização
30/04/2021 • 6 minutes to read

Este documento lista os erros de usuário comuns retornados pelos solucionadores de otimização do Azure
Quantum e as etapas de solução de problemas para cada erro. As mensagens de erro retornadas pelos
solucionadores devem ter um código de erro (no formato _<code>) anexado que o usuário deve usar para
encontrar seu problema específico. O código é prefixado com "AZQ".

Recursos insuficientes (intervalo de 001–100)


Os erros nessa categoria são devido à falta de recursos para executar um trabalho específico. Isso pode ser
causado por fatores como o tamanho do problema ou as configurações de parâmetro.
AZQ001 – Memória limitada
Causa : esse erro ocorre quando o problema enviado é muito grande (geralmente, devido a muitos termos) e
pode não caber na memória. Os usuários podem estimar a quantidade de memória que seu problema usará
com a seguinte fórmula (embora não seja 100% precisa, está perto do uso real):

memory_bytes = sum_coefficient_degrees_total*num_variables/8

Where
num_variables é o número de variáveis no problema
sum_coefficient_degrees_total é a soma total do número de variáveis em cada termo

Um exemplo com esta formulação:

"terms": [
{
"c": 1.0,
"ids": [0, 1, 2]
},
{
"c": 1.0,
"ids": [2, 3, 4]
}
]

Aqui num_variables = 5 (${0,1,2,3,4}$) e sum_coefficient_degrees_total = 6 ($3 + 3$). O total de bytes de


memória estimada é $6*5/8 = 3.75 \text{bytes}$.
Possíveis ações a serem tomadas : esse erro é difícil de "corrigir", pois alguns problemas terão uma grande
quantidade de expressões de termo expandidas, principalmente problemas de ordem mais alta. Se você vir esse
erro mais provavelmente neste momento, nossos solucionadores não conseguirão resolver o problema. No
entanto, o Azure Quantum está desenvolvendo continuamente o suporte para expressões de termo mais
complexas que podem reduzir o tamanho do problema no futuro. Lembre-se de comunicar a necessidade desse
recurso à nossa equipe de suporte.
Enquanto isso, considere:
Remover os termos constantes (por exemplo, termos sem variáveis, termos de Ising com capacidade variável
par etc.)
AZQ002 – Tempo limite insuficiente
Causa : esse erro ocorre ao usar os solucionadores livres de parâmetros especificamente. Isso significa que o
parâmetro "timeout" (em segundos) está definido muito baixo para qualquer exploração significativa. Cada
solucionador tem um processo de pesquisa diferente, e alguns solucionadores demorarão mais do que outros.
A tabela a seguir mostra o tempo limite mínimo necessário para um tamanho de problema específico obter um
resultado (observação: não necessariamente um resultado bom). Com base no tamanho do problema, você
pode ajustar os números de acordo.

P RO B L EM A REC O Z IM EN TO SIM UL A DO DEF O RM A Ç Ã O PA RA L EL A P ESQ UISA TA B U

Variáveis: 1024, termos: 5s 100 s 1s


195k

Possíveis ações a serem tomadas :


Aumente a duração do tempo limite. Isso dependerá do solucionador que está sendo chamado (confira a
tabela acima para ver o ponto de partida).
Se o problema for particularmente grande (mais de 10 mil variáveis) e/ou com muitos termos, um tempo
limite maior poderá ser necessário.

Dados de entrada inválidos (intervalo de 101–200)


Erros nessa categoria são devido a erros nas entradas do usuário. Houve um problema com a expressão de
função de custo ou os parâmetros são inválidos.
AZQ101 – Variável duplicada
Causa : esse erro ocorre ao usar a função de custo Ising. Os solucionadores do Azure Quantum só aceitarão
variáveis de um único grau, portanto, se o usuário estiver enviando variáveis de potência mais altas, um erro
será gerado.
Possíveis ações a serem tomadas :
Condense todas as variáveis de potência mais altas em uma única variável ou uma constante (1). Veja o
seguinte exemplo:

"terms": [
{
"c": 1.0,
"ids": [0, 0, 0, 1, 1]
},
{
"c": 1.0,
"ids": [2, 2]
}
]

Se PUBO/HOBO, devem ser condensadas para:


"terms": [
{
"c": 1.0,
"ids": [0, 1]
},
{
"c": 1.0,
"ids": [2]
}
]

Se Ising, os termos com capacidade par são 1, portanto, devem ser condensadas para:

"terms": [
{
"c": 1.0,
"ids": [0]
},
{
"c": 1.0,
"ids": []
}
]

AZQ102 – Seções ausentes nos dados de entrada


Causa : esse erro ocorre quando há campos ausentes nos dados de entrada. Isso geralmente ocorre apenas ao
enviar para a API diretamente, não por meio do SDK. O SDK formata automaticamente os termos enviados na
estrutura correta.
O solucionador normalmente retorna um erro mais específico com os campos ausentes na entrada.
Possíveis ações a serem tomadas :
Examine a mensagem de erro específica e determine qual campo está ausente na entrada. Verifique se o
campo está presente e se tem um valor do tipo correto.
Verifique se todos os campos estão presentes e se têm valores na expressão de entrada. Evite cadeias de
caracteres vazias "" e valores nulos .

{
"cost_function": {
"type": "pubo",
"version": "1.0",
"terms": [{
"c": 1.0,
"ids": [0, 1]
}, {
"c": 0.36,
"ids": [1, 2]
}]
}
}

AZQ103 – Tipos inválidos em dados de entrada


Causa : esse erro ocorre quando há campos nos dados com tipos inválidos. Para obter mais informações, confira
Como expressar os termos de problema corretamente. A tabela a seguir mostra os tipos esperados para cada
campo.
N O M E DO C A M P O T IP O ESP ERA DO ERRO S C O M UN S

c double Enviando esse campo na forma de


cadeia de caracteres, por exemplo,
"2,0" ou em formato nulo.

índices lista de números inteiros Enviando esse campo na forma de


cadeia de caracteres "[0,1]" ou itens
individuais como formato de cadeia de
caracteres ["0", "1"]. São permitidas
listas vazias e serão tratadas como
constantes.

Isso também pode acontecer nos parâmetros fornecidos para o solucionador.


Possíveis ações a serem tomadas :
Todos os tipos devem ser convertidos corretamente ao usar o SDK, principalmente se você estiver analisando
esses valores de cadeias de caracteres ou arquivos existentes.
Verifique se os parâmetros que são listas não estão sendo fornecidos como cadeias de caracteres de listas,
por exemplo [0,1] em vez de "[0,1]" ou ["0", "1"]
AZQ104 – Erro de configuração inicial
Causa : esse é um grupo de erros relacionados ao uso da definição de configuração inicial. A mensagem de erro
retornada pelo solucionador deve conter a mensagem específica. As possíveis causas podem incluir:
Os valores de variáveis são inválidos e não correspondem ao tipo de problema fornecido (Ising/PUBO). Por
exemplo, esse erro será exibido se o tipo de problema original for Ising e as variáveis de configuração iniciais
forem encontradas com valores fora de 1 e-1.
As dimensões variáveis fornecidas na configuração inicial não correspondem ao tamanho da variável do
problema original.
Possíveis ações a serem tomadas :
Verifique se as definições de configuração iniciais para as variáveis são válidas e usam apenas 2 valores
[(0|1) ou (-1|1)].
Verifique se as variáveis no mapa de configuração fazem parte do problema inicial e se você não incluiu
nenhuma nova ID de variável.
AZQ105 – Não foi possível analisar a entrada
Causa : esse erro ocorre quando o solucionador não consegue analisar os dados de entrada. Isso geralmente
ocorre apenas ao enviar para a API diretamente, não por meio do SDK. O SDK formata automaticamente os
termos enviados na estrutura json.
Isso acontece quando há um erro de sintaxe no json de dados de entrada ou no arquivo de parâmetro json.
Possíveis ações a serem tomadas :
Use um lint json ou ferramenta formatadora no json inválido e corrija os erros de sintaxe.
Formular problemas com o SDK (que envia automaticamente os problemas na forma correta)
AZQ106 – Erro de opção de recurso
Causa : esse erro ocorre quando a funcionalidade da opção de recurso é usada e uma ID de recurso inválida é
fornecida.
Possíveis ações a serem tomadas :
Confira a documentação e desabilite apenas as IDs de recurso compatível.
AZQ107 – Valores inválidos na entrada
Causa : esse erro ocorre quando há valores proibidos ou inválidos sendo inseridos. Isso ocorre principalmente
se os parâmetros definidos para os solucionadores não forem válidos.
Possíveis ações a serem tomadas :
Examine a mensagem de erro específica e determine qual campo é inválido e o intervalo de valores
permitidos para esse campo.
Execute a versão do solucionador sem parâmetros para obter um ponto de partida para os valores de
parâmetro.
A maioria dos parâmetros requer um valor de >0 (por exemplo, reinicializações, réplicas, varreduras etc.).
Glossário do Azure Quantum
13/05/2021 • 2 minutes to read

Azure Active Director y (AAD)/Plataforma de Identidade da Microsoft - serviço de identidade do Azure,


usado para implementar o controle de acesso e a autenticação para os recursos. Os nomes Azure Active
Directory e Plataforma de Identidade da Microsoft são intercambiáveis. Para saber mais, consulte Azure Active
Directory.
Aplicativo Gerenciado do Azure (AMA)/Aplicativo Gerenciado - tipo de aplicativo oferecido aos clientes
finais no Azure por meio do Azure Marketplace. Para saber mais, confira Aplicativos gerenciados do Azure.
Azure Marketplace – vitrine para software baseado em nuvem no Azure. Para saber mais, consulte Azure
Marketplace.
Trabalho do Azure Quantum (Trabalho) – programa, problema ou aplicativo enviado para o Azure
Quantum para ser processado por um provedor do Azure Quantum.
Destino do Trabalho do Azure Quantum (Destino) – os provedores expõem um ou mais destinos que
podem ser usados para executar os trabalhos. Os clientes selecionam qual destino gostariam de usar para
executar um trabalho específico. Por exemplo, uma máquina de três qubits e uma máquina de seis qubits
podem ser os destinos.
Provedor do Azure Quantum (Provedor) – um provedor é um componente no Azure Quantum que fornece
a capacidade de executar trabalhos. Os provedores podem ser disponibilizados pela Microsoft ou por parceiros
terceirizados.
Azure Quantum – serviço quantum da Microsoft para o Azure, permitindo que os clientes acessem soluções
quantum tanto de provedores da Microsoft quanto de provedores de parceiros.
Azure Resource Manager (ARM) - serviço de implantação e gerenciamento do Azure. Para obter mais
informações, consulte Azure Resource Manager.
Quantum Development Kit (QDK) - kit de desenvolvimento de software da Microsoft para desenvolver
aplicativos quantum no serviço Azure Quantum. O QDK contém Q#, linguagem de programação da Microsoft
para computação quantum, juntamente com bibliotecas, exemplos e tutoriais Q#. Também contém APIs de
desenvolvedor para executar trabalhos no serviço do Azure Quantum. Para saber mais, consulte a
Documentação do QDK da Microsoft
Problema de Otimização Inspirado no Quantum (problema de QIO) - problema expresso usando nossa
biblioteca de otimização do Python para ser resolvido usando o Azure Quantum. Os problemas podem ser
expressos como PUBOs (Otimização Binária não Restringida Polinomial) ou formas Ising.
Programa Quantum - no escopo do Azure Quantum, um programa escrito em Q# que tem como alvo um
provedor no Azure Quantum.

Próximas etapas
Para obter mais informações, consulte:
Glossário do Microsoft Azure
Glossário do Quantum Development Kit
Problemas comuns do Azure Quantum
13/05/2021 • 2 minutes to read

Quando começar a trabalhar com o Azure Quantum pela primeira vez, você poderá encontrar esses problemas
comuns.

Envio de trabalhos
Problema: a operação retorna um código de status inválido 'Não autorizado'
Etapas para resolver o problema:
1. Abra o portal do Azure (https://portal.azure.com) e autentique em sua conta.
2. Em Navegar , clique em Assinaturas e selecione sua assinatura.
3. Clique em IAM (Controle de Acesso) .
4. Em Verificar acesso , procure seu endereço de email e selecione a conta.
5. Você não verá uma função de Proprietário ou Colaborador na lista.
6. Clique na guia Atribuições de função .

NOTE
Se você não vir a guia Atribuições de função , talvez seja necessário expandir o portal para tela inteira ou fechar
o painel <your name>atribuições .

7. Clique no menu suspenso Função , selecione Proprietário ou Colaborador , insira seu endereço de
email e selecione sua conta.
8. Clique em Save (Salvar).
9. Agora você deve ver seu conjunto de contas configurado com a função de proprietário ou
Colaborador .
10. Crie o workspace do Quantum novamente e envie um trabalho para esse novo workspace do Quantum.
Problema: "Falha ao compilar o programa" ao tentar enviar um programa Q# por meio da CLI
Ao tentar enviar um trabalho no prompt de comando usando o comando az quantum submit , a seguinte
mensagem de erro pode ser exibida:

> az quantum job submit ...


Failed to compile program.
Command ran in 21.181 seconds (init: 0.457, invoke: 20.724)

Esse erro ocorre quando há um problema com o programa Q# que faz com que a compilação falhe. Para ver o
erro específico que está causando a falha, execute dotnet build na mesma pasta.
Proibido: a operação retornou o código de status inválido 'Proibido'
Ao enviar seu primeiro trabalho, você pode receber um código de erro "proibido".
Esse problema pode ocorrer durante a criação de um workspace, se o Azure Quantum não conseguir concluir a
atribuição de função para vincular o novo workspace à conta de armazenamento que foi especificada. Um
cenário típico para essa situação ocorrerá se a janela da guia ou do navegador da Web for fechada antes da
conclusão da criação do workspace.
Verifique se está enfrentando esse problema de atribuição de função seguindo estas etapas:
Navegue até o novo workspace quantum no portal do Azure
Em Visão geral > Essentials > Conta de armazenamento , clique no link da conta de armazenamento
Selecione Controle de acesso (IAM) na navegação à esquerda
Selecione Atribuições de função
Verifique se seu workspace aparece como Colaborador
Se o workspace não aparecer como Colaborador , você pode:
Criar um novo workspace e lembrar-se de aguardar a conclusão da criação do workspace antes de
fechar a janela ou a guia do navegador da Web.
Adicionar a atribuição de função apropriada na conta de armazenamento
Controle de Acesso (IAM) > Adicionar atribuição de função
Função > Colaborador
Atribuir acesso a > Usuário, grupo ou entidade de serviço
Selecione > [Nome do workspace]
Salvar
Provedor IonQ
13/05/2021 • 2 minutes to read

Os computadores quantum da IonQ executam cálculos manipulando com lasers, os estados de energia hiperfina
dos íons de itérbio. Os átomos são qubits da natureza, cada qubit é idêntico dentro e entre os programas.
Operações lógicas também podem ser executadas em qualquer par arbitrário de qubits, permitindo que
programas quantum complexos não sejam prejudicados pela conectividade física. Quer saber mais? Leia a visão
geral da tecnologia de computadores quantum de íons presos da IonQ.
Editor: IonQ
ID do provedor: ionq

Destinos
O provedor IonQ da Microsoft disponibiliza os seguintes segmentos:
Provedor do IonQ
Destinos
Simulador Quantum da IonQ
Computador Quantum da IonQ

Simulador Quantum
Simulador idealizado, acelerado por GPU, com suporte para até 29 qubits, usando o mesmo conjunto de portas
que a IonQ fornece em seu hardware quantum. Uma ótima ferramenta para testar trabalhos antes de executá-
los em um computador quantum real.
Tipo de trabalho: Simulation
Formato dos dados: ionq.circuit.v1
ID de destino: ionq.simulator
Perfil Q#: No Control Flow

Computador quantum
Computadores quantum de íons presos. Reconfigurável dinamicamente no software para usar até 11 qubits.
Todos os qubits estão totalmente conectados, o que significa que você pode executar uma porta de dois qubits
entre qualquer par.
Tipo de trabalho: Quantum Program
Formato dos dados: ionq.circuit.v1
ID de destino: ionq.qpu
Perfil Q#: No Control Flow

N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

shots INT Não Número de capturas


experimentais. O valor
padrão é 500.

Tempo do sistema
M EDIDA DURA Ç Ã O M ÉDIA DO T EM P O ( Μ S)

T1 >10^7

T2 200.000

Porta de qubit único 10

Porta de dois qubit 210

Leitura 100

Redefinição de registro 25

Tempo de coerência/duração da porta 1667

Fidelidade do sistema
O P ERA Ç Ã O F IDEL IDA DE M ÉDIA

Porta de qubit único 99,35% (SPAM corrigido)

Porta de dois qubit 96,02% (sem correção de SPAM)

SPAM 99,3 - 99,8%

Operação média geométrica 98,34%

Preços
Encargos da IonQ por Disparo de por ta : número de portas em seu circuito, multiplicado pelo número de
disparos.
As portas multi-controladas de dois qubit são cobradas como portas de dois qubits (6N - 6) , em que N é o
número de qubits envolvidos na porta. Por exemplo, uma NÃO porta com três controles seria cobrada como (6
* 4 - 6) ou 8 portas de dois qubits.
Para consultar as opções de preços:
1. Vá até o Portal do Azure e crie uma nova workspace.
2. No painel Provedores , clique no botão Adicionar do bloco IonQ e, na descrição, você encontrará as opções
de preço atuais.

Práticas recomendadas e Grafos de conectividade da IonQ


Para consultar as práticas recomendadas para o QPU da IonQ, recomendamos ler suas práticas recomendadas.
Política de suporte para IonQ no Azure Quantum
19/05/2021 • 2 minutes to read

Este artigo descreve a política de suporte da Microsoft que se aplica quando você usa o provedor IonQ no Azure
Quantum. O artigo se aplica a qualquer um dos destinos desse provedor.
Se você estiver usando o provedor IonQ e encontrar qualquer problema inesperado que não consiga solucionar,
normalmente basta entrar em contato com a equipe de suporte do Azure para obter ajuda criando um caso no
Suporte do Azure.
No entanto, existem algumas situações em que a equipe do Suporte do Azure precisa redirecionar você para a
equipe de suporte da IonQ ou em que você pode receber uma resposta mais rápida se entrar em contato direto
com a IonQ. Este artigo visa fornecer mais detalhes sobre isso com base em algumas perguntas frequentes.

Perguntas frequentes
P: Qual é a política de suporte para o uso de ofertas da IonQ por meio do Azure Quantum?
A Microsoft fornecerá suporte apenas para a Plataforma Azure e para o serviço do Azure Quantum. O suporte
para hardware, simuladores e outros produtos e destinos da IonQ será fornecido diretamente pela IonQ. Para
saber mais sobre o Suporte do Azure, consulte Planos de Suporte do Azure. Para saber mais sobre o suporte da
IonQ, visite o site do suporte da IonQ.
P: O que acontecerá se eu gerar um problema de suporte com a equipe de Suporte do Azure e um provedor de terceiros (como a
IonQ) precisar realizar mais etapas de solução de problemas?
O engenheiro do suporte criará um pacote de redirecionamento para você. Trata-se de um documento PDF que
contém informações sobre seu caso, que você pode apresentar à equipe de suporte da IonQ. O engenheiro de
suporte também fornecerá conselhos e diretrizes sobre como entrar em contato com a IonQ para continuar a
solução de problemas.
P: O que acontecerá se eu gerar um problema de suporte com a equipe da IonQ e descobrir que é um problema com o serviço do
Azure Quantum?
A equipe de suporte da IonQ ajudará você a entrar em contato com a Microsoft e fornecerá um pacote de
redirecionamento. Trata-se de um documento PDF que você pode usar ao continuar seu chamado de suporte
com a equipe do Suporte do Azure.
Aviso de isenção de responsabilidade de informações de terceiros
Os produtos de terceiros mencionados neste artigo são fabricados por empresas que são independentes da
Microsoft. A Microsoft não oferece nenhuma garantia, implícita ou não, sobre o desempenho ou a confiabilidade
desses produtos.
Aviso de isenção de responsabilidade de contato de terceiros
A Microsoft fornece informações de contato de terceiros para ajudá-lo a encontrar informações adicionais sobre
este tópico. Essas informações de contato podem ser alteradas sem aviso prévio. A Microsoft não garante a
precisão das informações de contato de terceiros.
Provedor da Honeywell
28/04/2021 • 2 minutes to read

Publicador: Honeywell
ID do provedor: honeywell

Destinos
Os seguintes destinos estão disponíveis neste provedor:
Validador de API
Modelo do sistema H0 da Honeywell
Modelo do sistema H1 da Honeywell
Validador de API
Ferramenta para verificar a sintaxe adequada e a conclusão da compilação. A pilha completa é exercitada com
exceção das operações quânticas reais. Supondo que não haja bugs, todos os zeros são retornados na estrutura
de dados apropriada.
Tipo de trabalho: Simulation
Formato de dados: honeywell.openqasm.v1
ID de destino: honeywell.hqs-lt-1.0-apival
Perfil de execução de destino: Comentários sobre medidas básicas
Informações de cobrança: sem custos para uso.
Modelo do sistema H0 da Honeywell
Computador quântico da Honeywell Quantum Solutions, modelo do sistema H0
Tipo de trabalho: Quantum Program
Formato de dados: honeywell.openqasm.v1
ID de destino: honeywell.hqs-lt-1.0
Perfil de execução de destino: Comentários sobre medidas básicas
Informações de cobrança:
Preço determinado pelos créditos quânticos da Honeywell (veja abaixo).
Valor por hora para o tempo reservado (uso exclusivo).

N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

count INT Não Número de capturas


experimentais. O valor
padrão é 1.

options list Não Opções do compilador.


Somente uma opção com
suporte no momento:
no-opt para desabilitar
toda a otimização
A equação a seguir define como os circuitos são convertidos em HQCs (créditos quânticos da Honeywell):
$$ HQC = 5 + C(N_{1q} + 10 N_{2q} + 5 N_m)/5000 $$
em que:
$N_{1Q}$ é o número de operações de um qubit em um circuito.
$N_{2Q}$ é o número de operações nativas de dois qubits em um circuito. O portão nativo é equivalente a
CNOT até vários portões de um qubit.
$N_{m}$ é o número de operações de preparação e medição de estado (SPAM) em um circuito, incluindo a
preparação de estado implícita inicial e quaisquer medidas intermediárias e finais e redefinições de estado.
$C$ é a contagem de captura.
Especificações técnicas
Computador quântico baseado em íons presos com portões baseados em laser
Arquitetura QCCD com interceptação linear e duas zonas de operação paralelas
6 qubits físicos, totalmente conectados
Fidelidade limitante típica >99,2% (fidelidade de dois qubits)
Tempo de coerência (T2) ~2 s
Capacidade de executar a medição de meio-circuito e a reutilização de qubit
Rotações de alta resolução (> $\pi$/500)
Conjunto de portão nativo:
rotações de qubit único
portões ZZ de dois qubits
Modelo do sistema H1 da Honeywell
Computador quântico da Honeywell Quantum Solutions, modelo do sistema H1
Tipo de trabalho: Quantum Program
Formato de dados: honeywell.openqasm.v1
ID de destino: honeywell.hqs-lt-s1
Perfil de execução de destino: Comentários sobre medidas básicas
Informações de cobrança:
Assinatura padrão:
Plano de assinatura com duas sessões de 4 horas de acesso dedicado, com acesso irrestrito e em fila no
restante do mês, com base na disponibilidade do sistema.
Assinatura premium:
Plano de assinatura com quatro sessões de 4 horas de acesso dedicado, com acesso irrestrito e em fila no
restante do mês, com base na disponibilidade do sistema.
Especificações técnicas
Computador quântico baseado em íons presos com portões baseados em laser
Arquitetura QCCD com interceptação linear e três zonas de operação paralelas
10 qubits físicos, totalmente conectados
Fidelidade limitante típica >99,5% (fidelidade de dois qubits)
Tempo de coerência (T2) ~3 s
Capacidade de executar a medição de meio-circuito e a reutilização de qubit
Rotações de alta resolução (> $\pi$/500)
Conjunto de portão nativo:
rotações de qubit único
portões ZZ de dois qubits
Mais detalhes estão disponíveis com um NDA.
Política de suporte para Honeywell no Azure
Quantum
28/04/2021 • 5 minutes to read

Este artigo descreve a política de suporte da Microsoft que se aplica ao uso do provedor da Honeywell no Azure
Quantum. O artigo se aplica a qualquer um dos destinos desse provedor.
Se você estiver usando o provedor da Honeywell e encontrar qualquer problema inesperado que não possa
solucionar você mesmo, normalmente basta entrar em contato com a equipe de suporte do Azure para obter
ajuda criando um caso de suporte do Azure.
No entanto, há algumas situações em que a equipe de Suporte do Azure precisará redirecioná-lo para a equipe
de suporte da Honeywell ou em que você pode receber uma resposta mais rápida entrando em contato com a
Honeywell diretamente. Este artigo visa fornecer mais detalhes sobre isso com base em algumas perguntas
frequentes.

Política de suporte da Honeywell – visualização pública


O objetivo do Suporte é identificar e solucionar defeitos ou mau funcionamento, fazendo com que o
Computador Quântico não funcione de acordo com as especificações e a documentação acordadas
("Problemas"). O suporte só aborda a versão atual lançada disponível para os clientes. Embora nossos
especialistas quânticos possam ajudá-lo a solucionar problemas de algoritmo, não somos responsáveis por
problemas ou defeitos com seu algoritmo. Para minimizar problemas de programação, os usuários podem
executar seus algoritmos por meio do validador de sintaxe antes de serem executados em hardware.
1.1 ENTRANDO EM CONTATO COM O SUPORTE TÉCNICO NA HONEYWELL QUANTUM SOLUTIONS
Para criar uma solicitação de suporte com a Honeywell Quantum Solutions, envie um relatório de incidente por
email para HoneywellAzureQuantumSupport@Honeywell.com. O engenheiro de plantão responderá dentro do
SLA apropriado (Tabela 2). Observe que somente os relatórios de incidentes que contêm todas as informações
abaixo notificarão a equipe da Honeywell Quantum Solutions e terão a janela de resolução do SLA apropriado.
Os relatórios sem as informações necessárias não dispararão essa resposta. Todas as solicitações devem ser
fornecidas no idioma inglês e serão respondidas no idioma inglês.
Tabela 1: informações de contato do suporte e horário de funcionamento padrão | Email | Horário de
funcionamento padrão | | - | - | | HoneywellAzureQuantumSupport@Honeywell.com | 8:00 – 17:00 MST nos dias
úteis1 |
1 "Dias úteis" significa segunda a sexta-feira, excluindo feriados nacionais, legais ou bancários.

O relatório de incidentes exigirá as seguintes informações:


Descrição: uma descrição detalhada do problema que você está enfrentando, o que você está observando e
as etapas que você já realizou para fazer a triagem/entender o problema
Contato principal: um nome de contato primário e um número de telefone, caso precisemos de mais
informações
Gravidade do incidente: a gravidade do incidente, de acordo com nossas definições de gravidade
Depois que um relatório for apresentado, a equipe de soluções da Honeywell Quantum Solutions será notificada
e responderá na janela de tempo do SLA necessária. Você receberá uma confirmação do problema depois que
ele tiver sido recebido e o engenheiro de plantão tiver sido notificado. O engenheiro de plantão pode solicitar
mais informações ou pode entrar em contato com você pelo número de contato listado no relatório,
dependendo da gravidade.
Durante sessões reservadas (dedicadas), a Honeywell Quantum Solutions disponibilizará pelo menos um
especialista quântico para você, para o atendimento ao cliente durante todo o período da sessão dedicada. A
Honeywell reserva o direito de alterar o especialista durante sua sessão, mas se esforçará, dentro do que for
comercialmente possível, para garantir que o novo especialista esteja preparado para continuar fornecendo
suporte.
Durante as Execuções em fila, se o trabalho estiver em execução na fila geral e você encontrar um problema,
você poderá enviar sua solicitação de problema online em: HoneywellAzureQuantumSupport@Honeywell.com.
Você pode esperar receber uma resposta em três (3) dias úteis.
1.2 ÍNDICE DE GRAVIDADE DA HONEYWELL QUANTUM SOLUTIONS E SLA DE RESPOSTA
Se o engenheiro da Honeywell Quantum Solutions determinar que o problema não está dentro dos limites de
nossos compromissos de resposta a incidentes ou não tiver a classificação de gravidade correta, ele poderá
optar, a critério próprio, por ajustar a gravidade e/ou finalizar o suporte se a nova gravidade prescindi-lo.
Alguns problemas podem ser mais fáceis de lidar do que outros, e talvez não seja possível resolver
completamente o problema com nossa resposta inicial. Se não for possível resolver o problema de forma
razoável, faremos um esforço de boa fé para oferecer uma avaliação do problema e um tempo estimado para a
resolução.
Tabela 2: definição do índice de gravidade

SEVERIDA DE SIGN IF IC A DO DESC RIÇ Ã O

Gravidade 1 Impacto significativo Desempenho do sistema degradado


abaixo do nível mínimo aceito

Gravidade 2 Impacto urgente ou alto O status do trabalho não foi


atualizado; problemas ao recuperar
dados

Gravidade 3 Não urgente Diversos

A tabela a seguir mostra o SLA de resposta que será cumprido pela Honeywell Quantum Solutions em
relatórios de incidentes da gravidade correspondente. Observe que definem a rapidez com que responderemos
a problemas, os tempos de resolução não são garantidos.
Tabela 3: SLA de resposta para diversos níveis de gravidade | Severidade | SLA de resposta | | - | - | | Gravidade 1
| Resposta dentro de uma hora no horário comercial | | Gravidade 2 | Resposta dentro de um dia útil no horário
comercial | | Gravidade 3 | Resposta dentro de três dias úteis no horário comercial |

Perguntas frequentes
P: o que acontece se eu gerar um chamado de suporte com a equipe de suporte do Azure e, na verdade, um provedor terceirizado
(como a Honeywell) é que precisará solucionar o problema?
O engenheiro de suporte criará um pacote de redirecionamento para você. Trata-se de um documento PDF que
contém informações sobre seu caso, que você pode apresentar à equipe de suporte da Honeywell. O
engenheiro de suporte também fornecerá conselhos e orientações sobre como entrar em contato com a
Honeywell para continuar a solução de problemas.
P: o que acontece se eu gerar um chamado de suporte com a equipe da Honeywell e, na verdade, há um problema com o serviço
do Azure Quantum?
A equipe de suporte da Honeywell irá ajudá-lo a entrar em contato com a Microsoft e fornecerá um pacote de
redirecionamento. Trata-se de um documento PDF que você pode usar ao continuar seu chamado de suporte
com a equipe de suporte do Azure.
Aviso de isenção de responsabilidade de informações de terceiros
Os produtos de terceiros mencionados neste artigo são fabricados por empresas que são independentes da
Microsoft. A Microsoft não oferece nenhuma garantia, implícita ou não, sobre o desempenho ou a confiabilidade
desses produtos.
Aviso de isenção de responsabilidade de contato de terceiros
A Microsoft fornece informações de contato de terceiros para ajudá-lo a encontrar informações adicionais sobre
este tópico. Essas informações de contato podem ser alteradas sem aviso prévio. A Microsoft não garante a
precisão das informações de contato de terceiros.
Provedor da 1QBit
01/05/2021 • 5 minutes to read

Editor: 1QBit
ID do provedor: 1qbit

Destinos
solucionador de pesquisa Tabu
Um algoritmo heurístico iterativo que usa técnicas de pesquisa local para resolver um problema de QUBO. Ele
começa com uma solução aleatória e procura uma solução aprimorada no ambiente da solução, que inclui todas
as inversões únicas possíveis. O algoritmo para quando atinge um critério de interrupção, como um número
especificado de iterações consecutivas sem melhoria.

Observe os valores dos parâmetros, é necessário que sejam do tipo cadeia de caracteres JSON. Os tipos listados
abaixo são os tipos que os solucionadores esperam dentro do valor da cadeia de caracteres. Por exemplo, '
improvement_cutoff ' está listado como tipo int, espera-se que ele seja passado no formato:
improvement_cutoff: "5"

Para obter mais informações, acesse: https://portal.1qbit-prod.com/docs/model/tabusolver


Tipo de trabalho: Quantum-Inspired Optimization Problem
Formato de dados: microsoft.qio.v2
ID de destino: 1qbit.tabu
Nome da classe do solucionador do Python: TabuSearch

N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

improvement_cutoff INT Opcional O número de iterações que


o solucionador tenta sem
melhoria antes de parar.
Padrão: 0

improvement_tolerance double Opcional O valor de tolerância que


determina se uma solução é
uma melhoria na iteração
anterior. Padrão: 1e-9

tabu_tenure INT Opcional A gestão impede que uma


variável invertida seja
invertida novamente
durante as iterações.
Padrão: 0

tabu_tenure_rand_max INT Opcional O limite superior do


intervalo exclusivo de
inteiros aleatórios. Intervalo
de valores válido: 1 a
200.000. Padrão: 0
N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

timeout INT Opcional A duração em MS que o


solucionador executa antes
de sair. Se o valor for
definido como 0, ele não
atingirá o tempo limite.
Padrão: 0

Solucionador PTICM
O solucionador de deformação paralela com isoenergético cluster Moves (PTICM) é uma abordagem Monte
Carlo para resolver problemas de QUBO. Nesse algoritmo, várias réplicas do sistema original, cada uma com um
estado inicial diferente, são simuladas em temperaturas diferentes simultaneamente. As réplicas em
temperaturas dos ambientes são trocados periodicamente com base em um critério do Metropolis. Essas trocas
permitem que réplicas diferentes façam uma movimentação aleatória no espaço de temperatura, assim,
superando com eficiência as barreiras de energia.
Para obter mais informações, acesse: https://portal.1qbit-prod.com/docs/model/pticmsolver
Tipo de trabalho: Quantum-Inspired Optimization Problem
Formato de dados: microsoft.qio.v2
ID de destino: 1qbit.pticm
Nome da classe do solucionador do Python: PticmSolver

N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

auto_set_temperatures booleano Opcional Isso define se os


parâmetros de temperatura
são calculados
automaticamente ou não.
Defina-o como True para
calcular automaticamente e
False para personalizar os
parâmetros de temperatura.
Padrão: True

elite_threshold double Opcional A fração das melhores


soluções usadas para o
parâmetro var_fixing_type
com o valor SPVAR. Padrão:
0.3

frac_icm_thermal_layers double Opcional A fração de temperaturas


para o cluster iso-
energético se move. Para
alterar esse valor, defina o
parâmetro perform_icm
como True. Padrão: 0.5

frac_sweeps_fixing double Opcional A fração de varreduras


usada para corrigir as
variáveis QUBO. Padrão:
0.15
N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

frac_sweeps_idle double Opcional A fração de varreduras a


aguardar antes de corrigir
as variáveis QUBO. Padrão:
1.0

frac_sweeps_stagnation double Opcional A fração de varreduras sem


melhoria que dispara uma
reinicialização. Padrão: 1.0

goal string Opcional Isso define se o


solucionador é usado para
otimização ou amostragem.
Valores válidos: "otimizar"
ou "exemplo" padrão:
otimizar

high_temp double Opcional A temperatura mais alta de


uma réplica. Defina o
parâmetro
auto_set_temperatures
como false para usar esse
recurso. Padrão: 2

low_temp double Opcional A temperatura mais alta de


uma réplica. Defina o
parâmetro
auto_set_temperatures
como false para usar esse
recurso. Padrão: 0.2

max_samples_per_layer INT Opcional O número máximo de


amostras coletadas por
réplica. Padrão: 10

max_total_sweeps INT Opcional O número total de


varreduras antes do
encerramento. Padrão:
num_sweeps_per_run * 10

manual_temperatures matriz [Double] Opcional Uma matriz de uma agenda


de temperatura
personalizada que inclui os
valores altos, intermediários
e de baixa temperatura para
as réplicas. Defina o
parâmetro
auto_set_temperatures
como false para usar esse
recurso.

num_elite_temps INT Opcional O número de temperaturas


de elite usadas para corrigir
as variáveis com
persistência. Padrão = 4

num_replicas INT Opcional O número de réplicas em


cada temperatura. Padrão: 2
N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

num_sweeps_per_run INT Opcional O número de varreduras de


Monte Carlo. Padrão: 100

num_temps INT Opcional O número de temperaturas,


incluindo as temperaturas
mais altas e mais baixas.
Defina o parâmetro
auto_set_temperatures
como false para usar esse
recurso. Padrão: 30

perform_icm booleano Opcional Isso define se as


movimentações do cluster
isoenergético devem ou
não ser executadas. Padrão:
true

scaling_type string Opcional Isso define se o problema


QUBO é automaticamente
dimensionado ou não.
MEDIANA significa que ela
é dimensionada
automaticamente e
NO_SCALING significa que
não é. Valores válidos:
"MEDIANA" ou
"NO_SCALING"

var_fixing_type string Opcional Isso decide se os valores


das variáveis QUBO são
fixos ou não. É possível
corrigi-los com os tipos
persistência ou SPVAR.
NO_FIXING significa que as
variáveis não são corrigidas.
Se você escolher
persistência ou SPVAR,
defina também os
parâmetros
solver.frac_sweeps_fixing e
solver.frac_sweeps_idle
como um número menor
que um. Valores válidos:
"Persistência", "SPVAR" ou
"NO_FIXING" padrão:
NO_FIXING

Solucionador Path-Relinking
O algoritmo de path-relinking é um algoritmo heurístico que usa a pesquisa tabu como uma sub-rotina para
resolver um problema de QUBO. O algoritmo começa com um conjunto de soluções de elite encontradas pela
pesquisa tabu. Em seguida, ele constrói um caminho entre cada par de soluções de elite, seleciona uma das
soluções ao longo do caminho e repete a pesquisa tabu. Se a pesquisa tabu encontrar uma solução distinta que
seja melhor do que a pior solução de elite atual, o conjunto de soluções de elite será atualizado com a nova
solução aprimorada. Esse procedimento inteiro é repetido até que o algoritmo atenda a uma condição de
interrupção.
Para obter mais informações, acesse: https://portal.1qbit-prod.com/docs/model/pathrelinkingsolver
Tipo de trabalho: Quantum-Inspired Optimization Problem
Formato de dados: microsoft.qio.v2
ID de destino: 1qbit.pathrelinking
Nome da classe do solucionador do Python: PathRelinkingSolver

N O M E DO PA RÂ M ET RO T IP O O B RIGATÓ RIO DESC RIÇ Ã O

distance_scale double Opcional A distância mínima das


soluções de inicialização e
de orientação para construir
a lista de soluções
candidatas. A solução de
qualidade mais alta na lista
de soluções candidatas é
selecionada para melhoria.
Valores válidos: 0,0 a 0,5
padrão 0,33

greedy_path_relinking booleano Opcional Ao usar o solucionador


path-relinking, há duas
maneiras de gerar um
caminho que leva à solução:
uma é a função ávido e a
outra opera em uma
questão aleatória. Se você
definir o parâmetro this
como true, o solucionador
usará a função ávido. Se
você defini-lo como false,
ele usará o método
aleatório. Padrão: false

ref_set_count INT Opcional O número de soluções de


elite iniciais a serem geradas
pelo algoritmo de pesquisa
tabu. Valores válidos: maior
que 1 padrão: 10

tempo limite INT Opcional A duração em MS que o


solucionador executa antes
de sair. Se o valor for
definido como 0, ele não
atingirá o tempo limite. Se
um valor não for
especificado, o solucionador
usará o valor padrão ou
estimará um novo com
base no problema de
entrada. Os valores
estimados são marcados
como "(computado)".
Padrão: 0
Política de suporte para 1QBit no Azure Quantum
13/05/2021 • 2 minutes to read

Este artigo descreve a política de suporte da Microsoft que se aplica ao uso do provedor da 1QBit no Azure
Quantum. O artigo se aplica a qualquer um dos destinos desse provedor.
Se você estiver usando o provedor da 1QBit e encontrar qualquer problema inesperado que não consiga
solucionar, normalmente basta entrar em contato com a equipe de suporte do Azure para obter ajuda criando
um caso no Suporte do Azure.
No entanto, existem algumas situações em que a equipe do Suporte do Azure precisa redirecionar você para a
equipe de suporte da 1QBit, ou em que você pode receber uma resposta mais rápida se entrar em contato
direto com a 1QBit. Este artigo visa fornecer mais detalhes sobre isso com base em algumas perguntas
frequentes.

Perguntas frequentes
P: Qual é a política de suporte para o uso de ofertas da 1QBit por meio do Azure Quantum?
A Microsoft fornecerá suporte apenas para a Plataforma Azure e para o serviço do Azure Quantum. O suporte
para hardware, simuladores e outros produtos e destinos da 1QBit será fornecido diretamente pela 1QBit. Para
saber mais sobre o Suporte do Azure, consulte Planos de Suporte do Azure. Para saber mais sobre o suporte da
1QBit, visite o site do suporte da 1QBit.
P: O que acontece se eu gerar um chamado de suporte com a equipe do Suporte do Azure e descobrir que um provedor
terceirizado (como a 1QBit) é quem precisará solucionar o problema?
O engenheiro do suporte criará um pacote de redirecionamento para você. Trata-se de um documento PDF que
contém informações sobre seu caso, que você pode apresentar à equipe de suporte da 1QBit. O engenheiro de
suporte também fornecerá conselhos e orientações sobre como entrar em contato com a 1QBit para continuar a
solução do problema.
P: O que acontece se eu gerar um chamado de suporte com a equipe da 1QBit e descobrir que é um problema com o serviço do
Azure Quantum?
A equipe de suporte da 1QBit irá ajudá-lo a entrar em contato com a Microsoft e fornecerá um pacote de
redirecionamento. Trata-se de um documento PDF que você pode usar ao continuar seu chamado de suporte
com a equipe do Suporte do Azure.
Aviso de isenção de responsabilidade de informações de terceiros
Os produtos de terceiros mencionados neste artigo são fabricados por empresas que são independentes da
Microsoft. A Microsoft não oferece nenhuma garantia, implícita ou não, sobre o desempenho ou a confiabilidade
desses produtos.
Aviso de isenção de responsabilidade de contato de terceiros
A Microsoft fornece informações de contato de terceiros para ajudá-lo a encontrar informações adicionais sobre
este tópico. Essas informações de contato podem ser alteradas sem aviso prévio. A Microsoft não garante a
precisão das informações de contato de terceiros.
Provedor QIO da Microsoft
13/05/2021 • 4 minutes to read

O provedor QIO (otimização inspirada em quantum) da Microsoft está habilitado em cada workspace do Azure
Quantum.
Editor: Microsoft
ID do provedor: microsoft

Destinos
O provedor QIO da Microsoft disponibiliza os seguintes segmentos:
Solução: Recozimento simulado (sem parâmetros)
Solução: Recozimento simulado (sem parâmetros - FPGA (matriz de porta programável no campo))
Solução: Recozimento simulado (parametrizada)
Solução: Recozimento simulado (parametrizada - FPGA)
Solução: Deformação paralela (sem parâmetros)
Solução: Deformação paralela (parametrizada)
Solução: Pesquisa de tabu (sem parâmetros)
Solução: Pesquisa tabulada (parametrizada)
Solução: Monte Carlo Quantum (parametrizada)

Comparação de segmentos
A tabela a seguir apresenta uma breve comparação entre os segmentos disponíveis:

M EL H O R C EN Á RIO A SER
NOME DESC RIÇ Ã O A P L IC A DO

Deformação Paralela Reformula o problema de Geralmente supera


otimização como um o desempenho do
sistema termodinâmico e Recozimento
executa várias cópias de um simulado em
sistema, inicializadas problemas
aleatoriamente, em complexos com
temperaturas diferentes. Em cenários robustos
seguida, com base em um Muito bom na
protocolo específico, troca solução de
configurações em problemas de Ising
temperaturas diferentes
para encontrar a
configuração ideal.
M EL H O R C EN Á RIO A SER
NOME DESC RIÇ Ã O A P L IC A DO

Recozimento simulado Reformula o problema de Cenários convexos


otimização como um
sistema termodinâmico e
considera a energia de um
único sistema. As alterações
no sistema serão aceitas se
reduzirem a energia ou
atenderem a um critério
com base na temperatura
decrescente.

Monte Carlo Quantum Semelhante ao Recozimento O cenário de


simulado, mas as alterações otimização tem
ocorrem através da barreiras altas e
simulação de tunelamento finas
quântico através de Devido à sua grande
barreiras, em vez de usar sobrecarga, é útil
saltos de energia térmica. para pequenos
problemas físicos

Pesquisa tabulada A pesquisa tabulada Cenários convexos,


examina as configurações problemas de alta
vizinhas. Pode aceitar densidade,
movimentos de problemas de
deterioração se nenhum QUBO.
movimento de melhoria
estiver disponível e proibir
movimentos para soluções
visitadas anteriormente

FPGA versus CPU


Para algumas soluções oferecemos duas versões: uma versão sem rótulo que é executada em CPUs tradicionais,
e uma versão FPGA rotulada. Na tabela a seguir, você pode analisar as vantagens e desvantagens de usar
soluções de FPGA:

VA N TA GEN S/ DESVA N TA GEN S SO L UÇ Õ ES DE F P GA

Vantagens Execução paralela altamente otimizada, em


comparação com as soluções de CPU. Presenciamos
ganho de desempenho de até 18 vezes (em
comparação com 72 núcleos de CPU) quando as
configurações de parâmetros de recozimento
simulado são as mesmas (reinicializações e
varreduras).
A solução de FPGA usa uma representação de
memória muito condensada. Portanto, para
problemas com um grande número de termos, a
solução de CPU pode falhar para OOM, enquanto
que a solução de FPGA não falha.
VA N TA GEN S/ DESVA N TA GEN S SO L UÇ Õ ES DE F P GA

Desvantagens A solução de FPGA suporta até 65.535 variáveis, essa


é uma limitação rígida.
Para obter o melhor desempenho, as soluções de
FPGA usam operações de ponto flutuante de 32 bits.
Por causa disso, a precisão computacional das
soluções de FPGA é um pouco menor do que as
soluções de CPU.

Disponibilidade regional de FPGA


As soluções baseadas em FPGA estão disponíveis somente para workspaces implantados nas seguintes regiões
do Azure:

REGIÃ O

Oeste dos EUA

Leste dos EUA

Recomendações para soluções de FPGA


As soluções de FPGA usam os mesmos parâmetros que as soluções de CPU correspondentes, mas para obter o
melhor desempenho, ajuste os parâmetros das soluções de FPGA, em vez de apenas usar diretamente os
parâmetros das soluções de CPU. Por exemplo, nas soluções de FPGA, criamos cerca de 200 pipelines paralelos,
e cada pipeline pode tratar uma reinicialização. Portanto, as reinicializações de FPGA não devem ser inferiores a
200.
As soluções de FPGA têm um tempo de inicialização que pode consumir grande parte do tempo total de
execução para resolver pequenos problemas. Se o seu problema puder ser resolvido em alguns segundos
usando uma solução de CPU, provavelmente você não terá um ganho de desempenho mudando para FPGA.
Recomendamos que use as soluções de FPGA quando o tempo de execução na CPU for de pelo menos alguns
minutos.

Recomendações gerais para as soluções do QIO da Microsoft


Abaixo estão algumas pontos a serem analisados ao usar nossas soluções QIO, e as etapas a serem seguidas
para melhorar o desempenho em determinados casos. Observe que outros provedores podem ter requisitos e
recomendações diferentes, específicos para as soluções de cada provedor. A recomendação abaixo se aplica aos
termos que representam o problema e a função de custo. Lembre-se de que um termo é composto por um
coeficiente $c$ e um conjunto de índices ${i}$.
1. Remova os coeficientes que excedem a precisão computacional:
Se a razão do maior para o menor coeficiente for maior do que $2^{64}$, o termo com o coeficiente
menor provavelmente não será levado em consideração e deverá ser removido. Em outras palavras, você
deve remover quaisquer termos com coeficientes $|c_i| < \frac{\max{|c_j|}}{2^{64}}$.
2. Mescle termos duplicados:
Se você gerar seus termos automaticamente, poderá encontrar alguns que são duplicatas uns dos outros,
ou seja, eles contêm o mesmo conjunto de índices/variáveis de decisão. Evitar termos duplicados
aumentará o desempenho da solução, pois ela tratará menos termos.
Vários termos com o mesmo conjunto de variáveis devem ser mesclados em um único termo,
adicionando os coeficientes. Por exemplo, $3 \cdot x_2 x_4 x_1$ e $2 \cdot x_1 x_4 x_2$ podem ser
mesclados em um termo único 5 \cdot x_1 x_2 x_4$.
3. Use coeficientes de inteiro:
Sempre que possível, você deve usar números inteiros para seus coeficientes em números de ponto
flutuante, pois eles fornecerão maior precisão.
Recozimento simulado
13/05/2021 • 6 minutes to read

O recozimento simulado é um método de pesquisa de Monte Carlo denominado a partir da metodologia de


aquecimento e resfriamento do recozimento de metal. O algoritmo simula um estado de temperaturas variáveis
em que a temperatura de um estado (em nossa implementação, representada pelo parâmetro beta – o inverso
de temperatura com a constante de Boltzmann definida como 1 ($\beta = 1 / T$)) influencia a probabilidade de
tomada de decisão em cada etapa.
No contexto de problemas de otimização, o algoritmo começa em um estado de alta temperatura inicial (beta
baixo ou beta_start ) em que as movimentações "ruins" no sistema são aceitas com uma probabilidade mais
alta e lentamente "resfria" em cada varredura até que o estado atinja a temperatura mais baixa especificada (alta
beta ou beta_stop ). Em temperaturas inferiores, as movimentações que não melhoram o valor de objetivo são
menos prováveis de serem aceitas.
Ao fazer uma varredura por um problema binário, cada variável de decisão é "invertida" com base no impacto
do valor de objetivo dessa inversão. Inversões que melhoram o valor de objetivo são aceitas automaticamente,
enquanto as movimentações que não melhoram o valor de objetivo são aceitas em uma base probabilística,
calculadas por meio do Critério Metropolis.

Recursos de recozimento simulado no Azure Quantum


O recozimento simulado no Azure Quantum dá suporte a:
Modo sem parâmetros e modo parametrizado (com parâmetros)
Formatos de entrada Ising e PUBO
Hardware de CPU e FPGA (confira abaixo instruções sobre como usar os dois)

Quando usar o recozimento simulado


O recozimento simulado é um algoritmo multifuncional padrão que serve para muitos tipos de problemas. É
recomendável começar com esse algoritmo, pois ele produzirá de forma confiável uma boa qualidade e solução
rápida para a maioria dos problemas.
Ele também é um bom algoritmo para problemas maiores (milhares de variáveis).

NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.

Recozimento simulado sem parâmetros (CPU)


A versão sem parâmetros do recozimento simulado é recomendada para novos usuários, aqueles que não
desejam ajustar os parâmetros manualmente (especialmente betas) e até mesmo como ponto de partida para
ajuste manual. Os principais parâmetros a serem ajustados para esse solucionador são o número de sweeps ,
beta_start e beta_stop (descrito na próxima seção).

O solucionador sem parâmetros será interrompido em timeout (segundos) ou quando houver convergência
suficiente em uma solução.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

timeout Tempo máximo de execução para o solucionador (em


segundos). Este é um mecanismo que trabalha bem,
portanto, o solucionador talvez não pare imediatamente
quando o tempo limite for atingido.

seed (optional) Valor de semente: usado para reproduzir resultados.

Para criar um solucionador de recozimento simulado sem parâmetros para a CPU usando o SDK:

from azure.quantum.optimization import SimulatedAnnealing


# Requires a workspace already created.
solver = SimulatedAnnealing(workspace, timeout=100, seed=22)

O solucionador sem parâmetros retornará os parâmetros usados no JSON de resposta. Você pode usar esses
parâmetros para resolver problemas semelhantes (número de variáveis, termos, localidade e escala de
coeficiente) usando o solucionador de recozimento simulado parametrizado.

CPU (recozimento simulado parametrizado)


O recozimento simulado com os parâmetros especificados será melhor aproveitado se você já estiver
familiarizado com a terminologia do recozimento simulado (varreduras, betas) e/ou tiver uma ideia de quais
valores de parâmetro você pretende usar. Se esta é a primeira vez que você usa o recozimento
simulado para resolver um problema, a versão sem parâmetros é recomendada. Alguns dos
parâmetros, como beta_start e beta_stop , são difíceis de estimar sem um bom ponto de partida.
O recozimento simulado permite os seguintes parâmetros:

N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

sweeps Número de conjuntos de iterações a serem executados sobre


as variáveis de um problema. Mais varreduras podem
melhorar a solução (a menos que ela já esteja no mínimo
global).

beta_start/beta_stop Representa o início e a interrupção dos betas do


agendamento de recozimento. Um valor adequado para
esses parâmetros dependerá totalmente do problema e da
magnitude de suas mudanças. Em geral, uma probabilidade
de aceitação diferente de zero e de declínio é suficiente.

restarts O número de repetições do agendamento do recozimento a


ser executado. Cada reinicialização será iniciada com uma
configuração aleatória a menos que uma configuração
inicial seja fornecida no arquivo de problema. As
reinicializações serão executadas em paralelo e divididas
entre os threads da VM. Recomendado definir esse valor em
pelo menos 72.

seed (optional) Valor de semente: usado para reproduzir resultados

Para criar um solucionador de recozimento simulado parametrizado para a CPU usando o SDK:
from azure.quantum.optimization import SimulatedAnnealing
# Requires a workspace already created.
solver = SimulatedAnnealing(workspace, sweeps=2, beta_start=0.1, beta_stop=1, restarts=72, seed=22)

Recozimento simulado (FPGA)


O solucionador de recozimento simulado também está disponível no hardware de FPGA para os modos de
parâmetro e sem parâmetros. Os parâmetros são os mesmos das versões para CPU.
Esta seção descreverá as vantagens e desvantagens de usar o solucionador FPGA. Os usuários são incentivados
a tomar uma decisão informada com base nas características dos próprios problemas.
Quando usar o recozimento simulado de FPGA
Os solucionadores FPGA têm alguns custos internos como transferências de PCIe, inicialização de dispositivo
FPGA etc. Se um problema de otimização for muito pequeno (por exemplo, a execução da CPU leva segundos), o
custo interno do hardware de FPGA será a maior parte do custo, com pouquíssimos benefícios da solução.
Portanto, é recomendável usar os solucionadores de FPGA quando o tempo de execução na CPU for de minutos
ou mais.
Vantagens do recozimento simulado de FPGA
Altamente otimizado para paralelização. Em comparação com o solucionador de recozimento simulado de
CPU equivalente com os mesmos parâmetros, o solucionador de recozimento simulado de FPGA é em média
10 vezes mais rápido.
Representação da memória altamente condensada. Um problema com um grande número de termos pode
falhar em um solucionador de CPU devido a limites de memória, mas pode se ajustar ao hardware de FPGA.
O ganho de desempenho pode não ser óbvio para o modo sem parâmetros porque ambos os algoritmos são
restringidos por uma configuração timeout (o que interrompe a maioria dos problemas). No entanto, no
hardware de FPGA, o solucionador provavelmente executará muitas varreduras no mesmo tempo do que na
CPU.
Limitações do recozimento simulado de FPGA
O solucionador de FPGA permite até 65.535 variáveis e essa é uma limitação rígida. Esse número é
limitado pelo DRAM disponível, o que geralmente não é um problema para FPGA ( já que a maioria dos
problemas é menor do que 65.535).
Para obter o melhor desempenho, os solucionadores de FPGA no Azure Quantum usam operações de ponto
flutuante de 32 bits. Por isso, a precisão da computação dos solucionadores de FPGA é um pouco menor do
que a dos solucionadores de CPU.
Guia de parâmetro para recozimento simulado de FPGA
O recozimento simulado de FPGA usa os mesmos parâmetros que o solucionador de CPU correspondente, mas
ainda é recomendável ajustar os parâmetros de solucionadores de FPGA separadamente, em vez de usar
parâmetros predefinidos de solucionadores de CPU.
Também é recomendável definir o número de reinicializações, no mínimo, como 216 caso o solucionador de
FPGA seja usado, pois ele pode permitir um nível mais alto de paralelização.
Para criar um solucionador de recozimento simulado para o FPGA usando o SDK, basta especificar a opção de
plataforma da seguinte maneira:

from azure.quantum.optimization import SimulatedAnnealing, HardwarePlatform


# Requires a workspace already created.
solver = SimulatedAnnealing(workspace, timeout=100, seed=22, platform=HardwarePlatform.FPGA)
Para a versão parametrizada:

from azure.quantum.optimization import SimulatedAnnealing, HardwarePlatform


# Requires a workspace already created.
solver = SimulatedAnnealing(workspace, sweeps=2, beta_start=0.1, beta_stop=1, restarts=72, seed=22,
platform=HardwarePlatform.FPGA)
Deformação paralela
30/04/2021 • 3 minutes to read

As deformações paralelas podem ser consideradas como uma variante do algoritmo do tratamento térmico
simulado ou mais geralmente, método de Monte Carlo via Cadeias de Markov. Os solucionadores de
deformação paralela do Azure Quantum foram projetados para resolver problemas de otimização binária por
meio de amostragem aleatória.
Assim como o tratamento térmico simulado, a função de custo é analisada por meio de saltos térmicos.
Diferentemente de um tratamento térmico simulado, uma temperatura de resfriamento não é usada.
Em vez de executar uma única cópia do sistema, a deformação paralela cria várias cópias de um sistema,
chamadas de réplicas, que são inicializadas e executadas aleatoriamente em temperaturas diferentes. Em
seguida, o mesmo processo é seguido como no tratamento térmico simulado, mas com base em um protocolo
específico, duas réplicas podem ser trocadas entre temperaturas diferentes. Essa alteração permite que
navegadores que estavam presos anteriormente em um local optimum fossem retirados de lá, incentivando
uma exploração mais ampla do espaço do problema.

NOTE
Você pode encontrar mais informações sobre o algoritmo de deformação paralela em Marinari e Parisi 1992-Simulated
Tempering: A New Monte Carlo Scheme

Recursos da deformação paralela no Azure Quantum


A deformação paralela no Azure Quantum dá suporte a:
Modo sem parâmetros e modo parametrizado
Formatos de entrada Ising e PUBO
Somente CPU

Quando usar a deformação paralela


A deformação paralela geralmente supera os tratamentos térmicos simulados em problemas complexos com
cenários robustos.
Ela também soluciona facilmente problemas de Ising ou problemas equivalentes (como problemas de corte
mínimo).

NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.

A deformação paralela sem parâmetros


A versão sem parâmetros da deformação paralela é recomendada para novos usuários, aqueles que não
desejam ajustar os parâmetros manualmente e até mesmo como ponto de partida para ajuste manual. Os
principais parâmetros a serem ajustados para esse solucionador são o número de sweeps , replicas e
all_betas (descrito na próxima seção).
O solucionador sem parâmetros será interrompido em timeout (segundos) ou quando houver convergência
suficiente em uma solução.

N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

timeout Tempo máximo de execução para o solucionador (em


segundos). Este é um mecanismo que trabalha bem,
portanto, o solucionador pode não parar imediatamente
quando o tempo limite for atingido.

seed (optional) Valor de semente: usado para reproduzir resultados.

Para criar um solucionador de deformação paralela sem parâmetros usando o SDK:

from azure.quantum.optimization import ParallelTempering


# Requires a workspace already created.
solver = ParallelTempering(workspace, timeout=100, seed=22)

O solucionador sem parâmetros retornará os parâmetros usados no JSON de resposta. Você pode usar esses
parâmetros para resolver problemas semelhantes (número de variáveis, termos, localidade e escala de
coeficiente) usando o solucionador de deformação paralela parametrizada.

Deformação paralela parametrizada


As deformações paralelas com os parâmetros especificados são bem aproveitados se você já estiver
familiarizado com a terminologia da deformação paralela (varreduras, betas) e/ou tiver uma ideia de quais
valores de parâmetro você pretende usar. Se esta for a primeira vez que você usa a deformação
paralela para resolver um problema, a versão sem parâmetros é recomendada. Alguns dos
parâmetros, como beta_start e beta_stop são difíceis de estimar sem um bom ponto de partida.
A deformação paralela dá suporte aos seguintes parâmetros:

N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

sweeps Número de conjuntos de iterações a serem executados sobre


as variáveis de um problema. Mais varreduras podem
melhorar a solução (a menos que ela já esteja no mínimo
global).

replicas O número de cópias simultâneas em execução para


amostragem. Cada instância será iniciada com uma
configuração aleatória. Recomendamos que esse valor não
seja menor do que o número de núcleos disponíveis no
computador, para aproveitar totalmente o poder de
computação disponível. Atualmente no Azure Quantum, isso
não deve ser menor que 72.

all_betas A lista de valores beta usados em cada réplica para


amostragem. O número de valores beta deve ser igual ao
número de réplicas, porque cada réplica será atribuída a um
valor beta da lista. Esses valores beta controlam como o
solucionador evita os pontos de lombada da otimização:
quanto maiores os valores beta, menor é a probabilidade de
que o processo de amostragem venha de um local optimum.

seed (optional) Valor de semente: usado para reproduzir resultados


Quanto maior o número de varreduras e réplicas, maior é a probabilidade de o solucionador de deformação
paralela encontrar uma solução ideal ou quase ideal, no entanto, o solucionador levará mais tempo para ser
executado.
Para criar um solucionador de deformação paralela parametrizado usando o SDK:

from azure.quantum.optimization import ParallelTempering


# Requires a workspace already created.
solver = ParallelTempering(workspace, sweeps=2, all_betas=[1.15, 3.14], replicas=2, seed=22)
Solucionador de otimização de pesquisa tabu
18/05/2021 • 3 minutes to read

A Pesquisa Tabu é um algoritmo de pesquisa de vizinhança que emprega uma lista de tabu. Uma lista tabu
representa um conjunto de soluções potenciais que a pesquisa não pode visitar por várias etapas (gestão tabu).
O processo de tomada de decisão por etapa é semelhante ao de um algoritmo greedy, mas com uma lista de
movimentações proibidas (geralmente movimentações que foram visitadas recentemente). Em cada varredura,
a movimentação que resulta na melhor melhoria da função objetiva e não está na lista tabu é executada.
Na Azure Quantum, a abordagem de algoritmos principais para nossa implementação de pesquisa tabu é
descrita em Beasley 1999 - Algoritmos heurísticos para o Problema de Programação Quadrática Binária
Irrestrita e ampliamos essa abordagem com vários aprimoramentos na eficiência computacional. Para vários
threads, utilizamos a abordagem de várias inicializações descrita em Palubeckis et al. 2004 - Estratégias de
pesquisa tabu de várias inicializações.

Recursos da pesquisa tabu no Azure Quantum


A pesquisa tabu no Azure Quantum dá suporte a:
Modo sem parâmetros e modo parametrizado (com parâmetros)
Formatos de entrada Ising e PUBO
Somente CPU

Quando usar a pesquisa tabu


No contexto de problemas de programação binária, a pesquisa tabu é mais comumente usada para problemas
de QUBO (variáveis de decisão com o valor 0 e 1, com localidade quadrática).
Espera-se que a pesquisa tabu tenha um desempenho muito bom em cenários de problema com os seguintes
recursos:
Alta densidade de termos (em qualquer densidade na faixa de 5%-100%)
Problemas "simples" altamente convexos que se adequarão a um algoritmo greedy
Variáveis de decisão 0,1 e localidade quadrática (problemas de QUBO)
Problemas menores (embora não haja nenhum limite no número de variáveis aceitas). Como apenas uma
movimentação é feita por varredura, esse algoritmo naturalmente levará mais tempo com problemas
maiores.

NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.

Pesquisa tabu sem parâmetros


A versão sem parâmetros da pesquisa tabu é recomendada para novos usuários, aqueles que não desejam
ajustar os parâmetros manualmente e até mesmo como ponto de partida para ajuste manual. Os principais
parâmetros a serem ajustados para esse solucionador são o número de sweeps e tabu_tenure (descrito na
próxima seção).
O solucionador sem parâmetros será interrompido em timeout (segundos) ou quando houver convergência
suficiente em uma solução.

N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

timeout Tempo máximo de execução para o solucionador (em


segundos). Este é um mecanismo que trabalha bem,
portanto, o solucionador talvez não pare imediatamente
quando o tempo limite for atingido.

seed (optional) Valor de semente: usado para reproduzir resultados.

Para criar um solucionador Tabu sem parâmetros usando o SDK:

from azure.quantum.optimization import Tabu


# Requires a workspace already created.
solver = Tabu(workspace, timeout=100, seed=22)

O solucionador sem parâmetros retornará os parâmetros usados no JSON de resposta. Você pode usar esses
parâmetros para resolver problemas semelhantes (número de variáveis, termos, localidade e escala de
coeficiente) usando o solucionador de pesquisa tabu parametrizada.

Pesquisa tabu parametrizada


A pesquisa tabu com parâmetros especificados é recomendada se você já estiver familiarizado com a
terminologia de pesquisa tabu (iterações, gestão, etc.) e/ou se tiver uma ideia de quais parâmetros você
pretende usar. Se esta for a primeira vez que você usa a pesquisa tabu para um problema, a versão livre de
parâmetros (descrita abaixo) é recomendada.
A pesquisa tabu oferece suporte aos seguintes parâmetros:

N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

sweeps Número de conjuntos de iterações a serem executados sobre


as variáveis de um problema. Varreduras adicionais
geralmente melhorarão a solução (a menos que ela já esteja
no mínimo global).

tabu_tenure A gestão da lista tabu em movimentações. Descreve quantas


movimentações uma variável permanece na lista tabu depois
que ela é criada. Esse valor é recomendado estar entre 1 e o
número de variáveis para que haja algum impacto. Esse será
o principal parâmetro ajustável que determinará a qualidade
da solução. Um bom ponto de partida é 20.

restarts O número de instâncias repetidas para executar o


solucionador. Cada instância será iniciada com uma
configuração aleatória a menos que uma configuração
inicial seja fornecida no arquivo de problema . É
recomendável que seja pelo menos 72 para utilizar
totalmente os recursos do computador.

replicas (deprecated) O número de solucionadores simultâneos para inicializar.


Esse parâmetro agora usará como padrão o número de
processadores disponíveis no computador e não aceitará
mais entradas.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

seed (optional) Valor de semente: usado para reproduzir resultados

Para criar um solucionador Tabu parametrizados usando o SDK:

from azure.quantum.optimization import Tabu


# Requires a workspace already created.
solver = Tabu(workspace, sweeps=2, tabu_tenure=5, restarts=72, seed=22)
Monte Carlo Quântico
01/05/2021 • 2 minutes to read

O Monte Carlo Quântico é um algoritmo de recozimento do Metropolis, semelhante ao conceito de recozimento


simuladoque começa em uma temperatura baixa e melhora a solução ao pesquisar entre as barreiras com
alguma probabilidade, quando uma perturbação externa é aplicado ao sistema. Como esse campo externo varia
em cada etapa do Monte Carlo, a configuração pode ser capaz de realizar o túnel por meio de barreiras de
energia e evoluir para um estado fundamental desejado (sem ter a energia térmica necessária para subir as
barreiras, como seria necessário no recozimento simulado).
No Azure Quantum, o núcleo da abordagem de algoritmos para nossa implementação QMC é baseado no
algoritmo Wolff para o recozimento e ampliamos essa abordagem com várias melhorias em nossa eficiência
computacional.

Recursos do Monte Carlo Quântico no Azure Quantum


Modo parametrizado (com parâmetros)
Formatos de entrada Ising e PUBO
Somente CPU

Quando usar o Monte Carlo Quântico


Esse algoritmo deve ter o melhor desempenho nos dois cenários a seguir:
Quando há barreiras altas e estreitas no panorama de energia (função de custo)
Se a solução já estiver em uma configuração viável em uma temperatura baixa e o usuário quiser melhorar a
solução

NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.

Monte Carlo Quântico (CPU) parametrizado


O Monte Carlo Quântico oferece suporte aos seguintes parâmetros:

N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

sweeps Número de conjuntos de iterações a serem executados sobre


as variáveis de um problema. Varreduras adicionais
geralmente melhorarão a solução (a menos que ela já esteja
no mínimo global).

trotter_number O número de cópias de cada variável a serem geradas para a


execução da simulação.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O

restarts O número de repetições do agendamento do recozimento a


ser executado. Cada reinicialização será iniciada com uma
configuração aleatória, a menos que uma configuração inicial
seja fornecida no arquivo de problema. As reinicializações
serão executadas em paralelo e divididas entre os threads da
máquina virtual. Recomendado definir esse valor em pelo
menos 72.

beta_start Representa a temperatura na qual o agendamento de


recozimento é executado. Isso deve ser um valor baixo o
suficiente para produzir uma configuração viável.

transverse_field_start & transverse_field_stop Representa os valores de início e parada do campo externo


aplicado ao agendamento de recozimento. Um valor
adequado para esses parâmetros dependerá totalmente do
problema e da magnitude de suas mudanças. Em geral, uma
probabilidade de aceitação diferente de zero e de declínio é
suficiente.

Para criar um solucionador parametrizado Monte Carlo Quântico usando o SDK:

from azure.quantum.optimization import QuantumMonteCarlo


# Requires a workspace to be already created
solver = QuantumMonteCarlo(workspace, sweeps = 2, trotter_number = 10, restarts = 72, seed = 22, beta_start
= 0.1, transverse_field_start = 10, transverse_field_stop = 0.1)
Workspace Quantum
13/05/2021 • 2 minutes to read

from azure.quantum import Workspace

Construtor
Para criar um objeto Workspace, você deve fornecer os argumentos a seguir para se conectar. Se você ainda não
criou um workspace, siga as etapas em Guia para a criação de workspace do Azure Quantum usando os
seguintes valores:
subscription_id : ID da assinatura em que o workspace está implantado.
resource_group : nome do grupo de recursos em que o workspace está implantado.
name : nome do workspace.
location : local em que o workspace está implantado, por exemplo, Oeste dos EUA , com o comando
create ou show .

workspace = Workspace (
subscription_id = "", # Add your subscription_id
resource_group = "", # Add your resource_group
name = "" # Add your workspace name
location= "" # Add the workspace location, for example, westus
)

Workspace.get_job
Recupera informações sobre um trabalho.

from azure.quantum import Workspace

workspace = Workspace(...)
job = workspace.get_job("285cfcb4-6822-11ea-a05f-2a16a847b8a3")
print(job.details.status)

> Succeeded

Workspace.list_jobs
Retorna a lista de trabalhos existentes no workspace.
from azure.quantum import Workspace

workspace = Workspace(...)
jobs = workspace.list_jobs()
for job in jobs:
print(job.id, job.details.status)

> 08ea8792-68f2-11ea-acc5-2a16a847b8a3 Succeeded


> 0ab1863a-68f2-11ea-82b3-2a16a847b8a3 Succeeded
> 0c5c507e-68f2-11ea-ba75-2a16a847b8a3 Cancelled
> f0c8de58-68f1-11ea-a565-2a16a847b8a3 Executing

Workspace.cancel_job
Cancela um trabalho que foi enviado anteriormente.

from azure.quantum import Workspace

workspace = Workspace(...)
job = workspace.get_job("285cfcb4-6822-11ea-a05f-2a16a847b8a3")

workspace.cancel_job(job)
print(job.details.status)

> Succeeded

Workspace.login
Registra o usuário local no Azure. Primeiro ele tenta usar as credenciais armazenadas em cache de um cache
local seguro. O argumento opcional refresh pode ser usado para ignorar o cache e forçar a autenticação.

NOTE
Passar para refresh=True limpará a propriedade workspace.credentials e forçará uma nova Autenticação de Dispositivo
Interativa. As credenciais definidas em workspace.credenciais serão perdidas, incluindo o ServicePrincipalCredentials.

workspace = Workspace(...)
workspace.login()

Ao chamar o logon, você verá a seguinte imagem em seu console:

To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code <CODE>
to authenticate.

Depois de você se conectar, as credenciais são armazenadas localmente em cache. O local do cache pode ser
especificado por meio da variável de ambiente AZURE_QUANTUM_TOKEN_CACHE .
Trabalho de otimização do Quantum
30/04/2021 • 2 minutes to read

from azure.quantum.optimization import Job

Job.get_results
Recupera o resultado do trabalho (ou seja, a solução calculada e o custo). Se o trabalho ainda não tiver sido
concluído, bloqueie-o até que ele tenha terminado.

results = job.get_results()
print(results)

> {'version': '1.0', 'configuration': {'1': 1, '0': 1, '2': -1, '3': 1}, 'cost': -23.0}

Job.refresh
Atualiza os detalhes do trabalho consultando o workspace.

job = workspace.get_job(jobId)
job.refresh()
print(job.id)

> 5d2f9cd70f55f149e3ed3aef

Job.has_completed
Retorna um valor booliano que indica se o trabalho foi concluído (por exemplo, se o trabalho está em um estado
final).

job = workspace.get_job(jobId)
job.refresh()
print(job.has_completed())

> False

Job.wait_until_completed
Continua atualizando os detalhes do trabalho até atingir um estado final. Para obter mais informações sobre os
estados de trabalho, confira Visão geral do Azure Quantum.

job = workspace.get_job(jobId)
job.wait_until_completed()
print(job.has_completed())

> True
Problema de otimização do Quantum
28/04/2021 • 2 minutes to read

Problema
from azure.quantum.optimization import Problem

Construtor
Para criar um objeto Problem , especifique as seguintes informações:
name : um nome amigável para o problema. Sem restrições de exclusividade.
[opcional] terms : uma lista de Term objetos a serem adicionados ao problema.
[opcional] problem_type : o tipo de problema. Deve ser ProblemType.ising ou ProblemType.pubo . O padrão é
ProblemType.ising .

terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0])
]

problem = Problem(name="My Difficult Problem", terms=terms)

Problem.add_term
Adiciona um único termo ao problema. Ele usa um coeficiente para o termo e os índices de variáveis que
aparecem no termo.

coefficient = 0.13
problem.add_term(c=coefficient, indices=[2,0])

Problem.add_terms
Adiciona vários termos ao problema usando uma lista de Terms .

problem.add_terms([
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0]),
Term(c=9, indices=[2,1]),
Term(c=2, indices=[3,0]),
Term(c=-4, indices=[3,1]),
Term(c=4, indices=[3,2])
])

Problem.serialize
Serializa um problema para uma cadeia de caracteres json.
problem = Problem("My Problem", [Term(c=1, indices=[0,1])])
problem.serialize()

> {"cost_function": {"version": "1.0", "type": "ising", "terms": [{"c": 1, "ids": [0, 1]}]}}

Problem.upload
Os dados do problema podem ser carregados explicitamente em uma conta de armazenamento do Azure
usando seu método upload que recebe como parâmetro a instância Workspace :

problem.upload(workspace=workspace)

Depois que um problema é carregado explicitamente, ele não será carregado automaticamente durante o envio,
a menos que seus termos sejam alterados.
ProblemType de otimização do Quantum
28/04/2021 • 2 minutes to read

Tipo de problema de otimização do Quantum


from azure.quantum.optimization import ProblemType

A ProblemType enumeração permite que você especifique o tipo de problema de otimização que deseja resolver.
ProblemType.pubo
Um problema de otimização binária não restringida polinomial (PUBO) é um problema do formulário:
$$H = \sum_{i} c_{i} x_{i} + \sum_{i,j} c_{i,j} x_{i} x_{ j} + \sum_{i,j,k} c_{i,j,k} x_{i} x_{ j} x_{k} \text{ where } c_{i,j,k} \in
R \text{ and } x_{i,j,k} \in [0, 1]$$
Em que H é a função de custo, também conhecida como Hamiltonian. Ele será chamado de k-local se o grau
máximo de polinomial for k.
ProblemType.ising
Um modelo Ising é uma função de custo do formulário:
$$H = \sum_{i} c_{i} s_{i} + \sum_{i,j} c_{i,j} s_{i} s_{ j} + \sum_{i,j,k} c_{i,j,k} s_{i} s_{ j} s_{k} \text{ where } c_{i,j,k} \in
R \text{ and } s_{i,j,k} \in [-1, 1]$$
Ele será chamado de k-local se o grau máximo de polinomial for k.
Formato de entrada para problemas de otimização
01/05/2021 • 2 minutes to read

Este documento explica como os parâmetros para problemas de otimização podem ser especificados para todos
os solucionadores diferentes.

Deformação paralela
N O M E DA P RO P RIEDA DE ( DIF EREN C IA
M A IÚSC UL A S E M IN ÚSC UL A S) T IP O DESC RIÇ Ã O

all_betas uma lista de floats Especifica a lista de temperaturas


inversas. Essa lista deve ser igual em
comprimento ao número de réplicas
(descritas abaixo)

réplicas inteiro Especifica o número de iterações do


solucionador a serem executadas.

varreduras inteiro Especifica o número de etapas de


Monte Carlo a serem executadas em
cada iteração de um solucionador.

seed Um inteiro aleatório. Especifica um valor aleatório para


iniciar a simulação.

tempo limite inteiro Especifica o número máximo de


segundos para executar o loop do
solucionador principal. a hora de
inicialização não respeita esse valor,
portanto, o solucionador pode ser
executado por mais tempo do que o
valor especificado.

Recozimento simulado
N O M E DA P RO P RIEDA DE ( DIF EREN C IA
M A IÚSC UL A S E M IN ÚSC UL A S) T IP O DESC RIÇ Ã O

beta_start FLOAT Especifica a lista de temperaturas


inversas. Esta lista deve ser igual, em
comprimento, ao número de réplicas
(descritas abaixo)

beta_stop FLOAT Especifica o número de iterações do


solucionador a serem executadas.

varreduras inteiro Especifica o número de etapas de


Monte Carlo a serem executadas em
cada iteração de um solucionador.
N O M E DA P RO P RIEDA DE ( DIF EREN C IA
M A IÚSC UL A S E M IN ÚSC UL A S) T IP O DESC RIÇ Ã O

seed Um inteiro aleatório. Especifica um valor aleatório para


iniciar a simulação.

tempo limite inteiro Especifica o número máximo de


segundos para executar o loop do
solucionador principal. a hora de
inicialização não respeita esse valor,
portanto, o solucionador pode ser
executado por mais tempo do que o
valor especificado.

reinícios integers Especifica o número de iterações da


simulação a serem executadas.

Tabu
N O M E DA P RO P RIEDA DE ( DIF EREN C IA
M A IÚSC UL A S E M IN ÚSC UL A S) T IP O DESC RIÇ Ã O

tabu_tenure inteiro Especifica a gestão de Tabu.

tempo limite inteiro Especifica o número máximo de


segundos para executar o loop do
solucionador principal. a hora de
inicialização não respeita esse valor,
portanto, o solucionador pode ser
executado por mais tempo do que o
valor especificado.

seed Um inteiro aleatório entre 0 e 101 Especifica um valor aleatório para


iniciar a simulação.

sweep inteiro Especifica o número de etapas de


Monte Carlo a serem executadas em
cada iteração da simulação.

Monte Carlo quântico


N O M E DA P RO P RIEDA DE ( DIF EREN C IA
M A IÚSC UL A S E M IN ÚSC UL A S) T IP O DESC RIÇ Ã O

beta_start FLOAT Especifica o inverso da temperatura na


qual o problema é resolvido.

transverse_field_start FLOAT Especifica o valor inicial do campo


externo fornecido para a simulação.

transverse_field_end FLOAT Especifica o valor final do campo


externo fornecido para a simulação.

seed Um inteiro aleatório Especifica um valor aleatório para


iniciar a simulação.
N O M E DA P RO P RIEDA DE ( DIF EREN C IA
M A IÚSC UL A S E M IN ÚSC UL A S) T IP O DESC RIÇ Ã O

sweep inteiro Especifica o número de etapas de


Monte Carlo a serem executadas em
cada iteração da simulação.

trotter_number inteiro Especifica o número de cópias de cada


variável a ser criada em uma
simulação.
Termo de otimização do Quantum
28/04/2021 • 2 minutes to read

Termo
from azure.quantum.optimization import Term

Construtor
Um problema de otimização consiste em uma soma dos termos, cada um dos quais podemos representar com
o objeto Term . Para criar um objeto Term , especifique as seguintes informações:
c : isso corresponde ao coeficiente.
indices : isso corresponde ao produto. Especificamente, o parâmetro é preenchido com os índices de todas
as variáveis que aparecem no termo.
Por exemplo, o termo $2 \cdot (x_1 \cdot x_2)$ é traduzido como o seguinte objeto:

Term(c=2, indices=[1,2]).

Ou, o termo $3 \cdot (x_0 \cdot x_1 \cdot x_2)$ é traduzido como o seguinte objeto:

Term(c=3, indices=[0,1,2]).

Para obter mais informações sobre as funções de custo e como os termos se relacionam com uma definição de
problema, consulte Funções de custo. Os termos podem ser fornecidos a um objeto Problem , consulte
Problema.
Apresentar um problema de otimização
13/05/2021 • 2 minutes to read

Para apresentar um problema simples a ser resolvido, crie uma instância de em Problem e defina problem_type
como ProblemType.ising ou ProblemType.pubo . Para obter mais informações, confira ProblemType .

from azure.quantum.optimization import Problem, ProblemType, Term, ParallelTempering

problem = Problem(name="My First Problem", problem_type=ProblemType.ising)

Em seguida, crie uma matriz de termos e as adicione ao problem :

terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0]),
Term(c=9, indices=[2,1]),
Term(c=2, indices=[3,0]),
Term(c=-4, indices=[3,1]),
Term(c=4, indices=[3,2])
]

problem.add_terms(terms=terms)

NOTE
Conforme descrito abaixo, há várias maneiras de fornecer os termos para o problema.

Maneiras de fornecer os termos do problema


Há três maneiras de fornecer os termos para um Problem : no construtor, individualmente e como uma lista de
objetos Term .
No construtor
Você pode fornecer uma matriz de objetos Term no construtor de um Problem .

terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0])
]

problem = Problem(name="My Difficult Problem", terms=terms)

Individualmente
Você pode fornecer cada termo individualmente chamando o método add_term no Problem .
problem = Problem(name="My Difficult Problem", problem_type=ProblemType.ising)
problem.add_term(c=-9, indices=[0])
problem.add_term(c=-3, indices=[1,0])
problem.add_term(c=5, indices=[2,0])

Adicionar uma lista de termos


Você também pode fornecer uma lista de objetos Term usando o método add-terms no Problem .

problem = Problem(name="My Difficult Problem")


terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0]),
Term(c=9, indices=[2,1]),
Term(c=2, indices=[3,0]),
Term(c=-4, indices=[3,1]),
Term(c=4, indices=[3,2])
]

problem.add_terms(terms=terms)
Aplicar os solucionadores para resolver problemas
de otimização
28/04/2021 • 2 minutes to read

Assim que tivermos Problem , estaremos prontos para solucioná-lo aplicando um solucionador . Neste
exemplo, usaremos uma versão sem parâmetros da deformação paralela. Você pode encontrar a documentação
sobre esse solucionador e os outros solucionadores disponíveis na Visão geral do solucionador.

solver = ParallelTempering(workspace, timeout=100)

Para argumentos, o solucionador usa o Workspace criado anteriormente, além de um único parâmetro que é a
quantidade máxima de tempo (em segundos) para executar o solucionador. A documentação detalhada sobre
parâmetros está disponível na referência para cada solucionador.

NOTE
Consulte Usar o SDK do Python para obter detalhes sobre como se conectar a um espaço de trabalho e obter um objeto
Workspace para ele.

Os solucionadores fornecem um método optimize que espera um Problem . O método optimize carrega a
definição do problema, envia um trabalho para resolver o problema e sonda o status até que o trabalho conclua
a execução. Depois que o trabalho for concluído, ele retornará um objeto JobOutput que contém os resultados.
Consulte Entender os resultados do solucionador para interpretar os resultados.

result = solver.optimize(problem)
print(result)

Esse método enviará o problema para o Azure Quantum para otimização e esperará que ele seja resolvido de
forma síncrona. Você verá uma saída semelhante à seguinte em seu terminal:

> {'configuration': {'0': 1, '1': 1, '2': -1, '3': 1}, 'cost': -32.0}

Consulte Resolver problemas de execução prolongada para resolver problemas de forma assíncrona.
Usando os solucionadores CPU vs FPGA
Por padrão, é usada a versão de CPU de um solucionador. Para especificar uma versão diferente, como FPGA,
especifique um parâmetro platform , conforme mostrado abaixo.

solver = SimulatedAnnealing(workspace, timeout=100, platform=HardwarePlatform.FPGA)


Entender os resultados do solucionador
30/04/2021 • 2 minutes to read

O resultado de um trabalho de solucionador é um objeto JobOutput que pode ser examinado para obter
informações úteis.
Algumas das propriedades úteis no resultado são:
1. configuration : o dicionário descreve a atribuição de variáveis, em que, para cada par de chave-valor no
dicionário, a chave é o índice de uma variável, e um valor é o valor atribuído a essa variável pelo
solucionador.
2. cost : a solução otimizada para o problema quando a configuration é aplicada às variáveis
3. parameters : solucionadores diferentes usam parâmetros diferentes para resolver o problema. Neste
exemplo, vemos:
a. all_betas : uma matriz das temperaturas iniciais para o solucionador de deformação paralela
b. replicas : o número de execuções do solucionador antes de avaliar a melhor configuração de todas
essas execuções
c. sweeps : o número de etapas de Monte Carlo executadas em cada iteração do solucionador.
O exemplo a seguir mostra como imprimir o resultado se um trabalho do solucionador foi enviado de forma
síncrona.

result = solver.optimize(problem)
print(result)

O exemplo a seguir mostra a versão assíncrona.

job = solver.submit(problem)
result = job.get_results()
print(result)

Em ambos os casos, o resultado deve ser semelhante ao descrito a seguir:

> {'version': '1.0', 'configuration': {'0': 1, '1': 1, '2': -1, '3': 1}, 'cost': -32.0, 'parameters':
{'all_betas': [0.1,0.5,1,2,4], 'replicas': 70, 'sweeps': 600}}
Gerenciamento de trabalho
28/04/2021 • 2 minutes to read

Quando um problema é enviado a um solucionador, um Job é criado no Azure Quantum. O Workspace fornece
os seguintes métodos para gerenciar trabalhos:
get_job : retorna os Job metadados e os resultados de um trabalho específico (com base no trabalho id ).
list_jobs : retorna uma lista de todos os trabalhos no espaço de trabalho.
cancel_job : cancela um trabalho específico.
Consulte Cancelamento de trabalho para obter mais informações sobre como as solicitações de cancelamento
são processadas.
Você pode usar o método list_jobs para obter uma lista de todos os trabalhos no espaço de trabalho:

jobs = [job.id for job in workspace.list_jobs()]


print(jobs)

> ['5d2f9cd70f55f149e3ed3aef', '23as12fs5d2f9cd70f55f', '1644428ea8507edb7361']

Isso mostra como enviar um trabalho de forma assíncrona e chamar get_job para obter os metadados (e os
resultados) de um trabalho enviado anteriormente, por id :

from azure.quantum.optimization import Problem, ProblemType, Term, ParallelTempering, SimulatedAnnealing

problem = Problem(name="MyOptimizationJob", problem_type=ProblemType.ising)


problem.add_term(c=-9, indices=[0])
problem.add_term(c=-3, indices=[1,0])
problem.add_term(c=5, indices=[2,0])

solver = SimulatedAnnealing(workspace)
job = solver.submit(problem)
print(job.id)

> 5d2f9cd70f55f149e3ed3aef

job = workspace.get_job(job.id)
results = job.get_results()
print(results)

> {'configuration': {'0': 1, '1': 1, '2': -1}, 'cost': -17.0}


Reutilizando as definições de problema
01/05/2021 • 2 minutes to read

Às vezes, é mais eficiente carregar uma definição de problema uma vez e encontrar sua solução usando
algoritmos diferentes (solucionadores) ou com parâmetros diferentes. É possível carregar uma definição de
problema usando o método upload , que retorna uma URL e, em seguida, fornecer essa URL para um
solucionador submit ou métodos de otimização:

url = problem.upload(workspace)
job = solver.submit(url)
print(job.id)

> 9228ea88-6832-11ea-8271-c49dede60d7c
Resolver problemas de execução prolongada
01/05/2021 • 2 minutes to read

No exemplo em Aplicar solucionadores para resolver problemas de otimização, um problema foi enviado para o
Azure Quantum e resolvido de forma síncrona. Isso é conveniente para determinados ambientes, mas não é
adequado para outros em que há a necessidade de enviar um problema e verificá-lo mais tarde, ou enviar
muitos problemas e comparar os resultados.
Enviar o problema
Para enviar um problema de forma assíncrona, use o submit método no solver . Isso envia um Job que é
retornado pelo método:

solver = ParallelTempering(workspace, timeout=100, seed=11)


job = solver.submit(problem)
print(job.id)

> ea81bb40-682f-11ea-8271-c49dede60d7c

Atualizar status do trabalho


Depois de enviar o trabalho, você pode verificar o status do trabalho chamando o refresh método. Cada vez
que o refresh é chamado, os metadados do trabalho são atualizados.

job.refresh()
print(job.details.status)

> Succeeded

Obter a saída do trabalho


Depois que o trabalho estiver em um estado final, como Succeeded , você pode baixar a saída do trabalho
usando get_results :

jobId = "ea81bb40-682f-11ea-8271-c49dede60d7c"
job = workspace.get_job(jobId)
result = job.get_results()
print(result)

> {'configuration': {'0': 1, '1': 1, '2': -1, '3': 1}, 'cost': -32.0}
Usando uma entidade de serviço para autenticar
30/04/2021 • 2 minutes to read

Às vezes, não é adequado usar a autenticação interativa ou autenticar como uma conta de usuário. Esses casos
podem surgir quando você quer enviar trabalhos de um serviço Web, de outra função de trabalho ou de um
sistema automatizado. Nesse caso, normalmente você quer autenticar usando uma entidade de serviço.

Pré-requisito: criar uma entidade de serviço e um segredo do


aplicativo
Para autenticar como uma entidade de serviço, você deve primeiro criar uma entidade de serviço.
Para criar uma entidade de serviço, atribua acesso e gere uma credencial:
1. Criar um aplicativo do Azure Active Directory:

NOTE
Não é preciso definir um URI de redirecionamento

a. Depois de criadas, anote a ID do aplicativo (cliente) e a ID do diretório (locatário) .


2. Crie uma credencial para fazer logon como o aplicativo:
a. Nas configurações do aplicativo, escolha Cer tificados e segredos .
b. Em Segredos do cliente , escolha Criar novo segredo .
c. Informe uma descrição e uma duração e escolha Adicionar .
d. Copie o valor do segredo para um local seguro imediatamente. Você não conseguirá vê-lo
novamente!
3. Dê permissões à sua entidade de serviço para acessar seu workspace:
a. Abra o Portal do Azure.
b. Na barra de pesquisa, insira o nome do grupo de recursos no qual você criou o workspace. Selecione
o grupo de recursos quando ele surgir nos resultados.
c. No menu do grupo de recursos, escolha Controle de acesso (IAM) .
d. Clique em Adicionar atribuição de função .
e. Procure e escolha a entidade de serviço.
f. Atribua a função Colaborador ou Proprietário .

Autenticar como a entidade de serviço


Etapa 1 : instalar o pacote azure-common do Python:

pip3 install azure-common

Etapa 2 : antes de chamar workspace.login() , crie uma instância da entidade de serviço e forneça-a ao
workspace:
from azure.common.credentials import ServicePrincipalCredentials
workspace.credentials = ServicePrincipalCredentials(
tenant = "", # From service principal creation, your Directory (tenant) ID
client_id = "", # From service principal creation, your Application (client) ID
secret = "", # From service principal creation, your secret
resource = "https://quantum.microsoft.com" # Do not change! This is the resource you want to
authenticate against - the Azure Quantum service
)

É isso! Chame workspace.login() depois de configurar a entidade de serviço, e você poderá criar trabalhos
como de costume.

NOTE
Chamar workspace.login(refresh=True) limpará a propriedade workspace.credentials e forçará uma nova autenticação
de dispositivo interativo. As credenciais foram definidas no workspace. As credenciais serão perdidas, incluindo
ServicePrincipalCredentials.
O que é computação quântica?
15/04/2021 • 4 minutes to read

Aproveitando o comportamento exclusivo da física quântica e aplicando-o à computação, os computadores


quânticos apresentam novos conceitos aos métodos tradicionais de programação e usam comportamentos da
física quântica, como superposição, emaranhamento e interferência quântica.
A computação quântica traz consigo a promessa de resolver alguns dos maiores desafios do nosso planeta, nas
áreas de meio ambiente, agricultura, saúde, energia, clima, ciência de materiais e outras ainda não encontradas.
Para alguns desses problemas, mesmo os computadores mais avançados apresentam problemas. Embora a
tecnologia quântica esteja apenas começando a impactar o mundo da computação, ela pode ter um amplo
alcance e mudar a maneira como pensamos sobre a computação.
No uso moderno, a palavra quantum significa a menor unidade discreta possível de qualquer propriedade física,
geralmente referindo-se às propriedades de partículas atômicas ou subatômicas. Os computadores quânticos
usam partículas quânticas reais, átomos artificiais ou propriedades coletivas de partículas quânticas como
unidades de processamento e são dispositivos grandes, complexos e caros.

O que um computador quântico pode fazer?


Um computador quântico não é um supercomputador que pode fazer tudo mais rapidamente, mas há algumas
áreas em que os computadores quânticos apresentam excelente desempenho.
Simulação quântica
Criptografia
Pesquisar
Otimização
Aprendizado de máquina

Simulação quântica
Como os computadores quânticos usam os fenômenos quânticos na computação, eles são adequados para
modelar outros sistemas quânticos. Fotossíntese, supercondutividade e formações moleculares complexas são
exemplos de mecanismos quânticos que podem ser simulados pelos programas quânticos.

Criptografia e algoritmo de Shor


Em 1994, Peter Shor demonstrou que um computador quântico escalonável pode decifrar técnicas de
criptografia amplamente usadas, como o algoritmo RSA. A criptografia clássica depende da intratabilidade de
problemas como fatoração de inteiros ou logaritmos discretos, muitos dos quais podem ser resolvidos com
mais eficiência por meio de computadores quânticos.

Pesquisa e algoritmo de Grover


Em 1996, Lov Grover desenvolveu um algoritmo quântico que acelera drasticamente a solução para pesquisas
de dados não estruturados, executando a pesquisa em menos etapas do que qualquer algoritmo clássico.

Computação e otimização inspiradas no quantum


Os algoritmos inspirados no quantum usam princípios quânticos para aumentar a velocidade e a precisão, mas
para implementar em sistemas de computadores clássicos. Essa abordagem permite que os desenvolvedores
aproveitem hoje mesmo o poder das novas técnicas quânticas sem esperar pelo hardware quântico, que ainda é
um setor emergente.
A otimização é o processo de encontrar a melhor solução para um problema, considerando o resultado
desejado e as restrições. Fatores como custo, qualidade ou tempo de produção são todos incluídos nas decisões
críticas tomadas pela indústria e pela ciência. Os algoritmos de otimização inspirados no quantum em execução
nos computadores clássicos de hoje podem encontrar soluções que até agora não foram possíveis. Além de
otimizar o fluxo de tráfego para reduzir o congestionamento, há a atribuição de portões de avião, entrega de
pacotes, agendamento de trabalhos, entre outros. Com as inovações em ciência de materiais, haverá novas
formas de energia, baterias com maior capacidade, bem como materiais mais leves e mais duráveis.

NOTE
Leia mais sobre como a computação inspirada no quantum da Microsoft está sendo usada na ciência de materiais, no
gerenciamento de riscos e na medicina.

Machine learning quântico


O machine learning em computadores clássicos está revolucionando o mundo da ciência e dos negócios. No
entanto, o alto custo computacional do treinamento dos modelos impede o desenvolvimento e o escopo do
campo. A área de machine learning quântico explora como desenvolver e implementar um software quântico
que permita um machine learning executado mais rapidamente comparado aos computadores clássicos.
O Quantum Development Kit é fornecido com a biblioteca de machine learning quântico, que permite executar
experimentos híbridos de machine learning quântico/clássico. A biblioteca inclui amostras e tutoriais e fornece
as ferramentas necessárias para implementar um novo algoritmo quântico-clássico híbrido, o classificador
quântico centrado em circuito, para resolver problemas de classificação supervisionados.

Q# e o Microsoft QDK (Quantum Development Kit)


O Q# é a linguagem de programação de software livre da Microsoft para o desenvolvimento e a execução de
algoritmos quânticos. Ele faz parte do QDK, um kit de desenvolvimento completo para o Q#, que pode ser
usado com ferramentas e linguagens padrão para desenvolver aplicativos quânticos que podem ser executados
em vários ambientes, incluindo o simulador quântico completo de estado integral interno.
Há extensões para o Visual Studio e o VS Code, além de pacotes para uso com o Python e o Jupyter Notebook.
O QDK inclui uma biblioteca padrão juntamente com bibliotecas especializadas de química, machine learning e
numéricas.
A documentação inclui um guia da linguagem Q#, tutoriais e um código de exemplo para uma introdução
rápida, bem como artigos avançados para ajudar você a se aprofundar nos conceitos de computação quântica.

Parceiros de hardware quântico da Microsoft


A Microsoft está fazendo uma parceria com empresas de hardware quântico para fornecer aos desenvolvedores
o acesso à nuvem ao hardware quântico. Com a plataforma Azure Quantum e a linguagem Q#, os
desenvolvedores poderão explorar algoritmos quânticos e executar os programas quânticos em diferentes tipos
de hardware quântico.
A IonQ e a Honeywell usam processadores baseados em íon capturados , utilizando íons individuais
capturados em um campo eletrônico, enquanto a QCI usa circuitos supercondutores.
Próximas etapas
Principais conceitos da computação quântica Início rápido
Introdução à otimização
01/05/2021 • 2 minutes to read

De maneira simples, a otimização é apenas o processo de seleção da melhor escolha de um conjunto de opções
possíveis.
Podemos definir o melhor de várias maneiras: pode ser a opção com o menor custo, o tempo de execução mais
rápido ou talvez o menor impacto ambiental. Geralmente, apenas nos referimos a essa definição como a melhor
forma de um custo a ser minimizado. Se quiséssemos maximizar o custo em vez disso (por exemplo, maximizar
a saída de energia de uma célula solar), tudo o que precisamos fazer é multiplicar o custo por um negativo e, em
seguida, minimizá-lo.
Frequentemente, quando dizemos que temos um problema de otimização, queremos dizer que temos um
problema complexo, em que muitas variáveis podem interagir de várias maneiras para influenciar o custo final.
Chamamos uma determinada organização das variáveis da configuração do problema.
Como há tantas configurações possíveis para escolher, às vezes é muito difícil identificar a melhor solução,
especialmente quando o espaço do problema é muito grande. É fácil ficar preso em um local optimum. Alguns
exemplos de local optimum são mostrados no gráfico abaixo, bem como a otimização global: a configuração de
menor custo que nosso sistema pode adotar.

A maneira como o custo varia conforme uma função da configuração do sistema nos dá o que chamamos de
função de custo, a linha rastreada em nosso grafo. O objetivo da nossa otimização é encontrar o ponto mínimo
nessa função de custo (ou o mais próximo possível do ponto mínimo, devido à quantidade razoável de tempo).
Vamos ilustrar isso com um exemplo: minimização de tráfego. O objetivo dessa tarefa de otimização é reduzir o
congestionamento em um sistema de estrada para reduzir a quantidade de tempo que os usuários passam
aguardando no tráfego.
Cada configuração representa uma combinação diferente de rotas atribuídas aos veículos no sistema. O custo é
o nível de tráfego geral (ou nível de congestionamento), que é o que desejamos minimizar.
O grafo acima realça alguns exemplos de configurações de sistema diferentes, cada uma com um valor de custo
diferente. Nós visualizamos o custo aqui usando a cor: a mais vermelha do segmento de estrada, maior o nível
de tráfego e, portanto, quanto maior o custo. Por outro lado, segmentos de estrada mais verdes têm menos
veículos que os ocupam simultaneamente e, portanto, reduzem o tráfego e os valores de custo.
Noções básicas sobre a computação quântica
19/05/2021 • 5 minutes to read

A computação quântica usa os princípios da mecânica quântica para processar informações. Por isso, a
computação quântica exige uma abordagem diferente da computação clássica. Um exemplo dessa diferença é o
processador usado em computadores quânticos. Enquanto os computadores clássicos usam chips conhecidos à
base de silício, os computadores quânticos usam sistemas quânticos, como átomos, íons, fótons ou elétrons.
Eles usam suas respectivas propriedades quânticas para representar bits que podem ser preparados em
diferentes superposições quânticas de 1 e 0.
O material quântico se comporta de acordo com as leis da mecânica quântica, utilizando conceitos como
computação probabilística, superposição e emaranhamento. Esses conceitos fornecem a base para algoritmos
quânticos que aproveitam o poder da computação quântica para resolver problemas complexos. Este artigo
descreve alguns dos conceitos essenciais da mecânica quântica na qual se baseia a computação quântica.

Uma visão geral da mecânica quântica


A mecânica quântica, também chamada de teoria quântica, é uma ramificação da física que lida com partículas
nos níveis atômico e subatômico. No entanto, no nível quântico, muitas das leis da mecânica amplamente
aceitas não se aplicam. Superposição, medida quântica e emaranhamento são três fenômenos que são
fundamentais para a computação quântica.

Superposição vs. computação binária


Imagine que você esteja fazendo exercícios físicos na sua sala de estar. Você gira todo o corpo para a esquerda e,
em seguida, para a direita. Agora, você gira para a esquerda e para a direita ao mesmo tempo. Você não pode
fazer isso (sem se dividir em dois, pelo menos). Obviamente, você não pode estar nesses dois estados ao
mesmo tempo; não é possível estar voltado para a esquerda e para a direita ao mesmo tempo.
No entanto, se você for uma partícula quântica, poderá haver uma certa probabilidade de estar voltado para a
esquerda E uma certa probabilidade de estar voltado para a direita, devido a um fenômeno conhecido como
superposição (também conhecido como coerência ).
Uma partícula quântica, como um elétron, tem as próprias propriedades “voltadas para a esquerda ou para a
direita”, por exemplo, o giro , conhecido como para cima ou para baixo, ou para tornar isso mais relacionável à
computação binária clássica, digamos apenas 1 ou 0. Quando uma partícula quântica está em um estado de
superposição, ela é uma combinação linear de um número infinito de estados entre 1 e 0, mas você não sabe
em qual deles ela estará até que realmente a examine, o que apresenta o nosso próximo fenômeno, a medida
quântica .

Medida quântica
Agora, digamos que o seu amigo venha até a sua casa e queira tirar uma foto de você se exercitando. É muito
provável que ele obtenha uma imagem desfocada sua girando em algum ponto para a esquerda e para a direita.
Mas se você for uma partícula quântica, acontecerá uma coisa interessante. Não importa onde você esteja
quando ele tirar a foto, a foto sempre mostrará você voltado para a esquerda ou para a direita (em nenhum
ponto intermediário).
Isso ocorre porque o ato de observar ou de medir uma partícula quântica causa um colapso no estado de
superposição (também conhecido como decoerência ), e a partícula assume um estado binário clássico de 1 ou
0.
Esse estado binário é útil para nós, pois, na computação, você pode fazer muitas coisas com 1 e 0. No entanto,
depois que uma partícula quântica é medida e entra em colapso, ela permanece nesse estado para sempre
(assim como a sua imagem) e sempre será um 1 ou um 0. Como você verá mais adiante, na computação
quântica, há operações que podem “restaurar” uma partícula para um estado de superposição, de modo que ela
possa ser usada em cálculos quânticos novamente.

Emaranhamento
Possivelmente, o fenômeno mais interessante da mecânica quântica é a capacidade de duas ou mais partículas
quânticas se emaranharem . Quando as partículas se emaranham, elas formam um só sistema, de tal modo
que o estado quântico de qualquer partícula não possa ser descrito de maneira independente do estado
quântico das outras partículas. Isso significa que qualquer operação ou processo que você aplicar a uma
partícula correlacionará às outras partículas também.
Além dessa interdependência, as partículas podem manter essa conexão mesmo quando estão separadas por
distâncias incrivelmente grandes, até mesmo em anos-luz. Os efeitos da medida quântica também se aplicam a
partículas emaranhadas, de tal modo que quando uma partícula é medida e entra em colapso, a outra partícula
também entra em colapso. Como há uma correlação entre os qubits emaranhados, a medição do estado de um
qubit fornece informações sobre o estado do outro qubit; essa propriedade específica é muito útil na
computação quântica.

Qubits e probabilidade
Os computadores clássicos armazenam e processam informações em bits, que podem ter um estado igual a 1
ou 0, mas nunca os dois. O equivalente na computação quântica é o qubit , que representa o estado de uma
partícula quântica. Devido à superposição, os qubits podem ser 1 ou 0 ou qualquer valor entre os dois.
Dependendo da configuração dele, um qubit tem uma certa probabilidade de sofrer um colapso para 1 ou 0. A
probabilidade de o qubit sofrer um colapso para uma forma ou outra é determinada pela interferência
quântica .
Lembra-se daquele amigo que estava tirando uma foto sua? Suponha que ele tenha filtros especiais na câmera
chamados filtros de interferência. Se ele selecionar o filtro 70/30 e começar a tirar fotos, em 70% delas, você
estará voltado para a esquerda e, em 30%, estará voltado para a direita. O filtro interferiu no estado normal da
câmera para influenciar a probabilidade do comportamento dela.
Da mesma forma, a interferência quântica afeta o estado de um qubit para influenciar a probabilidade de
determinado resultado durante a medição. Esse estado probabilístico é o aspecto no qual se destaca a eficiência
da computação quântica.
Por exemplo, com dois bits em um computador clássico, cada bit pode armazenar 1 ou 0 e, portanto, juntos,
você pode armazenar quatro valores possíveis, 00 , 01 , 10 e 11 , mas apenas um deles de cada vez. Com dois
qubits em superposição, no entanto, cada qubit pode ser 1 ou 0 ou ambos, de modo que você possa representar
os mesmos quatro valores simultaneamente. Com três qubits, você pode representar oito valores; com quatro
qubits, você pode representar 16 valores etc.

Resumo
Esses conceitos simplesmente são a superfície da mecânica quântica, mas são conceitos fundamentalmente
importantes a saber para a computação quântica.
Superposição : a capacidade de as partículas quânticas serem uma combinação de todos os estados
possíveis.
Medida quântica : o ato de observar uma partícula quântica em superposição e resultar em um dos estados
possíveis.
Emaranhamento : a capacidade de as partículas quânticas correlacionarem os respectivos resultados de
medição entre si.
Qubit : a unidade básica de informações na computação quântica. Um qubit representa uma partícula
quântica na superposição de todos os estados possíveis.
Interferência : comportamento intrínseco de um qubit devido à superposição para influenciar a
probabilidade de que ele entre em colapso para uma forma ou outra.

Próximas etapas
Computadores e simuladores quânticos
Computadores e simuladores quantum
19/05/2021 • 3 minutes to read

Os computadores quânticos ainda estão no início do desenvolvimento. O hardware e a manutenção são caros e
a maioria dos sistemas está localizada em universidades e laboratórios de pesquisa. No entanto, a tecnologia
está avançando e o acesso público limitado a alguns sistemas está disponível.
Os simuladores quânticos são programas de software executados em computadores clássicos e possibilitam
executar e testar programas quânticos em um ambiente que prevê como os qubits reagirão a diferentes
operações.

Hardware quântico
Um computador quântico é formado por três partes principais: uma área que hospeda os qubits, um método
para transferir sinais para os qubits e um computador clássico para executar um programa e enviar instruções.
O material quântico usado para os qubits é frágil e altamente sensível a interferências ambientais. Para
alguns métodos de armazenamento de qubits, a unidade que hospeda os qubits é mantida em uma
temperatura logo acima de zero absoluto para maximizar a coerência. Outros tipos de hospedagem de
qubits usam uma câmara de vácuo para ajudar a minimizar as vibrações e estabilizar os qubits.
Os sinais podem ser enviados para os qubits por meio de uma variedade de métodos, incluindo micro-
ondas, laser e voltagem.
Os computadores quânticos enfrentam uma infinidade de desafios para funcionarem corretamente. A correção
de erros em computadores quânticos é um problema significativo e o dimensionamento (adição de mais qubits)
aumenta a taxa de erros. Devido a essas limitações, um computador quântico da área de trabalho está distante
no futuro, mas um computador quântico comercialmente viável baseado em laboratório está mais próximo.

Simuladores quânticos
Os simuladores quânticos executados em computadores clássicos permitem simular a execução de algoritmos
quânticos em um sistema quântico. O QDK (Quantum Development Kit) da Microsoft inclui um simulador de
vetor de estado completo junto com outros simuladores quânticos especializados.

Qubit topológico
A Microsoft está desenvolvendo um computador quântico baseado em qubits topológicos. Um qubit topológico
será menos afetado pelas alterações no ambiente, reduzindo o grau de correção externa de erros necessário.
O recurso de qubits topológicos aumentou a estabilidade e a resistência ao ruído ambiental, o que significa que
eles podem ser dimensionados com mais facilidade e permanecer confiáveis por mais tempo.

Parcerias da Microsoft com fabricantes de hardware quântico


A Microsoft está fazendo uma parceria com os fabricantes de hardware quântico IonQ, Honeywell e QCI para
tornar os computadores quânticos acessíveis aos desenvolvedores no futuro. Com a plataforma Azure
Quantum, os desenvolvedores podem usar o QDK (Quantum development kit) da Microsoft e o Q# para
escrever programas quânticos e executá-los remotamente.

Cálculos quânticos
Realizar cálculos em um computador quântico ou um simulador quântico segue um processo básico:
Acessar os qubits
Inicializar os qubits no estado desejado
Executar operações para transformar os estados dos qubits
Medir os novos estados dos qubits
A inicialização e a transformação de qubits são feitas por meio de operações quânticas (às vezes chamadas
de portas quânticas). As operações quânticas são semelhantes às operações lógicas na computação clássica,
como AND, OR, NOT e XOR. Uma operação pode ser tão básica quanto a inversão de um estado de qubit de 1
para 0 ou o emaranhamento um par de qubits e tão complexa quanto o uso de várias operações em série para
afetar a probabilidade de colapso de um qubit superposto de uma forma ou de outra.

NOTE
As bibliotecas Q# fornecem operações internas que definem combinações complexas de operações quânticas de nível
inferior. Use as operações da biblioteca para transformar qubits e criar operações mais complexas definidas pelo usuário.

A medição do resultado do cálculo nos informa uma resposta, mas em alguns algoritmos quânticos, não
necessariamente a resposta correta. Como o resultado de alguns algoritmos quânticos é baseado na
probabilidade que foi configurada pelas operações quânticas, esses cálculos são executados várias vezes para
obter uma distribuição de probabilidade e refinar a precisão dos resultados. A garantia de que uma operação
retornou uma resposta correta é conhecida como verificação quântica e é um desafio significativo na
computação quântica.

Resumo
A computação quântica compartilha alguns dos mesmos conceitos da computação clássica, mas adiciona alguns
novos ajustes. Estas são algumas das principais considerações:
O hardware quântico é caro e frágil para o trabalho e, portanto, os simuladores quânticos são usados para
escrever e testar programas.
Os computadores clássicos e quânticos usam operações lógicas (ou portas) para preparar cálculos.
Os cálculos quânticos retornam probabilidades.
Os avanços em técnicas e hardware quânticos estão mudando rapidamente o campo. Estes são apenas alguns
dos desenvolvimento atuais.

Próximas etapas
O que são a linguagem de programação Q# e o QDK?
O que é a linguagem de programação Q# e o QDK
(Quantum development kit)?
19/05/2021 • 3 minutes to read

O Q# é a linguagem de programação de software livre da Microsoft para o desenvolvimento e a execução de


algoritmos quânticos. Ele faz parte do QDK (Quantum Development Kit), que inclui bibliotecas Q#, simuladores
quânticos, extensões para outros ambientes de programação e documentação da API. Além da biblioteca padrão
do Q#, o QDK inclui bibliotecas de Química, Machine Learning e Numéricas.
Como uma linguagem de programação, o Q# extrai elementos conhecidos do Python, do C# e do F# e é
compatível com um modelo de procedimento básico para escrever programas com loops, instruções if/then e
tipos de dados comuns. Ele também apresenta novas operações e estruturas de dados específicas do quantum.

O que posso fazer com o QDK?


O QDK é um kit de desenvolvimento completo para Q#, que pode ser usado com ferramentas e linguagens
comuns para desenvolver aplicativos quânticos que podem ser executados em vários ambientes. Os programas
Q# podem ser executados como um aplicativo de console, por meio de Jupyter Notebooks, ou usar um
programa de host do Python ou do .NET.
Desenvolvimento em ferramentas e ambientes comuns
Integre seu desenvolvimento quântico ao Visual Studio, ao Visual Studio Code e aos Jupyter Notebooks. Use as
APIs internas para emparelhar seus programas com as linguagens de host Python e .NET.
Experimentar as operações quânticas e as bibliotecas específicas do domínio
Escreva e teste algoritmos quânticos para explorar a superposição, o emaranhamento e outras operações
quânticas. As bibliotecas Q# permitem executar operações quânticas complexas sem precisar criar sequências
de operação de nível inferior.
Executar programas em simuladores
Execute seus programas quânticos em um simulador quântico de estado completo, um simulador de Toffoli de
escopo limitado ou teste seu código Q# em diferentes avaliadores de recursos.

Onde posso saber mais?

Não estou familiarizado com a computação quântica Examine algumas noções básicas de física e computação
quântica em Principais conceitos.

Quero saber mais sobre a linguagem Q# Explore os tipos, as expressões, as variáveis e a estrutura do
programa quântico no Guia do Usuário do Q#.

Quero apenas começar a escrever programas Configure seu ambiente Q# e comece a escrever programas
quânticos quânticos em Inícios Rápidos.

Como funciona o Q#?


Um programa Q# pode ser compilado em um aplicativo autônomo ou chamado por um programa de host
escrito em Python ou em uma linguagem .NET.
Quando você compila e executa o programa, ele cria uma instância do simulador quântico e transmite o código
Q# para ele. O simulador usa o código Q# para criar qubits (simulações de partículas quânticas) e aplicar
transformações para modificar o estado deles. Os resultados das operações quânticas no simulador são
retornados para o programa.
O isolamento do código Q# no simulador garante que os algoritmos sigam as leis da física quântica e possam
ser executados corretamente em computadores quânticos.

Como usar o QDK?


Tudo o que você precisa para escrever e executar programas Q#, incluindo o compilador Q#, as bibliotecas Q# e
os simuladores quânticos, pode ser instalado e executado no computador local. Por fim, você poderá executar
seus programas Q# remotamente em um computador quântico real, mas até lá, os simuladores quânticos
fornecidos com o QDK fornecem resultados precisos e confiáveis.
Desenvolver aplicativos Q# é a maneira mais rápida de começar.
Execute Jupyter Notebooks autônomos com o IQ#, uma extensão do Jupyter para compilar, simular e
visualizar programas Q#.
Caso esteja familiarizado com o Python, use-o como uma plataforma de programação de host para
começar a usá-lo. O Python é famoso pelo uso difundido não apenas entre desenvolvedores, mas
também entre cientistas, pesquisadores e professores.
Caso já tenha experiência com o C#, o F# ou o VB.NET e conheça com o ambiente de desenvolvimento do
Visual Studio, há apenas algumas extensões que você precisa adicionar ao Visual Studio para prepará-lo
para o Q#.

Resumo
O Q# é uma linguagem de programação de software livre para o desenvolvimento de programas quânticos. Ele
conta com bibliotecas que permitem criar operações quânticas complexas e simuladores quânticos para
executar e testar seus programas com precisão. Os programas Q# podem ser executados como aplicativos
autônomos ou chamados em um programa de host do Python ou do .NET e podem ser escritos, executados e
testados no computador local.

Próximas etapas
Álgebra linear para computação quântica
Álgebra linear para computação quântica
19/05/2021 • 4 minutes to read

A álgebra linear é a linguagem da computação quântica. Embora você não precise conhecê-la para implementar
ou escrever programas quânticos, ela é amplamente usada para descrever os estados de qubits e as operações
quânticas, além de prever o que um computador quântico faz em resposta a uma sequência de instruções.
Assim como a familiaridade com os conceitos básicos de física quântica pode ajudar você a entender a
computação quântica, um conhecimento básico de álgebra linear pode ajudar você a entender como funcionam
os algoritmos quânticos. No mínimo, o ideal é estar familiarizado com os vetores e a multiplicação de
matrizes . Caso você precise relembrar esses conceitos de álgebra, estes são alguns tutoriais que abordam os
princípios básicos:
Tutorial do Jupyter Notebook sobre álgebra linear
Tutorial do Jupyter Notebook sobre aritmética complexa
Álgebra linear para computação quântica
Conceitos básicos de álgebra linear
Primer de computação quântica

Vetores e matrizes na computação quântica


No tópico Noções básicas sobre computação quântica, você viu que um qubit pode estar em um estado igual a
1 ou 0, uma superposição ou ambos. Usando a álgebra linear, o estado de um qubit é descrito como um vetor e
é representado por uma só coluna matriz $ \begin{bmatrix} a \\ b \end{bmatrix} $. Ele também é conhecido
como um vetor de estado quântico e precisa atender ao requisito de $|a|^2 + |b|^2 = 1$.
Os elementos da matriz representam a probabilidade de o qubit entrar em colapso para uma forma ou outra,
com $|a|^2$ sendo a probabilidade de um colapso para zero e $|b|^2$, a probabilidade de um colapso para
um. Todas as seguintes matrizes representam vetores de estado quântico válidos:
$$\begin{bmatrix} 1 \\ 0 \end{bmatrix}, \begin{bmatrix} 0 \\ 1 \end{bmatrix}, \begin{bmatrix} \frac{1}{\sqrt{2}} \\
\frac{1}{\sqrt{2}} \end{bmatrix}, \begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{-1}{\sqrt{2}} \end{bmatrix}, \text{ and
}\begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{i}{\sqrt{2}} \end{bmatrix}.$$
Em Computadores e simuladores quânticos, você também viu que as operações quânticas são usadas para
modificar o estado de um qubit. As operações quânticas também podem ser representadas por uma matriz.
Quando uma operação quântica é aplicada a um qubit, as duas matrizes que os representam são multiplicadas e
a resposta resultante representa o novo estado do qubit após a operação.
Veja a seguir duas operações quânticas comuns representadas com a multiplicação de matrizes.
A operação X é representada pela matriz de Pauli $X$,
$$X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix},$$
e é usada para inverter o estado de um qubit de 0 para 1 (ou vice-versa), por exemplo
$$\begin{bmatrix}0 &1\\ 1 &0\end{bmatrix}\begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\ 1
\end{bmatrix}.$$
A operação 'H' é representada pela transformação de Hadamard $H$,
$$H = \dfrac{1}{\sqrt{2}}\begin{bmatrix}1 &1\\ 1 &-1\end{bmatrix},$$
e coloca um qubit em um estado de superposição no qual ele tem uma probabilidade igual de colapso para uma
das formas, conforme mostrado aqui
$$\frac{1}{\sqrt{2}}\begin{bmatrix}1 &1\\ 1 &-1\end{bmatrix}\begin{bmatrix} 1 \\ 0 \end{bmatrix}=\frac{1}
{\sqrt{2}}\begin{bmatrix} 1 \\ 1 \end{bmatrix}.$$
Observe que $|a|^2 =|b|^2 = \frac{1}{2}$, o que significa que a probabilidade de recolher para os estados zero
e um é a mesma.
Uma matriz que representa uma operação quântica tem um requisito: ela precisa ser uma matriz unitária . Uma
matriz será unitária se o inverso da matriz for igual ao conjugado transposto da matriz.

Como representar os estados de dois qubits


Nos exemplos acima, o estado de um qubit foi descrito usando uma matriz de coluna única $\begin{bmatrix} a
\\ b \end{bmatrix}$ e a aplicação de uma operação a ela foi descrita multiplicando as duas matrizes. No entanto,
os computadores quânticos usam mais de um qubit, portanto, como você descreve o estado combinado de dois
qubits?
Lembre-se de que cada qubit é um espaço vetorial, portanto, eles não podem apenas ser multiplicados. Em vez
disso, você usa um produto tensorial , uma operação relacionada que cria um espaço vetorial com base em
espaços vetoriais individuais e é representado pelo símbolo $\otimes$. Por exemplo, o produto tensorial de dois
estados de qubits $\begin{bmatrix} a \\ b \end{bmatrix}$ e $\begin{bmatrix} c \\ d \end{bmatrix}$ é calculado
$$ \begin{bmatrix} a \\ b \end{bmatrix} \otimes \begin{bmatrix} c \\ d \end{bmatrix} =\begin{bmatrix} a
\begin{bmatrix} c \\ d \end{bmatrix} \\ b \begin{bmatrix}c \\ d \end{bmatrix} \end{bmatrix} = \begin{bmatrix} ac
\\ ad \\ bc \\ bd \end{bmatrix}. $$
O resultado é uma matriz de quatro dimensões, com cada elemento representando uma probabilidade. Por
exemplo, $ac$ é a probabilidade de que os dois qubits sofram um colapso para 0 e 0, $ad$ é a probabilidade de
0 e 1 etc.
Assim como apenas um estado de qubit $\begin{bmatrix} a \\ b \end{bmatrix}$ deve atender ao requisito $|a|^2
+ |b|^2 = 1$ para representar um estado quântico, um estado de dois qubits $\begin{bmatrix} ac \\ ad \\ bc \\
bd \end{bmatrix}$ deve atender ao requisito $|ac|^2 + |ad|^2 + |bc|^2+ |bd|^2 = 1$.

Resumo
A álgebra linear é a linguagem padrão para descrever a computação e a física quântica. Embora as bibliotecas
incluídas no Microsoft Quantum Development Kit ajudem você a executar algoritmos quânticos avançados sem
se aprofundar na matemática subjacente, entender as noções básicas ajudará você a começar a usá-los
rapidamente e a fornecerá uma base sólida para o desenvolvimento.

Próximas etapas
Instalar o QDK
O que são algoritmos inspirados em quantum?
19/05/2021 • 2 minutes to read

Há muitos tipos de algoritmos inspirados em quantum. Um algoritmo inspirado em quantum frequentemente


utilizado é baseado em um modelo computacional chamado computação quântica adiabática. Essa abordagem
usa um conceito da física quântica conhecido como teorema adiabático. Ao aplicar esse teorema para resolver
um problema, você:
Primeiro, prepare um sistema e inicialize-o para o estado de energia mais baixo dele. Para um sistema
simples, um que compreendemos totalmente, isso é fácil de fazer.
Em seguida, transforme lentamente o sistema em um mais complexo que descreve o problema que você
está tentando resolver. O teorema adiabático afirma que, desde que essa transformação ocorra de modo
suficientemente lento, o sistema terá tempo para se adaptar e permanecerá na configuração de energia mais
baixa. Quando terminarmos com nossas transformações, teremos resolvido nosso problema.
Uma boa analogia para isso é imaginar que você tenha um vidro de água. Se você mover esse vidro lentamente
por uma mesa, o conteúdo não será despejado, pois o sistema tem tempo para se adaptar à sua nova
configuração. Se movesse o vidro rapidamente, no entanto, o sistema seria forçado a mudar rapidamente,
derramando água por todo o recinto.
A computação quântica adiabática é uma área de pesquisa ativa que já está sendo usada na indústria. Várias
técnicas foram desenvolvidas para simular esse tipo de física. Esses tipos de algoritmos clássicos, que podemos
executar em computadores clássicos atualmente, também são conhecidos como otimização inspirada em
quantum.

O que é a QIO (otimização inspirada em quantum)?


Problemas de otimização são encontrados em todos os setores, como fabricação, finanças e transporte. Na
verdade, os setores, como logística, são dedicados inteiramente a resolver problemas de otimização. Para
resolver esses problemas, pesquisamos entre as soluções viáveis. A melhor solução é aquela com o menor
custo. Os algoritmos quânticos adiabáticos são adequados para resolver muitos problemas de otimização.
Atualmente, podemos emular algoritmos quânticos adiabáticos usando técnicas inspiradas em quantum no
hardware clássico, uma abordagem conhecida como QIO (otimização inspirada em quantum). Essas técnicas
muitas vezes têm desempenho melhor do que as técnicas de otimização clássica de última geração.
Aplicar QIO a problemas do mundo real pode oferecer aos negócios insights novos ou ajudar a reduzir os
custos, tornando os processos deles mais eficientes. O QIO nos dá a oportunidade de:
Encontrar uma solução mais rápida do que outras técnicas de otimização para um caso de uso fixo e uma
qualidade de solução fixa.
Encontre uma solução de qualidade mais alta do que outras técnicas de otimização para um problema fixo e
uma quantidade fixa de tempo.
Use um modelo mais realista do que outras técnicas de otimização, estendendo o problema para considerar
mais variáveis.
Como os métodos QIO são heurísticos, não há garantia de que eles encontrem a solução ideal. Além disso, eles
nem sempre superam outras técnicas de otimização. Na realidade, isso depende do problema. Descobrir o que
faz com que a QIO tenha um desempenho melhor do que outros métodos em algumas situações e não outras
ainda é uma área ativa de pesquisa.
Conceitos básicos sobre a otimização
30/04/2021 • 8 minutes to read

Para entender problemas de otimização, primeiro será necessário aprender um pouco de termos e conceitos
básicos.

Função de custo
Uma função de custo é uma descrição matemática de um problema que, quando avaliado, informa o valor
dessa solução. Normalmente, em problemas de otimização, procuramos encontrar a solução de valor mais
baixo para um problema. Em outras palavras, estamos tentando minimizar o custo.

Espaço de pesquisa
O espaço de pesquisa contém todas as soluções viáveis para um problema de otimização. Cada ponto desse
espaço de pesquisa é uma solução válida para o problema. No entanto, ele não é necessariamente o ponto mais
baixo, que corresponde à solução de menor custo.

Cenário de otimização
Juntos, o espaço de pesquisa e a função de custo geralmente são chamados de cenário de otimização . No
caso de um problema que envolva duas variáveis contínuas, a analogia para um cenário é bem direta.
Vamos explorar alguns diferentes cenários de otimização e ver quais são bons candidatos para a otimização do
Azure Quantum.
Um cenário suave e convexo
Considere o seguinte gráfico de uma função de custo que se parece com um vale suave:

Esse tipo de problema é facilmente resolvido com técnicas como descendente de gradiente, em que você
começa de um ponto de partida inicial e se move avidamente para soluções com custo menor. Depois de
algumas movimentações, a solução convergirá para o mínimo global. O mínimo global é o ponto mais baixo no
cenário de otimização. Os solucionadores de otimização do Azure Quantum não oferecem vantagens em
relação a outras técnicas com esses problemas diretos.
Um cenário estruturado e acidentado
O Azure Quantum funciona melhor com problemas em que o cenário é acidentado, com muitos picos e vales.
Aqui está um exemplo que considera duas variáveis contínuas.

Nesse cenário, um dos maiores desafios é evitar ficar preso a qualquer um dos mínimos locais aquém do ideal.
Um cenário acidentado pode ter vários vales. Cada um desses valers terá um ponto mais baixo, que é o mínimo
local. Um desses pontos será o mínimo geral, também chamado de mínimo global. Esses cenários acidentados
apresentam situações em que a otimização do Azure Quantum pode superar as outras técnicas.
Um cenário disperso e aleatório
Até agora, discutimos funções de custo suaves e acidentadas, mas e se não houver nenhum padrão estrutural?
O seguinte diagrama mostra um cenário:

Nesses casos, nos quais as soluções são completamente aleatórias, nenhum algoritmo pode ser mais eficiente
que uma pesquisa de força bruta.
Definindo uma função de custo
Conforme mencionado acima, a função de custo representa a quantidade que você deseja minimizar. Sua
principal finalidade é mapear cada configuração de um problema para apenas um número. Isso permite que o
otimizador compare facilmente as soluções potenciais e determine qual é a melhor. A chave para gerar uma
função de custo para o problema é reconhecer quais parâmetros do sistema afetam o custo escolhido.
Em princípio, a função de custo poderia ser qualquer função matemática $f = f(x_0, x_1, \dots)$, em que as
variáveis de função $x_1, x_2, \dots$ codificam as diferentes configurações. O cenário suave mostrado acima
poderia, por exemplo, ser gerado usando uma função quadrática de duas variáveis contínuas $f(x, y) = x^2 +
y^2$. No entanto, certos métodos de otimização podem esperar que a função de custo esteja em um formato
específico. Por exemplo, os resolvedores do Azure Quantum esperam um Problema de otimização binária. Para
esse tipo de problema, as configurações devem ser expressas por meio de variáveis binárias com $x_i \in {0, 1}$,
e muitos problemas são naturalmente adequados para serem expressos dessa forma.
Variáveis
Vamos ver como definir a função de custo para um problema simples. Em primeiro lugar, temos diversas
variáveis. Podemos nomear essas variáveis x e, se tivermos variáveis y, podemos indexá-las individualmente da
seguinte maneira:
$$ x_{i} $$
Por exemplo, se tivéssemos cinco variáveis, poderíamos indexar da seguinte forma:
$$ x_{0}, x_{1}, x_{2}, x_{3}, x_{4} $$
Essas variáveis podem ter valores específicos e, no caso de um problema de otimização binária, elas só podem
ter dois. Em específico, se o problema estiver considerando essas variáveis como rotações, como no modelo
Ising, os valores das variáveis poderão ser +1 ou -1.
Por exemplo:
$$ x_{0} = 1, x_{1} = 1, x_{2} = -1, x_{3} = -1, x_{4} = 1 $$
Em outros casos, as variáveis podem simplesmente ter os valores 1 ou 0 atribuídos, como no modelo de QUBO
(otimização quadrática binária irrestrita) ou PUBO (otimização polinomial binária irrestrita).
Por exemplo:
$$ x_{0} = 1, x_{1} = 1, x_{2} = 0, x_{3} = 0, x_{4} = 1 $$

NOTE
Mais informações sobre funções de custo e modelos de otimização binária, como Ising e PUBO, podem ser encontradas
no artigo Funções de custo.

Pesos
Vamos considerar algumas variáveis. Cada uma dessas variáveis tem um peso associado, que determina sua
influência sobre a função de custo geral.
Podemos escrever esses pesos como w e, novamente, se tivermos variáveis, então o peso associado a essas
variáveis individuais poderá ser indexado da seguinte forma
$$ w_{i} $$
Se tivéssemos 5 pesos, poderíamos indexá-los da seguinte maneira:
$$ w_{0}, w_{1}, w_{2}, w_{3}, w_{4} $$
Um peso pode ser qualquer número de valor real. Por exemplo, podemos dar a esses pesos os seguintes
valores:
$$ w_{0} = 50, w_{1} = -2, w_{2} = 7, w_{3} = 24, w_{4} = -10 $$
Termos
Os termos são a combinação de pesos e variáveis e têm esta aparência:
$$ w_{i}x_{i} $$
Como exemplo, vamos considerar um termo com o índice 0, um peso de 50 e uma atribuição de variável de 1:
$$ w_{0}x_{0} = 50(1) = 50 $$
Esse foi um exemplo de avaliação do custo de um termo.
Formulação de função de custo
Retornando à nossa definição de uma função de custo, trata-se de uma descrição matemática de um problema
que, quando avaliado, informa o valor dessa solução.
Portanto, para escrever uma função de custo, escrevemos uma soma dos termos, ou seja, a soma desses pesos e
variáveis.
Ela se parece com:
$$ \Large\sum_{i}w_{i}x_{i} $$
Com 5 variáveis, ela seria expandida para:
$$ w_{0}x_{0} + w_{1}x_{1} + w_{2}x_{2} + w_{3}x_{3} + w_{4}x_{4} $$

Grau e "k-local"
As funções de custo com as quais você trabalhará serão funções polinomiais de grau variável. As próprias
variáveis serão binárias ($x_i \in \{\pm1\}$ para Ising e $x_i \in \{0, 1\}$ para problemas QUBO/PUBO), o que
torna esses problemas de otimização binária.
Em alguns casos, a função de custo será linear. Isso significa que a maior potência à qual qualquer um dos
termos é elevada é 1. $x + y + 1$ é um exemplo de uma função linear. Os termos lineares são considerados
como tendo um grau de 1.
Em outros casos, você pode ter uma função de custo quadrática . Nesse caso, a maior potência à qual qualquer
um dos termos é elevado é 2. $x^2 + y^2 + x + 1$ é um exemplo de uma função quadrática. Essa função,
portanto, tem um grau de 2 [você pode ver isso citado como problemas de QUBO (otimização quadrática
binária irrestrita )].
Quando uma função de custo tem termos elevados a potências mais altas do que 2, referimo-nos a eles como
problemas de PUBO (otimização polinomial binária irrestrita) ou problemas de HOBO (otimização
binária de ordem superior) . Essas funções de custo têm graus maiores que 2. $x^3 + xy + x^2 + y^2 + x +
1$ é um exemplo de uma função polinomial de ordem superior.
Em geral, muitas vezes falamos sobre o grau máximo, k, e os descrevemos como problemas de k-local . Por
exemplo, também podemos nos referir a um QUBO como um problema de 2 locais. Você pode reduzir uma
função polinomial de ordem superior a uma ordem inferior introduzindo outras variáveis, o que aumentará o
tamanho do problema. Esse processo é conhecido como redução de grau .
No Azure Quantum, usamos o termo PUBO para descrever os problemas com um grau máximo de k. Isso inclui
problemas de QUBO, pois QUBOs são apenas PUBOs com grau 2.
Heurística
Um heurística é uma técnica para encontrar uma solução aproximada, quando encontrar a solução exata
levaria muito tempo. Quando pensamos nisso em termos do nosso panorama de otimização acima, pode levar
muito tempo para encontrar a solução de custo mais baixo, no entanto, podemos encontrar uma solução
próxima da ideal em um período razoável. Isso geralmente vem com a experimentação: testar diferentes
técnicas com diferentes parâmetros e tempos de execução para encontrar o que gera bons resultados.

Navegador
Podemos imaginar uma pessoa ou uma partícula em nosso espaço de pesquisa , e cada etapa adotada cria um
caminho ou um trajeto por meio do panorama de otimização. Geralmente, isso é chamado de navegador . Os
navegadores podem ser usados de várias maneiras, por exemplo, você pode optar por ter vários navegadores
começando do mesmo ponto de partida ou fazer com que eles sejam iniciados em locais diferentes, e assim por
diante.

Converter o problema em um modelo Ising ou QUBO


O artigo Formulações de Ising de muitos problemas NP do professor Andrew Lucas é um bom resumo de como
converter um problema de NP para o modelo QUBO ou Ising de QIO. Você pode baixar o artigo pelo link
fornecido. Depois de converter o problema de campo no modelo Ising ou QUBO, é recomendável mesclar os
termos com a mesma lista de variáveis em um único termo. Por exemplo: $$ w_{0}x_{0}x_{1}, w_{1}x_{1}x_{0} $$
pode ser mesclado em
$$ (w_{0} + w_{1})x_{0}x_{1} $$
Expresso em termos de código (usando o SDK do Python da QIO):

term1 = Term(c=2, indices=[0,1])


term2 = Term(c=3, indices=[1,0])

pode ser mesclado em:

merged_term = Term(c=5, indices=[0,1])

A mesclagem de termos pode melhorar significativamente a performance da QIO, se o problema tiver muitos
desses termos. Você pode usar um mapa de hash ou um algoritmo de classificação para fazer a mesclagem.
Configurar o QDK (Quantum development kit)
19/05/2021 • 2 minutes to read

Saiba como configurar o QDK (Quantum development kit) para desenvolver aplicativos de otimização e
computação quântica em seu ambiente.
O QDK consiste no seguinte:
A linguagem de programação Q#
Um conjunto de bibliotecas que abstraem o recurso complexo em Q#
APIs para as linguagens Python e .NET (C#, F# e VB.NET) para executar programas quânticos escritos em Q#
Um SDK do Python para manipular o uso de resolvedores de otimização no Azure Quantum
Ferramentas para facilitar seu desenvolvimento

Configurar o Quantum development kit para desenvolver aplicativos


de computação quântica em Q#
Os programas Q# podem ser executados como aplicativos autônomos usando o Visual Studio Code ou o Visual
Studio, por meio dos Jupyter Notebooks com o kernel lQ# do Jupyter ou emparelhados com um programa host
escrito em Python ou em uma linguagem .NET (C#, F#). Você também pode executar programas Q# online
usando o Codespaces, o MyBinder.org ou o Docker.
Opções para configurar o QDK para computação quântica
Você pode usar o QDK de três maneiras:
Instalar o QDK para a computação quântica localmente
Usar o QDK para a computação quântica online
Usar um QDK para a imagem do Docker de computação quântica
Instalar o QDK para a computação quântica localmente
Você pode desenvolver o código Q# na maioria dos seus IDEs favoritos, bem como integrar Q# a outras
linguagens, como Python e .NET (C#, F#).

C Ó DIGO VS VISUA L ST UDIO


( 2019 O U ( 2019 O U JUP Y T ER L IN H A DE
P O ST ERIO R) P O ST ERIO R) N OT EB O O K S C O M A N DO

Supor te a SO: Windows, macOS, Somente Windows Windows, macOS, Windows, macOS,
Linux Linux Linux

Q# autônomo Instalar Instalar Instalar Instalar

Q# e Python Instalar Instalar Instalar Instalar


Q# e .NET (C#, Instalar Instalar ✖ Instalar
F#)

Usar o QDK para a computação quântica online


Você também pode desenvolver o código Q# sem instalar nada localmente com estas opções:

REC URSO VA N TA GEN S L IM ITA Ç Õ ES

Codespaces do Visual Studio Um ambiente de desenvolvimento Requer uma assinatura e um plano do


online avançado Azure

Binder Experiência gratuita de notebook Sem persistência


online

Usar o QDK para a computação quântica com o Docker


Você pode usar nossa imagem do Docker QDK em sua instalação local do Docker ou na nuvem por meio de
qualquer serviço que dê suporte a imagens do Docker, como a ACI.
Você pode baixar a imagem do Docker IQ# de https://github.com/microsoft/iqsharp/#using-iq-as-a-container.
Você também pode usar o Docker com um Contêiner de Desenvolvimento Remoto do Visual Studio Code para
definir ambientes de desenvolvimento com rapidez. Para obter mais informações sobre Contêineres de
Desenvolvimento do VS Code, confira https://github.com/microsoft/Quantum/tree/master/.devcontainer.
Os fluxos de trabalho de cada uma dessas configurações são descritos e comparados em Maneiras de executar
um programa Q#.

Configurar o Quantum development kit para gerenciar os


solucionadores de otimização no Azure Quantum
Você pode usar o SDK do Python do Quantum development kit para resolver problemas de otimização usando
os solucionadores do Azure Quantum.
Para configurar o SDK do Python, siga as etapas em Instalar e usar o SDK do Python para otimização.
Introdução ao QDK (Quantum development kit)
para a computação quântica
19/05/2021 • 4 minutes to read

O QDK (Quantum Development Kit) conta com todas as ferramentas necessárias para criar programas e
experimentos quânticos com o Q#, uma linguagem de programação projetada especificamente para o
desenvolvimento de aplicativos quânticos.
Para iniciar, leia o guia de configuração do QDK. Você obterá instruções sobre a configuração do Kit de
Desenvolvimento Quantum em computadores Windows, Linux ou MacOS para que seja possível gravar seus
programas quânticos.
Caso não esteja familiarizado com a computação quântica, examine a seção Visão geral para saber o que os
computadores quânticos podem fazer e os conceitos básicos da computação quântica.

Introdução à programação com Q#


O Quantum Development Kit oferece várias maneiras de aprender a desenvolver um programa quântico com
Q#. Para começar a usar o poder do quantum, experimente nossos tutoriais:
Gerador de números aleatórios quânticos: comece com um aplicativo no estilo "Olá, Mundo do Q#" que
oferece uma breve introdução aos conceitos quânticos e permite criar e executar um aplicativo quântico em
poucos minutos.
Explorar o emaranhamento com o Q#: esse tutorial ajuda a escrever um programa Q# que demonstra alguns
dos conceitos fundamentais da programação quântica. Caso não esteja tudo pronto para iniciar a codificação,
você ainda poderá acompanhar sem instalar o QDK e ter uma visão geral da linguagem de programação Q#
e dos primeiros conceitos da computação quântica.
Algoritmo de pesquisa de Grover: explore este exemplo de programa Q# para ter uma ideia da capacidade
do Q# de expressar o algoritmo quântico de modo a abstrair as operações quânticas de nível inferior. Este
tutorial descreverá o desenvolvimento do programa como um aplicativo Q# usando o Visual Studio ou o
Visual Studio Code.

Aprenda ainda mais


O Microsoft Learn oferece um treinamento online e gratuito sobre computação quântica. O Roteiro de
Aprendizagem Fundamentos da computação quântica apresenta conceitos básicos sobre computação
quântica e algoritmos quânticos, além de permitir que você comece a criar programas quânticos usando Q#.
Para saber ainda mais sobre a programação em Q#, confira o Quantum Katas, uma coleção de exercícios
individuais de programação que apresenta a computação quântica por meio de atividades de programação
em Q#. Muitos desses katas também estão disponíveis como notebooks do Q#.
Nosso repositório de exemplos apresenta vários exemplos de como escrever programas quânticos usando o
Q#. A maior parte desses exemplos é gravada usando nossas bibliotecas quânticas de software livre,
incluindo as bibliotecas padrão e de química (mais informações sobre elas abaixo).

Principais conceitos da computação quântica


Se você é novato no desenvolvimento quântico, sabemos que isso pode parecer um pouco assustador. Esses
principais conceitos foram desenvolvidos para ajudar você a entrar no mundo quântico e entender as diferenças
entre a computação quântica e a computação clássica.
Noções básicas sobre a computação quântica
Computadores e simuladores quânticos
O que são a linguagem de programação Q# e o QDK?
Álgebra linear para computação quântica

Documentação do Quantum Development Kit


A documentação atual inclui os seguintes tópicos adicionais:
Guias do desenvolvedor do Q#
O Guia do Usuário do Q# detalha os principais conceitos para criar programas quânticos em Q#.
Os simuladores e aplicativos host quânticos descrevem como os algoritmos quânticos são executados, quais
computadores quânticos estão disponíveis e como gravar em um driver não Q# para executar um programa
quântico.
Bibliotecas Q#
Bibliotecas padrão do Q#: descreve as operações e as funções compatíveis com o requisito de controle de
linguagem clássica e os algoritmos quânticos do Q#. Os tópicos incluem fluxo de controle, estruturas de
dados, correção de erros, teste e depuração.
Biblioteca de química doQ#: descreve as operações e as funções compatíveis com simulações de química
quântica, uma aplicação crítica da computação quântica. Os tópicos incluem a simulação da dinâmica
hamiltoniana e a estimativa da fase quântica, entre outros.
Biblioteca de numéricosQ#: descreve as operações e as funções compatíveis com a expressão de funções
aritméticas complexas em termos das operações nativas dos computadores de destino.
Referência de biblioteca doQ#: contém informações de referência sobre entidades de biblioteca por
namespace.
Computação quântica geral
Conceitos de computação quântica: inclui tópicos como a relevância da álgebra linear para computação
quântica, a natureza e o uso de um qubit, como ler um circuito quântico e muito mais.
Glossário de computação quântica: mostra os principais termos específicos da computação quântica e do
desenvolvimento de programas. Se você for novato, essa poderá ser uma referência útil ao ler a
documentação.
Leitura adicional: contém referências selecionadas especialmente para a cobertura aprofundada de tópicos
de computação quântica.
Informações adicionais
Notas sobre a versão do Microsoft Quantum Development Kit.

Faça parte da Comunidade de Software Livre do Q#


O Quantum Development Kit é um kit de desenvolvimento de software livre que permite aos desenvolvedores
tornar a computação quântica mais acessível para todos, para que possamos resolver alguns dos problemas
mais desafiadores do mundo. Instituições acadêmicas que exigem softwares livres poderão implantar o Q# para
aprendizado e desenvolvimento quântico. Transformar o kit de desenvolvimento em um kit de software livre
também dá aos desenvolvedores e especialistas de domínio uma oportunidade de contribuir com melhorias e
ideias por meio de código.
Seus comentários, sua participação e suas contribuições para o Quantum Development Kit são importantes.
Confira Contribuir para o Quantum Development Kit para saber mais sobre as fontes do Quantum Development
Kit, fazer comentários, descobrir como participar das decisões e contribuir para essa plataforma de
desenvolvimento quântico em crescimento.
Se você quiser mais informações gerais sobre a iniciativa de computação quântica da Microsoft, confira
Microsoft Quantum.
Desenvolver com aplicativos Q# em um IDE
19/05/2021 • 3 minutes to read

Saiba como desenvolver aplicativos Q# no VS Code (Visual Studio Code), no Visual Studio, nos Codespaces do
Visual Studio ou em qualquer editor/IDE e executar aplicativos no console do .NET. Os programas Q# podem ser
executados por conta própria em uma linguagem de host, como C#, F# ou Python, sem a necessidade de um
driver.

Pré-requisitos para todos os ambientes


SDK do .NET Core 3.1

Instalação
Embora seja possível compilar aplicativos Q# em qualquer IDE, é recomendável usar o VS Code (Visual Studio
Code) ou o IDE do Visual Studio para desenvolver aplicativos Q# localmente. Para desenvolvimento na nuvem
por meio do navegador da Web, recomendamos o Codespaces do Visual Studio. O desenvolvimento nesses
ambientes permite aproveitar a funcionalidade sofisticada da extensão do QDK (Quantum development kit), que
inclui avisos, realce de sintaxe, modelos de projeto e muito mais.

IMPORTANT
Se você está trabalhando em Linux, poderá encontrar uma dependência ausente conforme a distribuição e o método de
instalação em particular (por exemplo, determinadas imagens do Docker). Instale a biblioteca libgomp no sistema, pois a
biblioteca de suporte OpenMP GNU é exigida pelo simulador Quantum do QDK. No Ubuntu, você pode fazer isso
executando o sudo apt install libgomp1 ou o yum install libgomp no CentOS. Para outras distribuições, consulte
seu gerenciador de pacotes.

Para configurar para o VS Code:


1. Baixe e instale o VS Code 1.52.0 ou superior (Windows, Linux e Mac).
2. Instale o QDK para VS Code.
Para configurar para o Visual Studio:
1. Baixe e instale o Visual Studio 16.3 ou superior, com a carga de trabalho de desenvolvimento
multiplataforma do .NET Core habilitada.
2. Baixe e instale o QDK.
Para configurar para outro ambiente:
1. Digite o seguinte no prompt de comando

dotnet new -i Microsoft.Quantum.ProjectTemplates

Desenvolver com Q#
Siga as instruções na guia correspondente ao seu ambiente de desenvolvimento.
Código do VS
Visual Studio
Outros editores com o prompt de comando

Se você receber o erro "O 'npm' não é reconhecido como um comando interno ou externo", nas etapas abaixo,
instale o node.js, incluindo o npm. Como alternativa, use nossos modelos de linha de comando para criar um
projeto Q# ou use o Visual Studio.
Para criar um projeto:
1. Clique em Exibir -> Paleta de Comandos e selecione Q#: Criar Novo Projeto .
2. Clique em Aplicativo de console autônomo .
3. Navegue até a localização para salvar o projeto. Digite o nome do projeto e clique em Criar Projeto .
4. Quando o projeto for criado com êxito, clique em Abrir novo projeto... no canto inferior direito.
Inspecione o projeto. Você verá um arquivo de origem chamado Program.qs , um programa Q# que define uma
operação simples para imprimir uma mensagem no console.
Para executar o aplicativo:
1. Clique em Terminal -> Novo Terminal .
2. No prompt do terminal, insira dotnet run .
3. Você deverá ver o seguinte texto na Janela de Saída Hello quantum world!

NOTE
No momento, não há compatibilidade com workspaces com várias pastas raiz na extensão do Q# para VS Code. Caso
você tenha vários projetos em um workspace do VS Code, todos os projetos precisarão estar contidos na mesma pasta
raiz.

Próximas etapas
Agora que você instalou o Quantum development kit em seu ambiente preferido, escreva e execute seu
primeiro programa quântico.
Usar o QDK para computação quântica com Q# e
Python
19/05/2021 • 3 minutes to read

Saiba como instalar o QDK (Quantum development kit) para desenvolver programas de host do Python que
chamam operações Q#.

Instalar o pacote qsharp do Python


Instalar usando o conda (recomendado)
Instalar usando a CLI do .NET e o pip (avançado)

1. Instale o Miniconda ou o Anaconda. Obser vação: Instalação de 64 bits necessária.


2. Abra um prompt do Anaconda.
Ou, se preferir usar o PowerShell ou o pwsh: abra um shell, execute conda init powershell e feche-o e
abra-o novamente.
3. Crie e ative um novo ambiente de conda chamado qsharp-env com os pacotes necessários (incluindo o
Jupyter Notebook e o IQ#) executando os seguintes comandos:

conda create -n qsharp-env -c quantum-engineering qsharp notebook

conda activate qsharp-env

4. Execute python -c "import qsharp" no mesmo terminal para confirmar a instalação e preencher o cache
do pacote local com todos os componentes necessários do QDK.
É isso! Agora você tem o pacote qsharp do Python e o kernel do IQ# para Jupyter, que fornecem uma
funcionalidade básica para compilar e executar operações Q# do Python e permite usar Jupyter Notebooks do
Q#.

Escolher o IDE
Embora seja possível usar o Q# com o Python em qualquer IDE, é bastante recomendável usar o IDE do VS Code
(Visual Studio Code) para os aplicativos Q# + Python. Com a extensão do Visual Studio Code para o QDK, você
tem acesso a funcionalidades mais sofisticadas, como avisos, realce de sintaxe, modelos de projeto e muito
mais.
Se quiser usar o VS Code:
Instale o VS Code (Windows, Linux e Mac).
Instale a extensão do QDK para VS Code.
Se você prefere usar outro editor, as instruções acima fornecem todas as informações necessárias.

Escrever seu primeiro programa Q#


Agora você está pronto para verificar a instalação do pacote qsharp do Python gravando e executando um
programa de Q# simples.
1. Crie uma operação mínima de Q# criando um arquivo chamado Operation.qs e adicionando o seguinte
código a ele:

namespace Qrng {
open Microsoft.Quantum.Intrinsic;

operation SampleQuantumRandomNumberGenerator() : Result {


use q = Qubit(); // Allocate a qubit.
H(q); // Put the qubit to superposition. It now has a 50% chance of being 0 or 1.
let r = M(q); // Measure the qubit value.
Reset(q);
return r;
}
}

2. Na mesma pasta de Operation.qs , crie um programa Python chamado host.py para simular a operação
Q# SampleQuantumRandomNumberGenerator() :

import qsharp
from Qrng import SampleQuantumRandomNumberGenerator

print(SampleQuantumRandomNumberGenerator.simulate())

3. No ambiente criado durante a instalação (ou seja, o ambiente de conda ou de Python em que você
instalou qsharp ), execute o programa:

python host.py

4. Você verá o resultado da operação que invocou. Nesse caso, como a operação gera um resultado
aleatório, você verá 0 ou 1 impresso na tela. Caso execute o programa várias vezes, será possível
conferir cada resultado em aproximadamente metade do tempo.

NOTE
O código Python é apenas um programa Python normal. É possível usar qualquer ambiente Python, incluindo Jupyter
Notebooks baseados em Python, para escrever o programa nessa linguagem de programação e chamar as operações
Q#. O programa Python pode importar operações de Q# de arquivos .qs localizados na mesma pasta que o código
Python.

Próximas etapas
Agora que você testou o Kit de Desenvolvimento Quantum em um ambiente preferencial, será possível seguir
este tutorial para gravar e executar seu primeiro programa quântico.
Desenvolver com o Q# e o .NET
19/05/2021 • 3 minutes to read

A linguagem de programação Q# foi criada para funcionar bem com linguagens .NET, como C# e F#. Neste guia,
demonstramos como usar o Q# com um programa de host escrito em uma linguagem .NET.
Primeiro, criamos o aplicativo Q# e o host .NET, depois demonstramos como chamar Q# do host.

Pré-requisitos
Instalar o QDK (Quantum development kit) para uso com projetos Q#.

Criar uma biblioteca do Q# e um host do .NET


A primeira etapa é criar projetos para a biblioteca do Q# e para o host do .NET que chamará as operações e as
funções definidas na biblioteca do Q#.
Siga as instruções na guia correspondente ao seu ambiente de desenvolvimento. Se estiver usando um editor
diferente do Visual Studio ou do VS Code, basta seguir as etapas do prompt de comando.
Visual Studio Code ou prompt de comando
Visual Studio 2019

Criar uma nova biblioteca do Q#

dotnet new classlib -lang Q# -o quantum

Criar um projeto de console C# ou F#

dotnet new console -lang C# -o host

Adicione a biblioteca do Q# como uma referência do programa de host

cd host
dotnet add reference ../quantum/quantum.csproj

[Opcional] Crie uma solução para os dois projetos

dotnet new sln -n quantum-dotnet


dotnet sln quantum-dotnet.sln add ./quantum/quantum.csproj
dotnet sln quantum-dotnet.sln add ./host/host.csproj

Chamar o Q# no .NET
Depois de configurar seus projetos seguindo as instruções acima, chame o Q# no aplicativo de console .NET. O
compilador Q# criará as classes .NET para cada operação e função Q# que permite executar seus programas
quânticos em um simulador.
Por exemplo, a amostra de interoperabilidade com o .NET inclui o seguinte exemplo de uma operação Q#:
/// Instantiates the oracle and runs the parameter restoration algorithm.
operation RunAlgorithm(bits : Bool[]) : Bool[] {
Message("Hello, quantum world!");
// construct an oracle using the input array
let oracle = ApplyProductWithNegationFunction(bits, _, _);
// run the algorithm on this oracle and return the result
return ReconstructOracleParameters(Length(bits), oracle);
}

Para chamar essa operação no .NET em um simulador quântico, use o método Run da classe .NET
RunAlgorithm gerada pelo compilador Q#:

C#
F#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using static System.Diagnostics.Debug;

using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;

namespace Microsoft.Quantum.Samples
{
static class Program
{
static async Task Main(string[] args)
{
var bits = new[] { false, true, false };
using var sim = new QuantumSimulator();

Console.WriteLine($"Input: {bits.ToDelimitedString()}");

var restored = await RunAlgorithm.Run(sim, new QArray<bool>(bits));


Console.WriteLine($"Output: {restored.ToDelimitedString()}");

Assert(bits.Parity() == restored.Parity());
}

static bool Parity(this IEnumerable<bool> bitVector) =>


bitVector.Aggregate(
(acc, next) => acc ^ next
);

static string ToDelimitedString(this IEnumerable<bool> values) =>


String.Join(", ", values.Select(x => x.ToString()));
}
}

Próximas etapas
Agora que você tem o Quantum Development Kit configurado para os dois aplicativos Q# e para
interoperabilidade com o .NET, escreva e execute seu primeiro programa quântico.
Como desenvolver com o Q# e o Binder
19/05/2021 • 2 minutes to read

Saiba como criar um aplicativo Q# usando o Binder. Você pode usar o associador para executar e compartilhar
Jupyter Notebooks online.

Usar o associador com exemplos do QDK


Para configurar o associador automaticamente para usar os exemplos do QDK (Quantum development kit):
1. Abra um navegador e execute https://aka.ms/try-qsharp.
2. Na página de aterrissagem Exemplos do Kit de Desenvolvimento Quantum , clique em Q# Notebook
para saber como usar o IQ# a fim de gravar seus notebooks de aplicativos quânticos.

Use o Binder com seus notebooks e repositórios


Caso já tenha os notebooks em um repositório GitHub, será possível configurar o Binder para trabalhar com seu
repositório:
1. Verifique se há um arquivo chamado Dockerfile na raiz do repositório. O arquivo deverá conter pelo
menos as seguintes linhas:

FROM mcr.microsoft.com/quantum/iqsharp-base:0.12.20082513

USER root
COPY . ${HOME}
RUN chown -R ${USER} ${HOME}

USER ${USER}

NOTE
Será possível verificar a versão mais atual do IQ# em Notas sobre a versão.
Para obter mais informações sobre como criar um Dockerfile, confira a Referência do Dockerfile.
2. Abra um navegador para executar mybinder.org.
3. Insira o nome do repositório conforme a URL do GitHub (por exemplo MyName/MyRepo) e clique em
Iniciar .

Próximas etapas
Agora que você já configurou o ambiente do Binder, será possível gravar e executar seu primeiro programa
quântico.
Atualizar o QDK (Quantum development kit) para a
última versão
19/05/2021 • 6 minutes to read

Saiba como atualizar o QDK (Quantum development kit) para a última versão.
Este artigo pressupõe que você já tenha o QDK instalado. Se você estiver instalando-o pela primeira vez,
consulte o guia de instalação.
Recomendamos manter-se atualizado com a última versão do QDK. Siga este guia de atualização para atualizar
o QDK para a última versão. O processo consiste em duas partes:
1. Atualizar os arquivos e os projetos Q# existentes para alinhar o código com qualquer sintaxe atualizada.
2. Atualização do próprio QDK para o ambiente de desenvolvimento escolhido.

Atualizar projetos Q#
Independentemente de você estar usando o C# ou o Python para hospedar operações Q#, siga estas instruções
para atualizar projetos do Q#.
1. Primeiro, verifique se você tem a última versão do SDK do .NET Core 3.1. Execute o seguinte comando no
prompt de comando:

dotnet --version

Verifique se a saída é 3.1.100 ou superior. Caso contrário, instale a última versão e verifique novamente.
Em seguida, siga as instruções abaixo, dependendo da configuração (o Visual Studio, o Visual Studio
Code ou diretamente no prompt de comando).
Atualizar projetos do Q# no Visual Studio
1. Atualize para a última versão do Visual Studio 2019. Confira este link para obter instruções.
2. Abra sua solução no Visual Studio.
3. No menu, selecione Compilar -> Limpar Solução .
4. Em cada um dos arquivos .csproj, atualize a estrutura de destino para netcoreapp3.1 (ou netstandard2.1
se ela for um projeto de biblioteca). Ou seja, edite as linhas do formulário:

<TargetFramework>netcoreapp3.1</TargetFramework>

Encontre mais detalhes sobre como especificar as estruturas de destino aqui.


5. Em cada um dos arquivos .csproj, defina o SDK como Microsoft.Quantum.Sdk , conforme indicado na linha
abaixo. Observe que o número de versão deve ser o último disponível. Determine isso examinando as
notas sobre a versão.

<Project Sdk="Microsoft.Quantum.Sdk/0.12.20072031">

6. Salve e feche todos os arquivos da solução.


7. Selecione Ferramentas -> Linha de Comando -> Prompt de Comando do Desenvolvedor . Como
alternativa, você pode usar o console de gerenciamento de pacotes no Visual Studio.
8. Para cada projeto da solução, execute o seguinte comando para remover este pacote:

dotnet remove [project_name].csproj package Microsoft.Quantum.Development.Kit

Se os projetos usarem outros pacotes Microsoft.Quantum ou Microsoft.Azure.Quantum (por exemplo,


Microsoft.Quantum.Numerics), execute o comando add neles para atualizar a versão usada.

dotnet add [project_name].csproj package [package_name]

9. Feche o prompt de comando e selecione Compilar -> Compilar Solução (não selecione Recompilar
Solução).
Agora acesse Atualizar sua extensão do QDK no Visual Studio.
Atualizar projetos do Q# no Visual Studio Code
1. No Visual Studio Code, abra a pasta que contém o projeto a ser atualizado.
2. Selecione Terminal -> Novo Terminal .
3. Siga as instruções de atualização usando o prompt de comando (diretamente abaixo).
Atualizar projetos Q# no prompt de comando
1. Navegue até a pasta que contém o arquivo de projeto principal.
2. Execute o comando a seguir:

dotnet clean [project_name].csproj

3. Determine a versão atual do QDK. Para encontrá-la, examine as notas sobre a versão. A versão estará em
um formato semelhante a 0.12.20072031 .
4. Em cada um dos arquivos .csproj , execute as seguintes etapas:
Atualize a estrutura de destino para netcoreapp3.1 (ou netstandard2.1 se ela for um projeto de
biblioteca). Ou seja, edite as linhas do formulário:

<TargetFramework>netcoreapp3.1</TargetFramework>

Encontre mais detalhes sobre como especificar as estruturas de destino aqui.


Substitua a referência ao SDK na definição de projeto. Verifique se o número de versão
corresponde ao valor determinado na etapa 3 .

<Project Sdk="Microsoft.Quantum.Sdk/0.12.20072031">

Remova a referência ao pacote Microsoft.Quantum.Development.Kit , se presente, que será


especificada na seguinte entrada:

<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.10.1910.3107" />

Atualize a versão de todos os pacotes do Microsoft Quantum para a versão lançada mais
recentemente do QDK (determinada na etapa 3 ). Esses pacotes são nomeados com os seguintes
padrões:

Microsoft.Quantum.*
Microsoft.Azure.Quantum.*

As referências aos pacotes têm o seguinte formato:

<PackageReference Include="Microsoft.Quantum.Compiler" Version="0.12.20072031" />

Salve o arquivo atualizado.


Restaure as dependências do projeto fazendo o seguinte:

dotnet restore [project_name].csproj

5. Volte à pasta que contém o projeto principal e execute:

dotnet build [project_name].csproj

Com os projetos do Q# atualizados, siga estas instruções para atualizar o próprio QDK.

Atualizar o QDK
O processo usado para atualizar o QDK varia de acordo com o ambiente e a linguagem de desenvolvimento.
Selecione o ambiente de desenvolvimento abaixo.
Python: atualizar o pacote qsharp
Jupyter Notebooks: atualizar o kernel do IQ#
Visual Studio: atualizar a extensão do QDK
VS Code: atualizar a extensão do QDK
Linha de comando e C#: atualizar modelos de projeto

Atualizar o pacote qsharp do Python


O procedimento de atualização depende de a instalação original ter sido feita usando o conda ou a CLI do .NET e
o pip.

Atualizar usando o conda (recomendado)


Atualizar usando a CLI do .NET e o pip (avançado)

1. Ative o ambiente de conda em que você instalou o pacote qsharp e execute este comando para atualizá-
lo:

conda update -c quantum-engineering qsharp

2. Execute o comando a seguir no local dos arquivos .qs :

python -c "import qsharp; qsharp.reload()"


Agora você pode usar o pacote qsharp do Python atualizado para executar seus programas quânticos
existentes.

Atualizar o kernel de Jupyter do IQ#


O procedimento de atualização depende de a instalação original ter sido feita usando o conda ou a CLI do .NET e
o pip.
Atualizar usando o conda (recomendado)
Atualizar usando a CLI do .NET e o pip (avançado)

1. Ative o ambiente de conda em que você instalou o pacote qsharp e execute este comando para atualizá-
lo:

conda update -c quantum-engineering qsharp

2. Execute o seguinte comando em uma célula em cada um dos Jupyter Notebooks do Q# existentes:

%workspace reload

Agora você pode usar o kernel do IQ# atualizado para executar os Jupyter Notebooks existentes do Q#.

Atualizar a extensão do QDK para Visual Studio


1. Atualizar a extensão do Visual Studio do Q#
O Visual Studio exibe um aviso solicitando que você aceite as atualizações da extensão do Quantum
no Visual Studio
Aceite a atualização

NOTE
Os modelos de projeto serão atualizados com a extensão. Os modelos atualizados se aplicam apenas aos projetos
recém-criados. O código para os projetos existentes não é atualizado quando a extensão é atualizada.

Atualizar a extensão do QDK para VS Code


1. Atualize a extensão do Quantum para VS Code
Reinicie o VS Code
Navegue até a guia Extensões
Selecione a extensão Microsoft Quantum Development Kit para Visual Studio Code
Recarregue a extensão

C#, usando a ferramenta de linha de comando dotnet


1. Atualizar os modelos de projeto do Quantum para .NET
No prompt de comando:

dotnet new -i Microsoft.Quantum.ProjectTemplates


Como alternativa, se você pretende usar os modelos de linha de comando e já tiver a extensão VS Code
QDK instalada, atualize os modelos de projeto da própria extensão:
Atualizar a extensão QDK
No VS Code, acesse Exibir -> Paleta de Comandos
Selecione Q#: Instalar modelos de projeto de linha de comando
Após alguns segundos, você verá um pop-up com a mensagem "Modelos de projeto instalados com
êxito"
Tutorial: Implementar um gerador de número
quântico aleatório em Q#
19/05/2021 • 5 minutes to read

Um exemplo simples de um algoritmo quântico escrito em Q# é um gerador de número quântico aleatório. Esse
algoritmo usa a natureza da mecânica quântica para produzir um número aleatório.

Pré-requisitos
O Microsoft Quantum development kit.
A criação de um projeto Q# para um aplicativo Q#, com um programa de host em Python ou um programa
de host em C#.

Gravar uma operação Q#


Código de operação Q#
1. Substitua o conteúdo do arquivo Program.qs pelo seguinte código:

namespace Qrng {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
operation SampleQuantumRandomNumberGenerator() : Result {
use q = Qubit(); // Allocate a qubit.
H(q); // Put the qubit to superposition. It now has a 50% chance of being 0 or 1.
return MResetZ(q); // Measure the qubit value.
}
}

Conforme mencionado em nosso artigo Noções básicas sobre computação quântica, um qubit é uma unidade
de informações quânticas que podem estar em superposição. Quando medido, um qubit pode ser apenas 0 ou
1. No entanto, antes da medição, o estado do qubit representa a probabilidade de ler 0 ou 1 com uma medida.
Esse estado probabilístico é conhecido como sobreposição. Podemos usar essa probabilidade para gerar
números aleatórios.
Em nossa operação Q#, apresentamos o tipo de dados Qubit , nativo para Q#. Podemos alocar apenas um
Qubit com uma instrução use . Quando é alocado, um qubit está sempre no estado Zero .

Usando a operação H , podemos colocar nosso Qubit em sobreposição. Para medir um qubit e ler seu valor,
use a operação intrínseca M .
Ao colocar nosso Qubit em sobreposição e medi-lo, nosso resultado será um valor diferente cada vez que o
código for invocado.
Quando um Qubit for desalocado, ele precisará ser explicitamente definido de volta para o estado Zero ; caso
contrário, o simulador relatará um erro de runtime. Uma maneira fácil de alcançar isso é invocar Reset .
Visualizar o código com a esfera Bloch
Na esfera Bloch, o polo norte representa o valor clássico 0 e o polo sul representa o valor clássico 1 . Qualquer
sobreposição pode ser representada por um ponto na esfera (representada por uma seta). Quanto mais próxima
a extremidade da seta estiver de um polo, maior a probabilidade de o qubit recolher o valor clássico atribuído a
esse polo quando medido. Por exemplo, o estado do qubit representado pela seta vermelha abaixo terá uma
probabilidade mais alta de dar o valor 0 se medirmos isso.

Podemos usar essa representação para visualizar o que o código está fazendo:
Primeiro, começamos com um qubit inicializado no estado 0 e aplicamos H para criar uma sobreposição na
qual as probabilidades de 0 e 1 são as mesmas.

Em seguida, medimos o qubit e salvamos a saída:

Como o resultado da medição é completamente aleatório, obtivemos um bit aleatório. Podemos chamar essa
operação várias vezes para criar inteiros. Por exemplo, se chamarmos a operação três vezes para obter três bits
aleatórios, poderemos criar números aleatórios de 3 bits (ou seja, um número aleatório entre 0 e 7).

Criar um gerador de número aleatório completo


Agora que temos uma operação Q# que gera bits aleatórios, podemos usá-la para criar um gerador de número
quântico aleatório completo. Podemos usar um aplicativo Q# ou um programa host.
Aplicativos Q# com Visual Studio ou Visual Studio Code
Python com Visual Studio Code ou o prompt de comando
C# com o Visual Studio Code ou com o Visual Studio

Para criar um aplicativo Q# completo, adicione o seguinte ponto de entrada ao programa Q#:
namespace Qrng {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
operation SampleQuantumRandomNumberGenerator() : Result {
use q = Qubit(); // Allocate a qubit.
H(q); // Put the qubit to superposition. It now has a 50% chance of being 0 or 1.
return MResetZ(q); // Measure the qubit value.
}

operation SampleRandomNumberInRange(max : Int) : Int {


mutable bits = new Result[0];
for idxBit in 1..BitSizeI(max) {
set bits += [SampleQuantumRandomNumberGenerator()];
}
let sample = ResultArrayAsInt(bits);
return sample > max
? SampleRandomNumberInRange(max)
| sample;
}
@EntryPoint()
operation SampleRandomNumber() : Int {
let max = 50;
Message($"Sampling a random number between 0 and {max}: ");
return SampleRandomNumberInRange(max);
}
}

O programa executará a operação ou a função marcada com o atributo @EntryPoint() em um simulador ou em


um avaliador de recurso, dependendo da configuração do projeto e das opções de linha de comando.
No Visual Studio, basta pressionar Ctrl + F5 para executar o script.
No VS Code, compile o Program.qs pela primeira vez digitando o seguinte no terminal:

dotnet build

Para as próximas execuções, não é necessário compilá-lo novamente. Para executá-lo, digite o seguinte
comando e pressione Enter:

dotnet run --no-build


Tutorial: Explorar o emaranhamento com o Q#
19/05/2021 • 13 minutes to read

Neste tutorial, mostraremos como escrever um programa Q# que processa e mede qubits e demonstra os
efeitos da superposição e do emaranhamento.
Você escreverá um aplicativo chamado Bell para demonstrar o entrelaçamento quântico. O nome Bell foi criado
em referência aos estados Bell, que são estados quânticos específicos de dois qubits usados para representar os
exemplos mais simples de sobreposição e entrelaçamento quântico.

Pré-requisitos
Se estiver pronto para começar a codificação, siga estas etapas antes de continuar:
Instalar o QDK (Quantum development kit) usando sua linguagem e seu ambiente de desenvolvimento
preferenciais.
Caso já tenha o QDK instalado, verifique se você o atualizou para a última versão
Você também pode acompanhar a narrativa sem instalar o QDK, examinar as visões gerais da linguagem de
programação Q# e os primeiros conceitos da computação quântica.

Neste tutorial, você aprenderá como:


Criar e combinar operações em Q#
Crie operações para colocar qubits em sobreposição, entrelaçá-los e medi-los.
Demonstre o entrelaçamento quântico com um programa Q# executado em um simulador.

Como demonstrar o comportamento do qubit com o QDK


Onde os bits clássicos contêm apenas um valor binário, como 0 ou 1, o estado de um qubit pode estar em uma
sobreposição de 0 e 1. Conceitualmente, o estado de um qubit pode ser pensado como uma direção no espaço
abstrato (também conhecido como um vetor). Um estado de qubit pode estar em qualquer uma das possíveis
direções. Os dois estados clássicos são as duas direções, que representam 100% de chance de medir 0 e
100% de chance de medir 1.
O ato de medição produz um resultado binário e altera um estado do qubit. A medida produz um valor binário,
0 ou 1. O qubit passa de estar em sobreposição (qualquer direção) para um dos estados clássicos. Depois disso,
repetir a mesma medição sem nenhuma operação intermediária produz o mesmo resultado binário.
Vários qubits podem ser entrelaçados . Quando fazemos uma medição de um qubit entrelaçado, nosso
conhecimento do estado dos outros é atualizado também.
Agora, estamos prontos para demonstrar como o Q# expressa esse comportamento. Você começará com o
programa mais simples possível e o desenvolverá para demonstrar a sobreposição quântica e o entrelaçamento
quântico.

Como criar um projeto em Q#


A primeira coisa que precisamos fazer é criar um projeto Q#. Neste tutorial, vamos usar o ambiente baseado em
aplicativos Q# com VS Code.
Para criar um projeto, no VS Code:
1. Clique em Exibir -> Paleta de Comandos e selecione Q#: Criar Novo Projeto .
2. Clique em Aplicativo de console autônomo .
3. Navegue até o local para salvar o projeto e clique em Criar Projeto .
4. Quando o projeto for criado com êxito, clique em Abrir novo projeto... no canto inferior direito.
Nesse caso, chamamos o projeto Bell . Isso gera dois arquivos: Bell.csproj , o arquivo de projeto, e
Program.qs , um modelo de um aplicativo Q# que usaremos para escrever nosso aplicativo. O conteúdo de
Program.qs deve ser:

namespace Bell {

open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;

@EntryPoint()
operation HelloQ() : Unit {
Message("Hello quantum world!");
}
}

Escrever o aplicativo Q#
Nossa meta é preparar dois qubits em um estado quântico específico, demonstrando como operar em qubits
com Q# para alterar seu estado e demonstrar os efeitos da sobreposição e entrelaçamento. Criaremos isso peça
a peça para apresentar os estados, as operações e a medição do qubit.
Inicializar qubit usando medição
No primeiro snippet de código abaixo, mostramos como trabalhar com qubits no Q#. Apresentaremos duas
operações, M e X , que transformam o estado de um qubit. Neste snippet de código, uma operação
SetQubitState é definida que usa como parâmetro um qubit e outro parâmetro, desired , que representa o
estado no qual gostaríamos que o qubit estivesse. A operação SetQubitState executa uma medida no qubit
usando a operação M . Em Q#, uma medição de qubit sempre retorna Zero ou One . Se a medida retornar um
valor diferente do valor desejado, SetQubitState "inverterá" o qubit; ou seja, ele executa uma operação X , que
altera o estado do qubit para um novo estado no qual as probabilidades de uma medida que retorna Zero e
One são revertidas. Dessa forma, o SetQubitState sempre coloca o destino qubit no estado desejado.

Substitua o conteúdo de Program.qs pelo seguinte código:

namespace Bell {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;

operation SetQubitState(desired : Result, q1 : Qubit) : Unit {


if desired != M(q1) {
X(q1);
}
}
}

Esta operação agora pode ser chamada para definir um qubit como um estado clássico, retornando Zero 100%
do tempo ou retornando One 100% do tempo. Zero e One são constantes que representam os únicos dois
possíveis resultados de uma medição de um qubit.
A operação SetQubitState mede o qubit. Se o qubit estiver no estado que desejamos, SetQubitState o deixará
sozinho; caso contrário, executando a operação X , alteraremos o estado do qubit para o desejado.
Sobre operações Q#
Uma operação Q# é uma sub-rotina quântica. Ou seja, é uma rotina chamável que contém outras operações
quânticas.
Os argumentos de uma operação são especificados como uma tupla, entre parênteses.
O tipo de retorno da operação é especificado após dois-pontos. Nesse caso, a operação SetQubitState não tem
tipo de retorno, portanto, é marcada como retornando Unit . Esse é o Q# equivalente de unit em F#, que é
basicamente análogo a void em C# e uma tupla vazia em Python ( () , representada pela dica de tipo
Tuple[()] ).

Você usou duas operações quânticas em sua primeira operação Q#:


A operação M , que mede o estado do qubit
A operação X , que inverte o estado de um qubit

Uma operação quântica transforma o estado de um qubit. Algumas vezes, as pessoas falam sobre portões
quânticos em vez de operações, em analogia a portões lógicos clássicos. Isso está enraizado nos primórdios da
computação quântica, quando os algoritmos eram meramente um constructo teórico e eram visualizados como
diagramas de forma semelhante à dos diagramas de circuito na computação clássica.
Resultados da medição de contagem
Para demonstrar o efeito da operação SetQubitState , uma operação TestBellState é adicionada. Essa
operação aceita como entrada um Zero ou One e chama a operação SetQubitState algum número de vezes
com essa entrada, além de contar o número de vezes que Zero foi retornado da medição do qubit e do número
de vezes que One foi retornado. É claro que, nesta primeira simulação da operação TestBellState , esperamos
que a saída mostre que todas as medições do conjunto de qubits com Zero como a entrada do parâmetro
retornarão Zero e todas as medições de um conjunto de qubits com One como a entrada do parâmetro
retornarão One . Mais adiante, adicionaremos código a TestBellState para demonstrar a sobreposição e o
entrelaçamento.
Adicione a seguinte operação ao arquivo Program.qs , dentro do namespace, após o final da operação
SetQubitState :

operation TestBellState(count : Int, initial : Result) : (Int, Int) {

mutable numOnes = 0;
use qubit = Qubit();
for test in 1..count {
SetQubitState(initial, qubit);
let res = M(qubit);

// Count the number of ones we saw:


if res == One {
set numOnes += 1;
}
}

SetQubitState(Zero, qubit);

// Return number of times we saw a |0> and number of times we saw a |1>
Message("Test results (# of 0s, # of 1s): ");
return (count - numOnes, numOnes);
}

Observe que adicionamos uma linha antes de return para imprimir uma mensagem explicativa no console
com a função ( Message ) [microsoft.quantum.intrinsic.message]
Essa operação ( TestBellState ) executará um loop para iterações count , definirá um valor initial
especificado em um qubit e, em seguida, medirá ( M ) o resultado. Ela coletará estatísticas sobre quantos zeros e
aqueles medidos e os retornará ao chamador. Ela executa outra operação necessária. Ela redefine o qubit para
um estado conhecido ( Zero ) antes de retorná-lo, permitindo que outras pessoas aloquem esse qubit em um
estado conhecido. Isso é exigido pela instrução use .
Sobre as variáveis em #
Por padrão, as variáveis em Q# são imutáveis; o valor delas não pode ser alterado depois que elas são
associadas. A palavra-chave let é usada para indicar a associação de uma variável imutável. Os argumentos da
operação são sempre imutáveis.
Caso precise de uma variável cujo valor possa ser alterado, como numOnes no exemplo, você poderá declarar a
variável com a palavra-chave mutable . O valor de uma variável mutável pode ser alterado usando uma
instrução set .
Em ambos os casos, o tipo de uma variável é inferido pelo compilador. Q# não requer nenhuma anotação de
tipo para variáveis.
Sobre as instruções use em Q#
A instrução use também é especial para Q#. Ela é usada para alocar qubits para uso em um bloco de código.
Em Q#, todos os qubits são alocados e liberados dinamicamente, em vez de serem recursos fixos que existem
pelo tempo de vida inteiro de um algoritmo complexo. Uma instrução use aloca um conjunto de qubits no
início e libera esses qubits no final do bloco.

Execute o código no prompt de comando


Para executar o código, precisamos dizer ao compilador que chamável executar quando fornecemos o comando
dotnet run . Isso é feito com uma simples alteração no arquivo Q#, adicionando uma linha com @EntryPoint()
diretamente antes do chamável: a operação TestBellState nesse caso. O código completo deve ser:
namespace Bell {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;

operation SetQubitState(desired : Result, target : Qubit) : Unit {


if desired != M(target) {
X(target);
}
}

@EntryPoint()
operation TestBellState(count : Int, initial : Result) : (Int, Int) {

mutable numOnes = 0;
use qubit = Qubit();
for test in 1..count {
SetQubitState(initial, qubit);
let res = M(qubit);

// Count the number of ones we saw:


if res == One {
set numOnes += 1;
}
}

SetQubitState(Zero, qubit);

// Return number of times we saw a |0> and number of times we saw a |1>
Message("Test results (# of 0s, # of 1s): ");
return (count - numOnes, numOnes);
}
}

Para executar o programa, precisamos especificar os argumentos count e initial no prompt de comando.
Vamos escolher, por exemplo, count = 1000 e initial = One . Digite o seguinte comando:

dotnet run --count 1000 --initial One

Você deverá obter a seguinte saída:

Test results (# of 0s, # of 1s):


(0, 1000)

Se você tentar com initial = Zero , deverá observar:

dotnet run --count 1000 --initial Zero

Test results (# of 0s, # of 1s):


(1000, 0)

Preparar a superposição
Agora vamos examinar como o Q# expressa maneiras de colocar qubits em sobreposição. Lembre-se de que o
estado de um qubit pode estar em uma sobreposição de 0 e 1. Usaremos a operação Hadamard para fazer isso.
Se o qubit estiver em qualquer um dos estados clássicos (em que uma medição retorna Zero sempre ou One
sempre), a operação Hadamard ou H colocará o qubit em um estado em que uma medição do qubit retornará
Zero 50% do tempo e retornará One 50% do tempo. Conceitualmente, o qubit pode ser pensado como a
metade entre o Zero e o One . Agora, quando simularmos a operação TestBellState , veremos que os
resultados retornarão aproximadamente um número igual de Zero e One após a medição.
X inverte o estado qubit
Primeiro, tentaremos inverter o qubit (se o qubit estiver no Zero , o estado será invertido para One e vice-
versa). Isso é feito com a execução de uma operação X antes de medi-lo em TestBellState :

X(qubit);
let res = M(qubit);

Agora os resultados estão invertidos:

dotnet run --count 1000 --initial One

Test results (# of 0s, # of 1s):


(1000, 0)

dotnet run --count 1000 --initial Zero

Test results (# of 0s, # of 1s):


(0, 1000)

Agora vamos explorar as propriedades quânticas dos qubits.


H prepara a sobreposição
Basta substituir a operação X na execução anterior por uma operação H ou Hadamard. Em vez de
inverteremos o qubit até o final de 0 a 1, só o inverteremos até a metade. As linhas substituídas em
TestBellState agora têm a seguinte aparência:

H(qubit);
let res = M(qubit);

Agora os resultados ficam mais interessantes:

dotnet run --count 1000 --initial One

Test results (# of 0s, # of 1s):


(496, 504)

dotnet run --count 1000 --initial Zero

Test results (# of 0s, # of 1s):


(506, 494)

Sempre que medimos, pedimos um valor clássico, mas como o qubit está na metade entre 0 e 1, obtemos
(estatisticamente) 0 na metade do tempo e 1 na metade do tempo. Isso é conhecido como superposição e nos
dá nossa primeira visão real de um estado quântico.

Preparar o entrelaçamento
Agora vamos examinar como o Q# expressa maneiras de entrelaçar qubits. Primeiro, definimos o primeiro qubit
como o estado inicial e usamos a operação H para colocá-lo em sobreposição. Em seguida, antes de medirmos
o primeiro qubit, usamos uma nova operação ( CNOT ), que significa Controlled-NOT. O resultado da execução
dessa operação em dois qubits será inverter o segundo qubit se o primeiro qubit for One . Agora, os dois qubits
estão entrelaçados. Nossas estatísticas para o primeiro qubit não mudaram (possibilidade de 50-50 de Zero ou
One após a medição), mas agora, quando medimos o segundo qubit, ele é sempre igual ao que medimos para
o primeiro qubit. Nosso CNOT entrelaçou os dois qubits, de modo que o que acontecer com um deles
acontecerá com o outro. Se você inverter as medidas (fizer o segundo qubit antes do primeiro), a mesma coisa
acontecerá. A primeira medida será aleatória e a segunda estará na etapa de bloqueio com tudo o que foi
descoberto para o primeiro.
A primeira coisa que precisaremos fazer é alocar dois qubits em vez de um em TestBellState :

use (q0, q1) = (Qubit(), Qubit());

Isso nos permitirá adicionar uma nova operação ( CNOT ) antes de medir ( M ) em TestBellState :

SetQubitState(initial, q0);
SetQubitState(Zero, q1);

H(q0);
CNOT(q0, q1);
let res = M(q0);

Adicionamos outra operação SetQubitState para inicializar o primeiro qubit, a fim de garantir que ele esteja
sempre no estado Zero quando começarmos.
Também precisamos redefinir o segundo qubit antes de liberá-lo.

SetQubitState(Zero, q0);
SetQubitState(Zero, q1);

A rotina completa agora tem esta aparência:


operation TestBellState(count : Int, initial : Result) : (Int, Int) {

mutable numOnes = 0;
use (q0, q1) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q0);
SetQubitState(Zero, q1);

H(q0);
CNOT(q0,q1);
let res = M(q0);

// Count the number of ones we saw:


if res == One {
set numOnes += 1;
}
}

SetQubitState(Zero, q0);
SetQubitState(Zero, q1);

// Return number of times we saw a |0> and number of times we saw a |1>
return (count-numOnes, numOnes);
}

Se executarmos isso, obteremos exatamente o mesmo resultado 50-50 que obtivemos antes. No entanto,
estamos interessados mesmo em como o segundo qubit reage ao primeiro que está sendo medido.
Adicionaremos essa estatística com uma nova versão da operação TestBellState :

operation TestBellState(count : Int, initial : Result) : (Int, Int, Int) {


mutable numOnes = 0;
mutable agree = 0;
use (q0, q1) = (Qubit(), Qubit());
for test in 1..count {
SetQubitState(initial, q0);
SetQubitState(Zero, q1);

H(q0);
CNOT(q0, q1);
let res = M(q0);

if M(q1) == res {
set agree += 1;
}

// Count the number of ones we saw:


if res == One {
set numOnes += 1;
}
}

SetQubitState(Zero, q0);
SetQubitState(Zero, q1);

// Return times we saw |0>, times we saw |1>, and times measurements agreed
Message("Test results (# of 0s, # of 1s, # of agreements)");
return (count-numOnes, numOnes, agree);
}

O novo valor retornado ( agree ) faz o acompanhamento toda vez que a medida do primeiro qubit corresponde
à medida do segundo qubit.
Ao executar o código, obtemos:

dotnet run --count 1000 --initial One

(505, 495, 1000)

dotnet run --count 1000 --initial Zero

Test results (# of 0s, # of 1s, # of agreements)


(507, 493, 1000)

Conforme indicado na visão geral, nossas estatísticas para o primeiro qubit não foram alteradas (possibilidade
de 50-50 de 0 ou 1), mas agora, quando medimos o segundo qubit, ele é sempre igual ao que medimos para o
primeiro qubit, porque eles estão entrelaçados!

Próximas etapas
O tutorial Pesquisa de Grover mostrará como criar e executar a pesquisa de Grover, um dos algoritmos mais
populares da computação quântica, além de apresentar um bom exemplo de programa Q# que pode ser usado
para resolver problemas reais com a computação quântica.
A Introdução ao Quantum Development Kit recomenda mais maneiras de aprender o Q# e a programação
quântica.
Tutorial: Implementar o algoritmo de pesquisa de
Grover em Q#
19/05/2021 • 15 minutes to read

Neste tutorial, você aprenderá a implementar o algoritmo do Grover no Q# para resolver problemas com base
na pesquisa.
O algoritmo de Grover é um dos algoritmos mais famosos na computação quântica. O problema que ele resolve
é geralmente conhecido como "pesquisar um banco de dados", mas é mais preciso considerá-lo em termos do
problema de pesquisa.
Qualquer tarefa de pesquisa pode ser formulada matematicamente com uma função abstrata $f(x)$ que aceita
itens de pesquisa $x$. Se o item $x$ for uma solução para a tarefa de pesquisa, então $f(x)=1$. Se o item $x$
não for uma solução, então $f(x)=0$. O problema de pesquisa consiste em localizar qualquer item $x_0$ de
modo que $f(x_0)1$.

NOTE
Este tutorial destina-se a pessoas que já estão familiarizadas com o algoritmo de Grover e querem saber como
implementá-lo em Q#. Para obter um tutorial mais lento, recomendamos o módulo do Microsoft Learn Resolva problemas
de coloração de grafo usando a pesquisa de Grover. Para obter uma explicação detalhada sobre a teoria por trás do
algoritmo de Grover, verifique o artigo conceitual Teoria do algoritmo de Grover.

Tarefa do algoritmo de Grover


Dada uma função clássica $f(x):\{0,1\}^n \rightarrow\{0,1\}$, em que $n$ é o tamanho de bit do espaço de
pesquisa, encontre uma entrada $x_0$ para a qual $f(x_0)=1$.
Para implementar o algoritmo de o Grover para resolver um problema, você precisa:
1. Transformar o problema na forma de uma tarefa de Grover : por exemplo, suponha que queiramos
encontrar os fatores de um inteiro $M$ usando o algoritmo de Grover. Você pode transformar o problema
de fatoração de inteiro em uma tarefa de Grover criando uma função $$f_M(x)=1[r],$$ em que $1[r]=1$ se
$r=0$ e $1[r]=0$ se $r\neq0$ e $r$ é o resto de $M/x$. Dessa forma, os inteiros $x_i$ que compõem
$f_M(x_i)=1$ são os fatores de $M$ e transformamos o problema na tarefa de Grover.
2. Implementar a função da tarefa do Grover como um oráculo quântico: para implementar o
algoritmo de Grover, você precisa implementar a função $f(x)$ da tarefa de Grover como um oráculo
quântico.
3. Use o algoritmo de Grover com seu oráculo para resolver a tarefa: quando você tiver um oráculo
quântico, poderá conectá-lo à implementação do algoritmo de Grover para resolver o problema e interpretar
a saída.

Visão geral rápida do algoritmo de Grover


Suponha que tenhamos $N=2^n$ itens qualificados para a tarefa de pesquisa e os indexamos atribuindo a cada
item um inteiro de $0$ a $N-1$. As etapas do algoritmo são:
1. Comece com um registro de $n$ qubits inicializados no estado $\ket{0}$.
2. Prepare o registro em uma sobreposição uniforme aplicando $H$ a cada qubit no registro:
$$|\psi\rangle=\frac{1}{N^{1 / 2}} \sum_{x=0}^{N-1}|x\rangle$$
3. Aplique as seguintes operações ao registro $N_{\Text{optimal}}$ vezes:
a. A fase $O_f$ do oráculo que aplica uma mudança de fase condicional de $-1$ para os itens da
solução.
b. Aplique $H$ a cada qubit no registro.
c. Aplique $-O_0 $, uma mudança de fase condicional de $-$1 a cada estado de base computacional,
exceto $\ket{0}$.
d. Aplique $H$ a cada qubit no registro.
4. Meça o registro para obter o índice de um item que é uma solução com uma probabilidade muito alta.
5. Verifique se é uma solução válida. Caso contrário, comece novamente.

Escreva o código para o algoritmo de Grover


Agora, vamos ver como implementar o algoritmo em Q#.
Operador de difusão de Grover
Primeiro, vamos escrever uma operação que aplica as etapas b , c e d do loop acima. Juntas, essas etapas
também são conhecidas como o operador de difusão de Grover $-H^{\otimes n} O_0 H^{\otimes n}$

operation ReflectAboutUniform(inputQubits : Qubit[]) : Unit {

within {
ApplyToEachA(H, inputQubits);
ApplyToEachA(X, inputQubits);
} apply {
Controlled Z(Most(inputQubits), Tail(inputQubits));
}

Nesta operação, usamos a instrução within-apply que implementa o conjugado automático de operações que
ocorrem no operador de difusão de Grover.

NOTE
Para saber mais sobre os conjugados em Q#, confira o artigo de conjugados no guia da linguagem Q#.

Um bom exercício para entender o código e as operações é verificar com caneta e papel que a operação
ReflectAboutUniform aplica o operador de difusão de Grover. Para ver, observe que a operação
Controlled Z(Most(inputQubits),Tail(inputQubits)) só terá efeito diferente da identidade se, e apenas se, todos
os qubits estiverem no estado $\ket{1}$.
Você pode verificar o que cada uma das operações e funções usadas é examinando a documentação da API:
ApplyToEachA
Most
Tail

A operação é chamada ReflectAboutUniform porque pode ser interpretada de modo geométrico como uma
reflexão no espaço de vetor sobre o estado de sobreposição uniforme.
Número de iterações
A pesquisa de Grover tem um número ideal de iterações que geram a maior probabilidade de medir uma saída
válida. Se o nosso problema tiver $N=2^n$ itens elegíveis possíveis e $M$ deles forem soluções para o
problema, o número ideal de iterações será:
$$N_{\text{optimal}}\approx\frac{\pi}{4}\sqrt{\frac{N}{M}}$$
Continuar a iterar além desse número começa a reduzir essa probabilidade até atingirmos uma probabilidade
de sucesso quase zero na iteração $2 N_{\text{optimal}}$. Depois disso, a probabilidade aumenta novamente e
util $3 N_{\text{optimal}}$ etc.
Em aplicações práticas, você geralmente não sabe quantas soluções seu problema tem antes de resolvê-lo. Uma
estratégia eficiente para lidar com esse problema é "adivinhar" o número de soluções $M$ aumentando
progressivamente a estimativa nas potências de dois (ou seja, $1, 2, 4, 8, 16,..., 2^n$). Um desses palpites será
suficientemente próximo, pois o algoritmo ainda encontrará a solução com um número médio de iterações em
cerca de $\sqrt{\frac{N}{M}}$.
Concluir a operação de Grover
Agora estamos prontos para escrever uma operação Q# para o algoritmo de pesquisa de Grover. Ele terá três
entradas:
Uma matriz qubit register : Qubit[] que deve ser inicializada no estado tudo Zero . Esse registro codificará
a solução provisória para o problema de pesquisa. Após a operação, ela será medida.
Uma operação phaseOracle : (Qubit[]) => Unit is Adj que representa o oráculo da fase para a tarefa de
Grover. Esta operação aplica uma transformação unitária em um registro qubit genérico.
Um inteiro iterations : Int para representar as iterações do algoritmo.

operation RunGroversSearch(register : Qubit[], phaseOracle : (Qubit[]) => Unit is Adj, iterations : Int) :
Unit {
// Prepare register into uniform superposition.
ApplyToEach(H, register);
// Start Grover's loop.
for _ in 1 .. iterations {
// Apply phase oracle for the task.
phaseOracle(register);
// Apply Grover's diffusion operator.
ReflectAboutUniform(register);
}
}

Esse código é genérico. Ele pode ser usado para resolver qualquer problema de pesquisa. Passamos o oráculo
quântico – a única operação que depende do conhecimento da instância do problema que queremos resolver –
como um parâmetro para o código de pesquisa.

Implementar o oráculo
Uma das principais propriedades que torna o algoritmo de Grover mais rápido é a capacidade dos
computadores quânticos de executar cálculos não apenas em entradas individuais, mas também em
superposições de entradas. Precisamos computar a função $f(x)$ que descreve a instância de um problema de
pesquisa usando apenas operações quânticas. Dessa forma, podemos computar isso em uma sobreposição de
entradas.
Não há um modo automático de traduzir converter funções clássicas em operações quânticas. É um campo
aberto de pesquisa em ciência da computação chamado computação reversível.
No entanto, há algumas diretrizes que podem ajudar a converter sua função $f(x)$ em um oráculo quântico:
1. Dividir a função clássica em pequenos blocos de construção fáceis de implementar. Por exemplo,
você pode tentar decompor sua função $f(x)$ em uma série de operações aritméticas ou portões de lógica
booliana.
2. Use os blocos de construção de nível superior da biblioteca Q# para implementar as operações
intermediárias. Por exemplo, se você decompuser sua função em uma combinação de operações
aritméticas simples, poderá usar a biblioteca de numéricos para implementar as operações intermediárias.
A tabela de equivalência a seguir pode ser útil ao implementar funções boolianas em Q#.

P O RTÃ O LÓ GIC O C L Á SSIC O O P ERA Ç Ã O Q #

$NOT$ X

$XOR$ CNOT

$AND$ CCNOT com um qubit auxiliar

Exemplo: operação quantum para verificar se um número é um divisor

IMPORTANT
Neste tutorial, vamos fatorar um número usando o algoritmo de pesquisa de Grover como um exemplo didático para
mostrar como converter um problema matemático em uma tarefa de Grover. No entanto, o algoritmo de Grover não
é eficiente para resolver o problema de fatoração de inteiro . Para explorar um algoritmo quântico que resolva o
problema de fatoração de inteiro com mais rapidez do que qualquer algoritmo clássico, verifique o exemplo de algoritmo
de Shor .

Como exemplo, vamos ver como expressaríamos a função $f_M(x)=1[r]$ do problema de fatoração como uma
operação quântica em Q#.
De modo clássico, computamos o resto da divisão $M/x$ e verificamos se é igual a zero. Se for, o programa
produzirá 1 ; se não for, o programa produzirá 0 . Precisamos corrigir:
Computar o restante da divisão.
Aplicar uma operação controlada no bit de saída para ela seja 1 se o restante for 0 .

Portanto, precisamos calcular uma divisão de dois números com uma operação quântica. Felizmente, você não
precisa escrever o circuito que está implementando a divisão do zero, pode usar a operação DivideI da
biblioteca de numéricos em vez disso.
Se olharmos para a descrição de DivideI , veremos que ele precisa de três registros de qubit: o dividendo de
$n$ bits xs , o divisor de $n$ bits ys e o result de $n$ bits que deve ser inicializado no estado Zero . A
operação é Adj + Ctl , portanto, podemos conjugá-la e usá-la em instruções within-apply. Além disso, a
descrição diz que o dividendo no registro de entrada xs é substituído pelo resto. Isso é perfeito, pois estamos
interessados exclusivamente no resto, não no resultado da operação.
Em seguida, podemos criar uma operação quântica que faz o seguinte:
1. Usa três entradas:
O dividendo, number : Int . É o $M$ em $f_M(x)$.
Uma matriz qubit que codifica o divisor, divisorRegister : Qubit[] . É o $x$ in $f_M(x)$,
possivelmente em um estado de sobreposição.
Um qubit de destino, target : Qubit , que muda se a saída de $f_M(x)$ é $1$.
2. Calcula a divisão $M/x$ usando apenas operações quânticas reversíveis e inverte o estado de target se, e
apenas se, o resto é zero.
3. Reverte todas as operações, exceto a inversão de target , para retornar os qubits auxiliares usados para o
estado zero sem introduzir operações irreversíveis, como medição. Esta etapa é importante para preservar o
entrelaçamento e a sobreposição durante o processo.
O código para implementar essa operação quântica é:
operation MarkDivisor (
dividend : Int,
divisorRegister : Qubit[],
target : Qubit
) : Unit is Adj + Ctl {
// Calculate the bit-size of the dividend.
let size = BitSizeI(dividend);
// Allocate two new qubit registers for the dividend and the result.
use dividendQubits = Qubit[size];
use resultQubits = Qubit[size];
// Create new LittleEndian instances from the registers to use DivideI
let xs = LittleEndian(dividendQubits);
let ys = LittleEndian(divisorRegister);
let result = LittleEndian(resultQubits);

// Start a within-apply statement to perform the operation.


within {
// Encode the dividend in the register.
ApplyXorInPlace(dividend, xs);
// Apply the division operation.
DivideI(xs, ys, result);
// Flip all the qubits from the remainder.
ApplyToEachA(X, xs!);
} apply {
// Apply a controlled NOT over the flipped remainder.
Controlled X(xs!, target);
// The target flips if and only if the remainder is 0.
}
}

NOTE
Aproveitamos a instrução within-apply para obter a etapa 3. Como alternativa, poderíamos gravar explicitamente
adjuntos de cada uma das operações dentro do bloco within após a inversão controlada do target . A instrução
within-apply faz isso para nós, tornando o código mais curto e mais legível. Uma das principais metas do Q# é tornar os
programas quânticos fáceis de escrever e ler.

Transforme a operação em um oráculo de fase


A operação MarkDivisor é o que é conhecido como um oráculo de marcação, já que marca os itens válidos com
um qubit auxiliar entrelaçado ( target ). No entanto, o algoritmo de Grover precisa de um oráculo de fase, ou
seja, um oráculo que aplica uma mudança de fase condicional de $-1$ aos itens da solução. Porém, não entre
em pânico. A operação acima não foi escrita em vão. É muito fácil mudar de um tipo de oráculo para o outro em
Q#.
Podemos aplicar qualquer oráculo de marcação racle como um oráculo de fase com a seguinte operação:

operation ApplyMarkingOracleAsPhaseOracle(
markingOracle : (Qubit[], Qubit) => Unit is Adj,
register : Qubit[]
) : Unit is Adj {
use target = Qubit();
within {
X(target);
H(target);
} apply {
markingOracle(register, target);
}
}
Essa transformação famosa é geralmente conhecida como reação de fase e é amplamente usada em muitos
algoritmos de computação quântica. Você pode encontrar uma explicação detalhada dessa técnica neste módulo
do Microsoft Learn.

Como fatorar números com a pesquisa de Grover


Agora temos todos os ingredientes para implementar uma determinada instância do algoritmo de pesquisa de
Grover e resolver nosso problema de fatoração.
Vamos usar o programa abaixo para encontrar um fator de 21. Para simplificar o código, vamos supor que
sabemos o número $M$ de itens válidos. Nesse caso, $M=4$, já que há dois fatores, 3 e 7, mais 1 e 21 em si.

namespace GroversTutorial {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.Preparation;

@EntryPoint()
operation FactorizeWithGrovers(number : Int) : Unit {

// Define the oracle that for the factoring problem.


let markingOracle = MarkDivisor(number, _, _);
let phaseOracle = ApplyMarkingOracleAsPhaseOracle(markingOracle, _);
// Bit-size of the number to factorize.
let size = BitSizeI(number);
// Estimate of the number of solutions.
let nSolutions = 4;
// The number of iterations can be computed using the formula.
let nIterations = Round(PI() / 4.0 * Sqrt(IntAsDouble(size) / IntAsDouble(nSolutions)));

// Initialize the register to run the algorithm


use (register, output) = (Qubit[size], Qubit());
mutable isCorrect = false;
mutable answer = 0;
// Use a Repeat-Until-Succeed loop to iterate until the solution is valid.
repeat {
RunGroversSearch(register, phaseOracle, nIterations);
let res = MultiM(register);
set answer = BoolArrayAsInt(ResultArrayAsBoolArray(res));
// Check that if the result is a solution with the oracle.
markingOracle(register, output);
if MResetZ(output) == One and answer != 1 and answer != number {
set isCorrect = true;
}
ResetAll(register);
} until isCorrect;

// Print out the answer.


Message($"The number {answer} is a factor of {number}.");

operation MarkDivisor (
dividend : Int,
divisorRegister : Qubit[],
target : Qubit
) : Unit is Adj+Ctl {
let size = BitSizeI(dividend);
use (dividendQubits, resultQubits) = (Qubit[size], Qubit[size]);
let xs = LittleEndian(dividendQubits);
let ys = LittleEndian(divisorRegister);
let ys = LittleEndian(divisorRegister);
let result = LittleEndian(resultQubits);
within{
ApplyXorInPlace(dividend, xs);
DivideI(xs, ys, result);
ApplyToEachA(X, xs!);
}
apply{
Controlled X(xs!, target);
}
}

operation PrepareUniformSuperpositionOverDigits(digitReg : Qubit[]) : Unit is Adj + Ctl {


PrepareArbitraryStateCP(ConstantArray(10, ComplexPolar(1.0, 0.0)), LittleEndian(digitReg));
}

operation ApplyMarkingOracleAsPhaseOracle(
markingOracle : (Qubit[], Qubit) => Unit is Adj,
register : Qubit[]
) : Unit is Adj {
use target = Qubit();
within {
X(target);
H(target);
} apply {
markingOracle(register, target);
}
}

operation RunGroversSearch(register : Qubit[], phaseOracle : ((Qubit[]) => Unit is Adj), iterations :


Int) : Unit {
ApplyToEach(H, register);
for _ in 1 .. iterations {
phaseOracle(register);
ReflectAboutUniform(register);
}
}

operation ReflectAboutUniform(inputQubits : Qubit[]) : Unit {


within {
ApplyToEachA(H, inputQubits);
ApplyToEachA(X, inputQubits);
} apply {
Controlled Z(Most(inputQubits), Tail(inputQubits));
}
}
}

IMPORTANT
Para poder usar as operações da biblioteca de numéricos (ou qualquer outra biblioteca além da biblioteca padrão),
precisamos garantir que o pacote correspondente tenha sido adicionado ao nosso projeto. Para um modo rápido de fazer
isso no VS Code, abra o terminal no seu projeto e execute o seguinte comando:
dotnet add package Microsoft.Quantum.Numerics

Execute -o com o Visual Studio ou o Visual Studio Code


O programa executará a operação ou a função marcada com o atributo @EntryPoint() em um simulador ou em
um avaliador de recurso, dependendo da configuração do projeto e das opções de linha de comando.
No Visual Studio, basta pressionar Ctrl + F5 para executar o script.
No VS Code, compile o Program.qs pela primeira vez digitando o seguinte no terminal:
dotnet build

Para as próximas execuções, não é necessário compilá-lo novamente. Para executá-lo, digite o seguinte
comando e pressione Enter:

dotnet run --no-build --number 21

Depois de pressionar Enter, você deverá ver a seguinte mensagem exibida no terminal:

The number 7 is a factor of 21.

Extra: verifique as estatísticas com o Python


Como você pode verificar se o algoritmo está se comportando corretamente? Por exemplo, se substituímos a
pesquisa de Grover por um gerador de números aleatórios no código acima, após ~$N$ tentativas, ele também
vai encontrar um fator.
Vamos escrever um pequeno script do Python para verificar se o programa está funcionando como deveria.

TIP
Se precisar de ajuda para executar aplicativos Q# no Python, você pode dar uma olhada em nosso guia sobre maneiras de
executar um programa Q# e no guia de instalação do Python.

Primeiro, vamos modificar nossa operação principal para se livrar do loop repeat-until-successful, em vez de
gerar o primeiro resultado de medida depois de executar a pesquisa de Grover:

@EntryPoint()
operation FactorizeWithGrovers2(number : Int) : Int {

let markingOracle = MarkDivisor(number, _, _);


let phaseOracle = ApplyMarkingOracleAsPhaseOracle(markingOracle, _);
let size = BitSizeI(number);
let nSolutions = 4;
let nIterations = Round(PI() / 4.0 * Sqrt(IntAsDouble(size) / IntAsDouble(nSolutions)));

use register = Qubit[size] {


RunGroversSearch(register, phaseOracle, nIterations);
let res = MultiM(register);
return ResultArrayAsInt(res);
// Check whether the result is correct.
}

Observe que alteramos o tipo de saída de Unit para Int . Isso será útil para o programa Python.
O programa Python é muito simples. Ele apenas chama a operação FactorizeWithGrovers2 várias vezes e plota
os resultados em um histograma.
O código é o seguinte:
import qsharp
qsharp.packages.add("Microsoft.Quantum.Numerics")
qsharp.reload()
from GroversTutorial import FactorizeWithGrovers2
import matplotlib.pyplot as plt
import numpy as np

def main():

# Instantiate variables
frequency = {}
N_Experiments = 1000
results = []
number = 21

# Run N_Experiments times the Q# operation.


for i in range(N_Experiments):
print(f'Experiment: {i} of {N_Experiments}')
results.append(FactorizeWithGrovers2.simulate(number = number))

# Store the results in a dictionary


for i in results:
if i in frequency:c
frequency[i]=frequency[i]+1
else:
frequency[i]=1

# Sort and print the results


frequency = dict(reversed(sorted(frequency.items(), key=lambda item: item[1])))
print('Output, Frequency' )
for k, v in frequency.items():
print(f'{k:<8} {v}')

# Plot an histogram with the results


plt.bar(frequency.keys(), frequency.values())
plt.xlabel("Output")
plt.ylabel("Frequency of the outputs")
plt.title("Outputs for Grover's factoring. N=21, 1000 iterations")
plt.xticks(np.arange(1, 33, 2.0))
plt.show()

if __name__ == "__main__":
main()

NOTE
A linha from GroversTutorial import FactorizeWithGrovers2 no programa Python importa o código Q# que
escrevemos anteriormente. Observe que o nome do módulo Python ( GroversTutorial ) precisa ser idêntico ao
namespace da operação que desejamos importar (neste caso, FactorizeWithGrovers2 ).

O programa gera o seguinte histograma:


Como você pode ver no histograma, o algoritmo gera as soluções para o problema de pesquisa (1, 3, 7 e 21)
com uma probabilidade muito maior do que as não soluções. Você pode considerar o algoritmo de Grover
como um gerador quântico aleatório intencionalmente polarizado em relação a esses índices que são soluções
para o problema de pesquisa.

Próximas etapas
Agora que você sabe como implementar o algoritmo de Grover, tente transformar um problema matemático em
uma tarefa de pesquisa e solucioná-lo com o algoritmo de Grover e Q#.
Tutorial: Escreva e simule programas em nível de
qubit em #
19/05/2021 • 21 minutes to read

Bem-vindo(a) ao tutorial do QDK (Quantum development kit) sobre como escrever e simular um programa
quântico básico que opera em qubits individuais.
Embora o Q# tenha sido criado principalmente como uma linguagem de programação de alto nível para
programas quânticos de grande escala, ele pode ser facilmente usado para explorar o nível inferior dos
programas quânticos: lidar diretamente com qubits específico. A flexibilidade do Q# permite aos usuários
abordar sistemas quânticos em qualquer nível de abstração. Neste tutorial, vamos nos aprofundar nos próprios
qubits. Especificamente, examinamos os bastidores da transformação quântica de Fourier, uma sub-rotina que é
integral para muitos algoritmos quânticos maiores.
Observe que essa exibição de nível 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 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 um "diagrama de circuito". Em nosso caso, vamos definir uma operação Q# para executar a
transformação quântica de Fourier de três qubit completa, que tem a seguinte representação como um circuito:

Pré-requisitos
Instalar o QDK usando sua linguagem e seu ambiente de desenvolvimento preferenciais.
Caso já tenha o QDK instalado, verifique se você o atualizou para a última versão

Neste tutorial, você aprenderá como:


Definir operações quânticas em Q#
Chamar operações Q# diretamente do prompt de comando ou usando um programa de host clássico
Simular uma operação quântica de alocação qubit para saída de medida
Observe como a wavefunction simulada do sistema quântico evolui em toda a operação
Executar um programa quântico com o QDK normalmente consiste em duas partes:
1. O próprio programa, que é implementado usando a linguagem de programação quântica Q# e então
invocado para ser executado em um computador quântico ou simulador quântico. São formados por
Operações Q#: sub-rotinas que atuam em registros quânticos e
Funções Q#: sub-rotinas clássicas usadas dentro do algoritmo quântico.
2. O ponto de entrada usado para chamar o programa quântico e especificar o computador de destino no qual
ele deve ser executado. Isso pode ser feito diretamente do prompt de comando ou por meio de um
programa de host escrito em uma linguagem de programação clássica, como Python ou C#. Este tutorial
inclui instruções para qualquer método que você preferir.

Alocar qubits e definir operações quânticas


A primeira parte deste tutorial consiste em definir a operação Q# Perform3qubitQFT , que executa a
transformação quântica de Fourier em três qubits.
Além disso, usaremos a função DumpMachine para observar como a wavefunction simulada de nosso sistema de
três qubit evolui na operação.
A primeira etapa é criar seu projeto e arquivo Q#. As etapas para isso dependem do ambiente que você usará
para chamar o programa e você pode encontrar os detalhes nos respectivos guias de instalação.
Vamos conduzir você pelos componentes do arquivo passo a passo, mas o código também está disponível
como um bloco completo abaixo.
Namespaces para acessar outras operações Q#
Dentro do arquivo, primeiro definimos o namespace NamespaceQFT que será acessado pelo compilador. Para que
nossa operação use operações Q# existentes, abrimos os namespaces Microsoft.Quantum.<> relevantes.

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, definimos a operação Perform3qubitQFT :

operation Perform3qubitQFT() : Unit {


// do stuff
}

Por enquanto, a operação não usa argumentos e não retorna nada. Nesse caso, escrevemos que ela retorna um
objeto Unit , que é semelhante a void em C# ou uma tupla vazia, Tuple[()] , em Python. Posteriormente,
vamos modificá-lo para retornar uma matriz de resultados de medição, quando Unit será substituído por
Result[] .

Alocar qubits com use

Em nossa operação Q#, primeiro alocamos um registro de três qubits com a palavra-chave use :

use qs = Qubit[3];

Message("Initial state |000>:");


DumpMachine();

Com use, os qubits são alocados automaticamente no estado $\ket {0}$. Podemos verificar isso usando
Message(<string>) e DumpMachine() , que imprime uma cadeia de caracteres e o estado atual do sistema para o
console.
NOTE
As funções Message(<string>) e (de Microsoft.Quantum.Intrinsic e
DumpMachine()
Microsoft.Quantum.Diagnostics , respectivamente) são impressas diretamente no console. Assim como uma
computação quântica real, Q# não nos permite acessar diretamente os estados de qubit. No entanto, como
DumpMachine imprime o estado atual do computador de destino, ele pode fornecer insight valioso para depuração e
aprendizagem quando usado em conjunto com o simulador de estado completo.

Como aplicar portões controlados e de qubit único


Em seguida, aplicamos os portões que compõem a própria operação. Q# já contém muitos portões básicos de
quantum como operações no namespace Microsoft.Quantum.Intrinsic , e isso não é exceção.
Dentro de uma operação Q#, as instruções que invocam os chamáveis, é claro, serão executadas em ordem
sequencial. Portanto, o primeiro portão a ser aplicado é o H (Hadamard) para o primeiro qubit:

Para aplicar uma operação a um qubit específico de um registro (por exemplo, um só Qubit de uma matriz
Qubit[] ), usamos a notação de índice padrão. Portanto, aplicar o H ao primeiro qubit do nosso registro qs
assume a forma:

H(qs[0]);

Além de aplicar o portão H (Hadamard) a qubits individuais, o circuito QFT consiste principalmente em
rotações R1 controladas. Uma operação R1(θ, <qubit>) em geral deixa o componente $\ket{0}$ do qubit
inalterado, ao mesmo tempo em que aplica uma rotação de $e^{i\theta}$ ao componente $\ket{1}$.
Operações controladas
Q# torna extremamente fácil condicionar a execução de uma operação em um ou vários qubits de controle. Em
geral, simplesmente precedemos a chamada com Controlled e os argumentos da operação são alterados
como:
Op(<normal args>) $\para$ Controlled Op([<control qubits>], (<normal args>)) .
Observe que os qubits de controle devem ser fornecidos como uma matriz, mesmo que sejam um só qubit.
Após H , vemos que os próximos portões são os portões R1 que atuam no primeiro qubit (e controlados pela
segundo/terceiro):

Chamamos isso com


Controlled R1([qs[1]], (PI()/2.0, qs[0]));
Controlled R1([qs[2]], (PI()/4.0, qs[0]));

Observe que usamos a função PI() do namespace Microsoft.Quantum.Math para definir as rotações em termos
de pi radianos. Além disso, dividimos por um Double (por exemplo, 2.0 ) porque dividir por um inteiro 2
geraria um erro de tipo.

TIP
R1(π/2) e R1(π/4) são equivalentes às operações S e T (também em Microsoft.Quantum.Intrinsic ).

Depois de aplicar as operações H relevantes e as rotações controladas ao segundo e ao terceiro qubit:

//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));

//third qubit:
H(qs[2]);

Precisamos apenas aplicar um portão SWAP para concluir o circuito:

SWAP(qs[2], qs[0]);

Isso é necessário porque a natureza da transformação quântica de Fourier gera o qubits na ordem inversa,
portanto, as trocas permitem a integração direta da sub-rotina a algoritmos maiores.
Com isso, terminamos de escrever as operações em nível de qubit da transformação quântica de Fourier na
nossa operação Q#:

No entanto, não podemos encerrar por aqui. Nossos qubits estavam no estado $\ket{0}$ quando os alocamos e,
muito parecido com o que acontece na vida, em Q#, devemos deixar as coisas da mesma forma como as
encontramos (ou melhor!).
Desalocar qubits
Chamamos DumpMachine() novamente para ver o estado de pós-operação e, por fim, aplicamos ResetAll ao
registro qubit para redefinir nosso qubits para $\ket{0}$ antes de concluir a operação:

Message("After:");
DumpMachine();

ResetAll(qs);

Exigir que todos os qubits desalocados sejam explicitamente definidos como $\ket{0}$ é um recurso básico do
Q#, pois permite que outras operações saibam seu estado precisamente quando começam a usar esses mesmos
qubits (um recurso escasso). Além disso, isso garante que eles não sejam entrelaçados com nenhum outro qubit
no sistema. Se a redefinição não for executada no final de um bloco de alocação use , um erro de runtime
poderá ser gerado.
O arquivo Q# completo agora deve ser assim:

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];

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);

}
}

Com o arquivo Q# e a operação concluídos, nosso programa quântico está pronto para ser chamado e
simulado.

Execute o programa
Depois de definir nossa operação Q# em um arquivo .qs , agora precisamos chamar essa operação e observar
os dados clássicos retornados. Por enquanto, não há nada retornado (lembre-se de que nossa operação definida
acima retorna Unit ), porém, quando modificamos posteriormente a operação Q# para retornar uma matriz de
resultados de medida ( Result[] ), abordaremos isso.
Embora o programa Q# esteja onipresente em todos os ambientes usados para chamá-lo, a maneira de fazer
isso é, com certeza, diferente. Assim, basta seguir as instruções na guia correspondente à sua configuração:
trabalhar no aplicativo Q# ou usar um programa de host em Python ou C#.

Prompt de comando
Python
C#

A execução do programa Q# por meio do prompt de comando requer apenas uma pequena alteração no
arquivo Q#.
Basta adicionar @EntryPoint() a uma linha anterior à definição da operação:

@EntryPoint()
operation Perform3qubitQFT() : Unit {
// ...

Para executar o programa, abra o terminal na pasta do seu projeto e insira

dotnet run

Após a conclusão, você deverá ver as saídas Message e DumpMachine abaixo impressas em seu console.

Initial state |000>:


# wave function for qubits with ids (least to most significant): 0;1;2
|0>: 1.000000 + 0.000000 i == ******************** [ 1.000000 ] --- [ 0.00000 rad ]
|1>: 0.000000 + 0.000000 i == [ 0.000000 ]
|2>: 0.000000 + 0.000000 i == [ 0.000000 ]
|3>: 0.000000 + 0.000000 i == [ 0.000000 ]
|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 ]
After:
# 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 ]

Quando chamado no simulador de estado completo, DumpMachine() fornece essas várias representações da
wavefunction do estado quântico. Os possíveis estados de um sistema de $n$ qubit podem ser representados
por estados de base computacional $2^n$, cada um com um coeficiente complexo correspondente
(simplesmente uma amplitude e uma fase). Os estados de base computacional correspondem a todas as
possíveis cadeias de caracteres binárias de comprimento $n$. Ou seja, todas as combinações possíveis de
estados 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 as IDs dos qubits correspondentes na ordem significativa. O qubit
2 ser o "mais significativo" significa simplesmente que, na representação binária do vetor de estado base
$\ket{i}$, o estado de qubit 2 corresponde ao dígito mais à esquerda. Por exemplo, $\ket{6} = \ket{110}$
compreende qubits 2 e 1 , ambos em $\ket{1}$ e qubit 0 em $\ket{0}$.
O restante das linhas descreve a amplitude de probabilidade de medir o vetor de estado base $\ket{i}$ nos
formatos cartesiano e polar. Em detalhes para a primeira linha do nosso estado de entrada $\ket{000}$:
|0>: essa linha corresponde ao estado 0 de base computacional (considerando que nossa pós-alocação de
estado inicial era $\ket{000}$, esperamos que esse seja o único estado com amplitude de probabilidade
neste ponto).
1.000000 + 0.000000 i : a amplitude de probabilidade no formato cartesiano.
== : o sinal equal separa as representações equivalentes.
******************** : uma representação gráfica da magnitude, o número de * é proporcional à
probabilidade de medir esse vetor de estado.
[ 1.000000 ] : o valor numérico da magnitude
--- : uma representação gráfica da fase de amplitude.
[ 0.0000 rad ] : o valor numérico da fase (em radianos).

Tanto a magnitude quanto a fase são exibidas com uma representação gráfica. A representação de magnitude é
simples: ela mostra uma barra de * e, quanto maior a probabilidade, maior a barra. Para a fase, confira Como
testar e depurar: funções de despejo para as possíveis representações de símbolo com base em intervalos de
ângulo.
Portanto, a saída impressa está ilustrando que nossos portões programados transformaram nosso estado de
$$ \ket{\psi}_{initial} = \ket{000} $$
como
$$ \begin{align} \ket{\psi}_{final} &= \frac{1}{\sqrt{8}} \left( \ket{000} + \ket{001} + \ket{010} + \ket{011} +
\ket{100} + \ket{101} + \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 de Fourier de 3 qubits.
Se você estiver curioso sobre como outros estados de entrada são afetados, experimente aplicar operações
qubit antes da transformação.

Como adicionar medidas


Infelizmente, uma base da mecânica quântica nos informa que um sistema quântico real não pode ter uma
função DumpMachine . Em vez disso, somos forçados a extrair informações por meio de medidas, o que, em geral,
não apenas falha em nos fornecer o estado quântico completo, como também pode alterar drasticamente o
próprio sistema. Há muitos tipos de medidas quânticas, mas vamos nos concentrar 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.
Para implementar medidas dentro de um programa Q#, usamos a operação M (de
Microsoft.Quantum.Intrinsic ), que retorna um tipo Result .

Primeiro, modificamos nossa operação Perform3QubitQFT para retornar uma matriz de resultados de medição,
Result[] em vez de Unit .

operation Perform3QubitQFT() : Result[] {

Definir e inicializar a matriz Result[]

Antes de alocar qubits (por exemplo, antes da instrução use ), declaramos e associamos essa matriz de
comprimento 3 (um Result para cada qubit):

mutable resultArray = new Result[3];

A palavra-chave mutable que precede resultArray permite que a variável seja reassociada posteriormente no
código, por exemplo, ao adicionar nossos resultados de medição.
Executar medidas em um loop for e adicionar resultados à matriz
Depois que as operações de transformação de Fourier inserem o seguinte código:
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}

A função IndexRange chamada em uma matriz (por exemplo, nossa matriz de qubits qs ) retorna um intervalo
sobre os índices da matriz. Aqui, nós o usamos 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.

NOTE
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;

Compreendendo os efeitos da medição


Vamos alterar o posicionamento de nossas funções DumpMachine para gerar o estado antes e depois das
medições. O código da operação final deve se assemelhar a:
operation Perform3QubitQFT() : Result[] {

mutable resultArray = new Result[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);

return resultArray;

Se você estiver trabalhando no prompt de comando, a matriz retornada será simplesmente exibida de modo
direto no console no final da execução. Caso contrário, atualize o programa de host para processar a matriz
retornada.

Prompt de comando
Python
C#

Para ter mais conhecimento da matriz retornada que será impressa no console, podemos adicionar outro
Message no arquivo Q# antes da instrução return :

Message("Post-QFT measurement results [qubit0, qubit1, qubit2]: ");


return resultArray;

Execute o projeto, e a saída deverá ser semelhante à seguinte:


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]

Essa saída ilustra alguns pontos diferentes:


1. Comparando o resultado retornado à medida anterior DumpMachine , ele claramente não ilustra a
sobreposição QFT em relação a estados de base. Uma medida retorna apenas um estado de base, com uma
probabilidade determinada pela amplitude desse estado na wavefunction do sistema.
2. Da DumpMachine pós-medição, vemos 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 precisarmos repetir essa operação muitas vezes, veremos que as estatísticas de resultado começam a ilustrar
a sobreposiçã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 não é um problema neste exemplo, mas vemos que as fases relativas
aparecerão se fosse fornecida ao QFT uma entrada mais complexa do que $\ket{000}$.
Medidas parciais
Para explorar como medir apenas algumas qubits do registro pode afetar o estado do sistema, tente adicionar o
seguinte dentro do loop for após a linha de medida:

let iString = IntAsString(i);


Message("After measurement of qubit " + iString + ":");
DumpMachine();

Observe que, para acessar a função IntAsString , você precisará adicionar

open Microsoft.Quantum.Convert;

com o restante das instruções de namespace open .


Na saída resultante, você verá a projeção gradual em subespaços conforme cada qubit é medido.

Usar as bibliotecas Q#
Como mencionamos 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.
As bibliotecas Q# contêm a operação QFT, que você pode simplesmente tomar e aplicar para qualquer número
de qubits. Para experimentar, defina uma nova operação no arquivo Q# que tenha o mesmo conteúdo de
Perform3QubitQFT , mas com tudo, desde o primeiro H até o SWAP , substituído por duas linhas fáceis:

let register = BigEndian(qs); //from Microsoft.Quantum.Arithmetic


QFT(register); //from Microsoft.Quantum.Canon

A primeira linha simplesmente cria uma expressão BigEndian da matriz alocada de qubits, qs , que a operação
QFT utiliza como um argumento. Isso corresponde à ordenação de índice do qubits no registro.
Para ter acesso a essas operações, adicione instruções open a seus respectivos namespaces no início do arquivo
Q#:

open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Arithmetic;

Agora, ajuste o programa de host para chamar o nome da nova operação (por exemplo, PerformIntrinsicQFT )e
rode-o.
Para ver o verdadeiro benefício de usar as operações de biblioteca Q#, altere o número de qubits para algo
diferente de 3 :

mutable resultArray = new Result[4];

use qs = Qubit[4];
//...

Portanto, você pode aplicar o QFT apropriado a qualquer número determinado de qubits sem precisar se
preocupar com confusão de novas operações H e rotações em cada qubit.
Observe que o simulador quântico leva exponencialmente mais tempo para ser executado à medida que você
aumenta o número de qubits. É exatamente por isso que estamos ansiosos pelo hardware quântico real.
Conheça a computação quântica com o Quantum
Katas
19/05/2021 • 5 minutes to read

O Quantum Katas é uma série de tutoriais individuais de software livre e exercícios de programação destinada a
ensinar os elementos de computação quântica e programação Q# ao mesmo tempo.

Aprender fazendo
Os tutoriais e os exercícios coletados neste projeto destacam o aprender fazendo: eles oferecem tarefas de
programação que abordam determinados tópicos que avançam desde muito simples a bastante desafiadores.
Cada tarefa solicita que você preencha alguns códigos: a primeira delas pode exigir apenas uma linha e a última
pode exigir um fragmento de código dimensionável.
O mais importante é que os katas incluem estruturas de teste que configuram, executam e validam as soluções
para as tarefas. Com isso, você receberá comentários imediatos sobre sua solução e reconsiderará sua
abordagem se ela estiver incorreta.
Você pode usar o katas para aprender no ambiente de sua escolha:
Jupyter Notebooks online no ambiente do Binder
Jupyter Notebooks em execução no computador local
Visual Studio
Visual Studio Code

O que eu posso aprender com o Quantum Katas?


Explore os conceitos básicos e fundamentais da computação quântica ou aprofunde-se nos algoritmos e
protocolos quânticos. Recomendamos que você siga este roteiro de aprendizagem no começo para ter a certeza
de que você tem uma noção sólida sobre os conceitos fundamentais da computação quântica. É claro que você
pode ignorar os tópicos com os quais está familiarizado, como aritmética complexa, e aprender os algoritmos
em qualquer ordem que desejar.
Introdução aos conceitos de computação quântica
K ATA DESC RIÇ Ã O

Aritmética complexa Este tutorial explica um pouco da experiência matemática


necessária para trabalhar com computação quântica, como
números imaginários e complexos.

Álgebra linear A álgebra linear é usada para representar os estados


quânticos e as operações na computação quântica. Este
tutorial aborda as noções básicas, incluindo matrizes e
vetores.

O conceito de um qubit Saiba mais sobre o qubits, um dos principais conceitos da


computação quântica.
K ATA DESC RIÇ Ã O

Portões quânticos de qubit único Este tutorial apresenta portões quânticos de qubit único,
que atuam como os blocos de construção de algoritmos
quânticos e que transformam estados de qubit quânticos de
várias maneiras.

Sistemas com vários qubits Este tutorial apresenta sistemas com vários qubits, sua
representação em notação matemática e em código Q# e o
conceito de entrelaçamento.

Portões quânticos de vários qubits Este tutorial segue o tutorial Portões quânticos de qubit
único e se concentra na aplicação de portões quânticos para
sistemas com vários qubits.

Conceitos sobre computação quântica


K ATA DESC RIÇ Ã O

Reconhecer portões quânticos Uma série de exercícios projetados para familiarizar-se com
os portões quânticos básicos em Q#. Inclui exercícios para
portões básico de qubit único e de vários qubits, portões
adjuntos e controlados e como usar portões para modificar
o estado de um qubit.

Criar uma sobreposição quântica Use esses exercícios para se familiarizar com o conceito de
superposição e programação em Q#. Inclui exercícios para os
portões básico de um qubit e de vários qubits, sobreposição
e controle de fluxo e recursão em Q#.

Diferenciar estados quânticos usando medidas Resolva estes exercícios enquanto aprende sobre medida
quântica e os estados ortogonal e não ortogonal.

Medidas conjuntas Saiba mais sobre as medidas de paridade conjuntas e como


usar a operação Medida para distinguir os estados
quânticos.

Algoritmos
K ATA DESC RIÇ Ã O

Portabilidade quântica Este kata explora a teleportação quântica, um protocolo que


permite a comunicação de um estado quântico usando
apenas a comunicação clássica e o entrelaçamento quântico
compartilhado anteriormente.

Codificação superdensa A codificação superdensa é um protocolo que permite a


transmissão de dois bits de informações clássicas enviando
apenas um qubit usando o entrelaçamento quântico
compartilhado anteriormente.

Algoritmo Deutsch-Jozsa Este algoritmo é famoso para ser um dos primeiros


exemplos de um algoritmo quântico exponencialmente mais
rápido do que qualquer algoritmo clássico determinístico.
K ATA DESC RIÇ Ã O

Explorar propriedades de alto nível do algoritmo de pesquisa Uma introdução de alto nível a um dos algoritmos mais
de Grover famosos na computação quântica. Ele resolve o problema de
localizar uma entrada para uma caixa preta (oráculo) que
produz uma saída específica.

Implementar o algoritmo de pesquisa de Grover Este kata se aprofunda no algoritmo de pesquisa de Grover
e aborda a escrita de oráculos, a execução de etapas do
algoritmo e, finalmente, a junção de todos.

Resolver problemas reais usando o algoritmo de Grover: Uma série de exercícios que usa o algoritmo de Grover para
problemas de SAT resolver problemas realistas, usando SAT problemas de
satisfatibilidade booliana como exemplo.

Resolver problemas reais usando o algoritmo de Grover: Este kata explora ainda mais o algoritmo de Grover, desta
problemas de coloração de grafo vez para resolver problemas de satisfação de restrição,
usando um problema de coloração de grafo como um
exemplo.

Protocolos e bibliotecas
K ATA DESC RIÇ Ã O

Protocolo BB84 para distribuição de chave quântica Saiba mais e implemente um protocolo de distribuição de
chave quântica, BB84, usando qubits para trocar chaves de
criptografia.

Código de correção de erro de inversão de bit Explore a correção de erro quântico com o mais simples dos
códigos de correção de erro quântico (QEC), o código de
inversão de bit de três qubits.

Estimativa de fase Os algoritmos de estimativa de fase são alguns dos blocos


de construção mais fundamentais da computação quântica.
Saiba mais sobre a estimativa de fase com estes exercícios
que abrangem a estimativa de fase quântica e como
preparar e executar rotinas de estimativa de fase em Q#.

Aritmética quantum: criar somadores ripple-carry Uma série detalhada de exercícios que explora a adição ripple
carry a um computador quântico. Crie um somador quântico
no local, expanda-o com um algoritmo diferente e, por fim,
crie um subtrator quântico no local.

Jogos de entrelaçamento
K ATA DESC RIÇ Ã O

Jogo CHSH Explore o entrelaçamento quântico com uma implementação


do jogo CHSH. Esse jogo não local mostra como o
entrelaçamento quântico pode ser usado para aumentar a
chance de os jogadores ganharem além do que seria
possível com uma estratégia puramente clássica.

Jogo GHZ O jogo GHZ é outro jogo não local, mas envolve três
jogadores.

Jogo do quadrado mágico Mermin-Peres Uma série de exercícios que explora a pseudotelepatia
quântica para resolver um jogo quadrado mágico.
Recursos
Confira a série completa do Quantum Katas
Executar o katas online
Histórico e contexto sobre a computação quântica
28/04/2021 • 5 minutes to read

Uma série de novas tecnologias computacionais surgiu nos últimos anos, sendo a computação quântica
possivelmente a tecnologia que exige a maior mudança de paradigma por parte dos desenvolvedores. Os
computadores quânticos foram propostos na década de 1980 por Richard Feynman e Yuri Manin. A intuição por
trás da computação quântica originou-se do que costumava ser visto como um dos maiores constrangimentos
da física: o notável progresso científico enfrentava uma incapacidade de modelar até mesmo sistemas simples.
Observe que a mecânica quântica foi desenvolvida entre 1900 e 1925 e ela continua sendo a base na qual a
química, a física da matéria condensada e as tecnologias que vão de chips de computador à iluminação LED, em
última análise, se apoiam. Ainda assim, apesar desses sucessos, até mesmo alguns dos sistemas mais simples
pareciam estar além da capacidade humana de modelagem com a mecânica quântica. Isso ocorre porque a
simulação de sistemas de, até mesmo, algumas dezenas de partículas interativas exige mais potência
computacional do que qualquer computador convencional possa fornecer em milhares de anos!
Há várias maneiras de entender por que a mecânica quântica é difícil de ser simulada. Talvez a mais simples
delas seja ver que a teoria quântica pode ser interpretada como indicando que a matéria, em um nível quântico,
está em uma série de configurações possíveis (conhecidas como estados). Ao contrário da teoria de
probabilidade clássica, essas muitas configurações do estado quântico, potencialmente observadas, podem
interferir umas com as outras como as ondas em uma poça de maré. Essa interferência impede o uso da
amostragem estatística para obter as configurações de estado quântico. Em vez disso, precisamos acompanhar
cada configuração possível na qual um sistema quântico poderá estar se desejamos entender a evolução
quântica.
Considere um sistema de elétrons em que os elétrons podem estar em qualquer uma das $40$ posições,
digamos. Os elétrons, portanto, podem estar em qualquer uma das $2^{40}$ configurações ( já que cada
posição pode ter ou não um elétron). Para armazenar o estado quântico dos elétrons em uma memória de
computador convencional, será necessário mais de $130$ GB de memória! Isso é substancial, mas dentro do
alcance de alguns computadores. Se permitíssemos que as partículas estivessem em qualquer uma das $41$
posições, haveria o dobro de configurações em $2^{41}$, que, por sua vez, exigiria mais de $260$ GB de
memória para armazenar o estado quântico. Esse jogo de aumento do número de posições não poderá ser
reproduzido indefinidamente se desejarmos armazenar o estado de forma convencional, pois excederemos
rapidamente as capacidades de memória dos computadores mais eficientes do mundo. Com algumas centenas
de elétrons, a memória necessária para armazenar o sistema excede o número de partículas no universo;
portanto, não há esperança de nossos computadores convencionais simularem a dinâmica quântica delas.
Apesar disso, por natureza, esses sistemas evoluem imediatamente no tempo de acordo com as leis mecânicas
quânticas, sem conhecimento algum da incapacidade de projetar e simular a evolução deles com a potência da
computação convencional.
Essa observação faz com que aqueles que têm uma visão pioneira da computação quântica façam uma
pergunta simples, mas significativa: podemos transformar essa dificuldade em uma oportunidade?
Especificamente, se a dinâmica quântica teria a dificuldade de simular o que aconteceria se tivéssemos criado
um hardware com efeitos quânticos como operações fundamentais? Poderíamos simular sistemas de partículas
interativas usando um sistema que explora exatamente as mesmas leis que os regem naturalmente?
Poderíamos investigar tarefas que estão totalmente ausentes de natureza, mas ainda seguir as leis mecânicas
quânticas ou se beneficiar delas? Essas perguntas levaram à gênese da computação quântica.
A base da computação quântica é armazenar informações em estados quânticos de matéria e usar operações de
portões quânticos para calcular essas informações, aproveitando a interferência quântica e aprendendo a
"programá-la". Um exemplo antecipado da interferência de programação para resolver um problema
considerado difícil em nossos computadores convencionais foi feito por Peter Shor em 1994 para um problema
conhecido como fatoração. A solução da fatoração traz consigo a capacidade de dividir muitos de nossos
sistemas de criptografia de chave pública subjacentes à segurança do comércio eletrônico atualmente, incluindo
RSA e criptografia de curva elíptica. Desde essa época, foram desenvolvidos algoritmos de computador
quântico rápidos e eficientes para muitas de nossas tarefas clássicas difíceis: simulação de sistemas físicos em
química, física e ciência da matéria, pesquisa de um banco de dados não ordenado, solução de sistemas de
equações lineares e aprendizado de máquina.
A criação de um programa quântico para utilização da interferência pode parecer um desafio assustador e,
embora ele seja, muitas técnicas e ferramentas, incluindo o Quantum Development Kit (QDK), foram
introduzidas para tornar o desenvolvimento de programação e algoritmo quânticos mais acessível. Há algumas
estratégias básicas que podem ser usadas para manipular a interferência quântica de uma maneira útil para a
computação, embora, ao mesmo tempo, não façam com que a solução se perca em um emaranhado de
possibilidades quânticas. A programação quântica é uma arte distinta da programação clássica, exigindo
ferramentas muito diferentes para entender e expressar o raciocínio de algoritmo quântico. De fato, sem
ferramentas gerais para ajudar um desenvolvedor quântico a lidar com a arte da programação quântica, o
desenvolvimento de algoritmos quânticos não é tão fácil.
O Quantum Development Kit capacita uma comunidade cada vez maior com ferramentas para possibilitar a
revolução quântica para tarefas, problemas e soluções. Nossa linguagem de programação de alto nível, Q#, foi
desenvolvida para resolver os desafios do processamento de informações quânticas. É integrada a uma pilha de
software que permite que um algoritmo quântico seja compilado até as operações primitivas de um
computador quântico. Antes de abordar a linguagem de programação, é útil examinar os princípios
fundamentais nos quais a computação quântica se baseia. Vamos usar as regras fundamentais da computação
quântica como axiomas, em vez de detalhar as bases delas na mecânica quântica. Além disso, presumiremos
uma familiaridade básica com a álgebra linear (vetores, matrizes, etc.). Caso você deseje obter um estudo mais
profundo da história e dos princípios da computação quântica, indicamos a seção de referência que contém
mais informações.
Usar vetores e matrizes na computação quântica
15/05/2021 • 6 minutes to read

É essencial ter alguma familiaridade com vetores e matrizes para entender a computação quântica. Fornecemos
abaixo uma breve introdução. Recomenda-se que os leitores interessados leiam uma bibliografia padrão sobre
álgebra linear, como Strang, G. (1993). Introdução à álgebra linear (Vol. 3). Wellesley, MA: Wellesley-Cambridge
Press ou uma referência online como Álgebra Linear.
Um vetor de coluna (ou simplesmente vetor) $v$ de dimensão (ou tamanho) $n$ é uma coleção de $n$
números complexos $ (v_1,v_2,\ldots,v_n)$ organizados como uma coluna:
$$v =\begin{bmatrix} v_1\\ v_2\\ \vdots\\ v_n \end{bmatrix}$$
A norma de um vetor $v$ é definida como $\sqrt{\sum_i|v_|^2}$. Um vetor é considerado de norma unitária
(ou como alternativa, é chamado de vetor unitário) se a sua norma for $1$. O adjunto de um vetor $v$ é
indicado como $v^\dagger$ e é definido para ser o seguinte vetor de linha, em que $*$ denota o conjugado
complexo,
$$\begin{bmatrix}v_1 \\ \vdots \\ v_n \end{bmatrix}^\dagger = \begin{bmatrix}v_1^* & \cdots & v_n^*
\end{bmatrix}$$
Observe que distinguimos entre um vetor de coluna $v$ e um vetor de linha $v^\dagger $.

Produto interno
Podemos multiplicar dois vetores juntos por meio do produto interno, também conhecido como produto de
ponto ou produto escalar. Como o nome indica, o resultado do produto interno de dois vetores é um escalar. O
produto interno fornece a projeção de um vetor para outro e é indispensável na descrição de como expressar
um vetor como uma soma de outros vetores mais simples. O produto interno entre dois vetores de coluna $u=
(u_1, u_2, \ldots, u_n)$ e $v=(v_1, v_2, \ldots, v_n)$, denotado como $\left\langle u, v\right\rangle$ é definido
como
$$ \langle u, v\rangle = u^\dagger v= \begin{bmatrix}u_1^* & \cdots & u_n^* \end{bmatrix} \begin{bmatrix}
v_1\\ \vdots\\ v_n \end{bmatrix} = u_1^{*} v_1 + \cdots + u_n^{*} v_n. $$
Essa notação também permite que a norma de um vetor $v$ seja escrita como $\sqrt{\langle v, v\rangle}$.
Podemos multiplicar um vetor com um número $c$ para formar um novo vetor cujas entradas são
multiplicadas por $c$. Também podemos adicionar dois vetores $u$ e $v$ para formar um novo vetor cujas
entradas são a soma das entradas de $u$ e $v$. Essas operações são descritas abaixo:
$$\mathrm{Se}~u =\begin{bmatrix} u_1\\ u_2\\ \vdots\\ u_n \end{bmatrix}~\mathrm{e}~ v =\begin{bmatrix}
v_1\\ v_2\\ \vdots\\ v_n \end{bmatrix},~\mathrm{então}~ au+bv =\begin{bmatrix} au_1+bv_1\\ au_2+bv_2\\
\vdots\\ au_n+bv_n \end{bmatrix}. $$
Uma matriz de tamanho $m \times n$ é uma coleção de números complexos $mn$ organizados em $m$ linhas
e $n$ colunas, conforme mostrado abaixo:
$$M = \begin{bmatrix} M_{11} ~~ M_{12} ~~ \cdots ~~ M_{1n}\\ M_{21} ~~ M_{22} ~~ \cdots ~~ M_{2n}\\
\ddots\\ M_{m1} ~~ M_{m2} ~~ \cdots ~~ M_{mn}\\ \end{bmatrix}.$$
Observe que um vetor de dimensão $n$ é simplesmente uma matriz de tamanho $n\times 1$. Assim como os
vetores, podemos multiplicar uma matriz com um número $c$ para obter uma nova matriz em que cada
entrada é multiplicada por $c$. Também podemos adicionar duas matrizes do mesmo tamanho para produzir
uma nova matriz cujas entradas são a soma das respectivas entradas das duas matrizes.

Multiplicação de matrizes
Também podemos multiplicar duas matrizes $M$ de dimensão $m\times n$ e $N$ de dimensão $n\times p$
para obter uma nova matriz $P$ de dimensão $m \times p$ da seguinte maneira:
\begin{align} &\begin{bmatrix} M_{11} ~~ M_{12} ~~ \cdots ~~ M_{1n}\\ M_{21} ~~ M_{22} ~~ \cdots ~~
M_{2n}\\ \ddots\\ M_{m1} ~~ M_{m2} ~~ \cdots ~~ M_{mn} \end{bmatrix} \begin{bmatrix} N_{11} ~~ N_{12}
~~ \cdots ~~ N_{1p}\\ N_{21} ~~ N_{22} ~~ \cdots ~~ N_{2p}\\ \ddots\\ N_{n1} ~~ N_{n2} ~~ \cdots ~~
N_{np} \end{bmatrix}=\begin{bmatrix} P_{11} ~~ P_{12} ~~ \cdots ~~ P_{1p}\\ P_{21} ~~ P_{22} ~~ \cdots ~~
P_{2p}\\ \ddots\\ P_{m1} ~~ P_{m2} ~~ \cdots ~~ P_{mp} \end{bmatrix} \end{align}
em que as entradas de $P$ são $P_{ik} = \sum_j M_{ij}N_{ jk}$. Por exemplo, a entrada $P_{11}$ é o produto
interno da primeira linha de $M$ com a primeira coluna de $N$. Observe que, como um vetor é simplesmente
um caso especial de uma matriz, essa definição se estende à multiplicação vetor-matriz.
Todas as matrizes que consideramos serão matrizes quadradas, em que o número de linhas e colunas são iguais
(ou vetores), que correspondem a apenas $1$ coluna. Uma matriz quadrada especial é a matriz de identidade,
designada $\boldone$, que tem todos os elementos diagonais iguais a $1$ e os elementos restantes iguais a
$0$:
$$\boldone=\begin{bmatrix} 1 ~~ 0 ~~ \cdots ~~ 0\\ 0 ~~ 1 ~~ \cdots ~~ 0\\ ~~ \ddots\\ 0 ~~ 0 ~~ \cdots
~~ 1 \end{bmatrix}.$$
Para uma matriz quadrada $A$, dizemos que uma matriz $B$ é seu inverso se a $AB = BA = \boldone$. O
inverso de uma matriz não precisa existir, mas quando ela existe, ela é exclusiva e a designamos como $A^{-1}$.
Para qualquer matriz $M$, a transposição adjunta ou conjugada de $M$ é a matriz $N$ tal que $N_{ij} =
M_{ ji}^*$. O adjunto de $M$ geralmente é designado como $M^\dagger$. Dizemos que uma matriz $U$ é
unitária se $UU^\dagger = U^\dagger U = \boldone$ ou o equivalente, $U^{-1} =U^\dagger$. Talvez a
propriedade mais importante das matrizes unitárias seja que elas preservam a norma de um vetor. Isso ocorre
porque
$$\langle v,v \rangle=v^\dagger v = v^\dagger U^{-1} U v = v^\dagger U^\dagger U v = \langle U v, U
v\rangle.$$
Uma matriz $M$ é considerada Hermitian se $M=M^\dagger$.

Produto tensorial
Por fim, outra operação importante é o produto Kronecker, também chamado de produto direto de matrizes ou
produto tensorial. Observe que o produto Kronecker é diferente da multiplicação de matrizes, que é uma
operação totalmente diferente. Na teoria da computação quântica, o produto tensorial normalmente é usado
para denotar o produto Kronecker.
Considere os dois vetores $v=\begin{bmatrix}a \\ b \end{bmatrix} $ e $u =\begin{bmatrix} c \\ d \\ e
\end{bmatrix} $. Seu produto tensorial é indicado como $v otimes u$ e resulta em uma matriz de blocos. $$
\begin{bmatrix} a \\ b \end{bmatrix} \otimes \begin{bmatrix} c \\ d \\ e \end{bmatrix} = \begin{bmatrix} a
\begin{bmatrix} c \\ d \\ e \end{bmatrix} \\[1,5 em] b \begin{bmatrix} c \\ d \\ e\end{bmatrix} \end{bmatrix} =
\begin{bmatrix} a c \\ a d \\ a e \\ b c \\ b d \\ be\end{bmatrix} $$
Observe que o produto tensorial é uma operação em duas matrizes ou em vetores de tamanho arbitrário. O
produto tensorial de duas matrizes $M$ de tamanho $m\times n$ e $N$ de tamanho $p \times q$ é uma mariz
maior $P=M\otimes N$ de tamanho $mp \times nq$, obtida de $M$ e $N$ da seguinte maneira:
\begin{align} M \otimes N &= \begin{bmatrix} M_{11} ~~ \cdots ~~ M_{1n} \\ \ddots\\ M_{m1} ~~ \cdots ~~
M_{mn} \end{bmatrix} \otimes \begin{bmatrix} N_{11} ~~ \cdots ~~ N_{1q}\\ \ddots\\ N_{p1} ~~ \cdots ~~
N_{pq} \end{bmatrix}\\ &= \begin{bmatrix} M_{11} \begin{bmatrix} N_{11} ~~ \cdots ~~ N_{1q}\\ \ddots\\
N_{p1} ~~ \cdots ~~ N_{pq} \end{bmatrix}~~ \cdots ~~ M_{1n} \begin{bmatrix} N_{11} ~~ \cdots ~~ N_{1q}\\
\ddots\\ N_{p1} ~~ \cdots ~~ N_{pq} \end{bmatrix}\\ \ddots\\ M_{m1} \begin{bmatrix} N_{11} ~~ \cdots ~~
N_{1q}\\ \ddots\\ N_{p1} ~~ \cdots ~~ N_{pq} \end{bmatrix}~~ \cdots ~~ M_{mn} \begin{bmatrix} N_{11} ~~
\cdots ~~ N_{1q}\\ \ddots\\ N_{p1} ~~ \cdots ~~ N_{pq} \end{bmatrix} \end{bmatrix}. \end{align}
Isso fica mais claro com um exemplo: $$ \begin{bmatrix} a\ b \\ c\ d \end{bmatrix} \otimes \begin{bmatrix} e\
f\\g\ h \end{bmatrix} = \begin{bmatrix} a\begin{bmatrix} e\ f\\ g\ h \end{bmatrix} b\begin{bmatrix} e\ f\\ g\ h
\end{bmatrix} \\[1em] c\begin{bmatrix} e\ f\\ g\ h \end{bmatrix} d\begin{bmatrix} e\ f\\ g\ h \end{bmatrix}
\end{bmatrix} = \begin{bmatrix} ae\ af\ be\ bf \\ ag\ ah\ bg\ bh \\ ce\ cf\ de\ df \\ cg\ ch\ dg\ dh \end{bmatrix}.
$$
Uma última convenção de notação útil sobre os produtos tensoriais é que, para qualquer vetor $v$ ou matriz
$M$, $v^{\otimes n}$ ou $M^{\otimes n}$ é um atalho para um produto tensorial repetido $n$ vezes. Por
exemplo:
\begin{align} &\begin{bmatrix} 1 \\ 0 \end{bmatrix}^{\otimes 1} = \begin{bmatrix} 1 \\ 0 \end{bmatrix},
\qquad\begin{bmatrix} 1 \\ 0 \end{bmatrix}^{\otimes 2} = \begin{bmatrix} 1 \\ 0 \\0 \\0 \end{bmatrix},
\qquad\begin{bmatrix} 1 \\ -1 \end{bmatrix}^{\otimes 2} = \begin{bmatrix} 1 \\ -1 \\-1 \\1 \end{bmatrix}, \\
&\begin{bmatrix} 0 & 1 \\ 1& 0 \end{bmatrix}^{\otimes 1}= \begin{bmatrix} 0& 1 \\ 1& 0 \end{bmatrix},
\qquad\begin{bmatrix} 0 & 1 \\ 1& 0 \end{bmatrix}^{\otimes 2}= \begin{bmatrix} 0 &0&0&1 \\ 0 &0&1&0 \\
0 &1&0&0\\ 1 &0&0&0\end{bmatrix}. \end{align}
Conceitos avançados sobre matriz
15/05/2021 • 4 minutes to read

Agora estendemos nossa manipulação de matrizes para autovalores, autovetores e exponenciais que formam
um conjunto fundamental de ferramentas que precisamos para descrever e implementar os Algoritmos
quantum.

Autovalores e autovetores
Seja $M$ uma matriz quadrada e $v$ um vetor que não contenha somente zeros (por exemplo, um vetor com
todas as entradas iguais a $0$).
Digamos que $v$ é um autovetor de $M$ se o $Mv =cv$ de algum número $c$. Digamos que $c$ é o autovalor
correspondente ao autovetor $v$. Em geral, uma matriz $M$ pode transformar um vetor em qualquer outro
vetor, mas um autovetor é especial porque permanece inalterado, exceto para ser multiplicado por um número.
Observe que se $v$ for um autovetor com autovalor $c$, então $av$ também será um autovetor (para
qualquer $a$ diferente de zero) com o mesmo autovalor.
Por exemplo, para a matriz de identidade, todo vetor $v$ é um autovetor com autovalor $1$.
Como um exemplo adicional, considere uma matriz diagonal $D$ que tem apenas entradas diferentes de zero
na diagonal:
$$ \begin{bmatrix} d_1 & 0 & 0 \\ 0 & d_2 & 0 \\ 0 & 0 & d_3 \end{bmatrix}. $$
Os vetores
$$\begin{bmatrix}1 \\ 0 \\ 0 \end{bmatrix}, \begin{bmatrix}0 \\ 1 \\ 0\end{bmatrix} e \begin{bmatrix}0 \\ 0 \\
1\end{bmatrix}$$
são autovetores dessa matriz com autovalores $d_1$, $d_2$ e $d_3$, respectivamente. Se $d_1$, $d_2$ e
$d_3$ forem números distintos, esses vetores (e seus múltiplos) serão os únicos autovetores da matriz $D$. Em
geral, para uma matriz diagonal, é fácil ler autovalores e autovetores. Autovalores são todos os números que
aparecem na diagonal, e seus respectivos autovetores são os vetores unitários com uma entrada igual a $1$ e
as entradas restantes iguais a $0$.
Observe no exemplo acima que os autovetores de $D$ formam uma base para vetores de $3$ dimensões. Uma
base é um conjunto de vetores, de modo que qualquer vetor pode ser escrito como uma combinação linear
deles. De forma mais explícita, $v_1$, $v_2$ e $v_3$ formam uma base se qualquer vetor $v$ puder ser escrito
como $v=a_1 v_1 + a_2 v_2 + a_3 v_3$ para alguns números $a_1$, $a_2$ e $a_3$.
Lembre-se de que uma matriz Hermitiana (também chamada de auto-adjunta) é uma matriz quadrada
complexa igual à sua própria transposição conjugada complexa, enquanto uma matriz unitária é uma matriz
quadrada complexa cujo inverso é igual à sua transposição conjugada adjunta ou complexa. Para matrizes
Hermitiana e unitárias, que são essencialmente as únicas matrizes encontradas na computação quântica, existe
um resultado geral conhecido como teorema espectral que declara: para qualquer matriz Hermitiana ou unitária
$M$, existe um $U$ unitário, tal que $M=U^\dagger D U $ para alguma matriz diagonal $D$. Além disso, as
entradas diagonais de $ D$ serão os autovalores de $M$.
Já sabemos como calcular os autovalores e os autovetores de uma matriz diagonal $D$. Usando este teorema,
sabemos que se $v$ for um autovetor de $D$ com autovalor $c$, por exemplo, $Dv = cv$, então $U^\dagger
v$ será um autovetor de $M$ com autovalor $c$. Isso ocorre porque
$$M(U^\dagger v) = U^\dagger D U (U^\dagger v) =U^\dagger D (U U^\dagger) v = U^\dagger D v = c
U^\dagger v.$$

Exponenciais de matriz
Um exponencial de matriz também pode ser definido em analogia exata com a função exponencial. O
exponencial de matriz de uma matriz $A$ pode ser expressa como
$$ e^A=\boldone + A + \frac{A^2}{2!}+\frac{A^3}{3!}+\cdots $$
Isso é importante porque a evolução da mecânica quântica no tempo é descrita por uma matriz unitária na
forma $e^{iB}$ para a matriz Hermitiana $B$. Por esse motivo, a execução de exponenciais de matriz é uma
parte fundamental da computação quântica e, como tal, Q# oferece rotinas intrínsecas para descrever essas
operações. Na prática, existem muitas maneiras de calcular um exponencial de matriz em um computador
clássico e, em geral, a aproximação numérica de tal exponencial é repleta de perigos. Para saber mais sobre os
desafios envolvidos, confira Cleve Moler e Charles Van Loan. "Nineteen dubious ways to compute the
exponential of a matrix". SIAM revisão 20.4 (1978): 801-836.
A maneira mais fácil de entender como calcular o exponencial de uma matriz, é por meio de autovalores e
autovetores dessa matriz. Especificamente, o teorema espectral discutido acima diz que para cada matriz
Hermitiana ou unitária $A$, existe uma matriz unitária $U$ e uma matriz diagonal $D$, tal que $A=U^\dagger
D U$. Devido às propriedades unitárias, temos que $A^2 = U^\dagger D^2 U$ e, da mesma forma, para
qualquer força $p$ $A^p = U^\dagger D^p U$. Se substituirmos isso na definição do operador do operador
exponencial, obteremos:
$$ e^A= U^\dagger \left(\boldone +D +\frac{D^2}{2!}+\cdots \right)U= U^\dagger
\begin{bmatrix}\exp(D_{11}) & 0 &\cdots &0\\ 0 & \exp(D_{22})&\cdots& 0\\ \vdots &\vdots &\ddots
&\vdots\\ 0&0&\cdots&\exp(D_{NN}) \end{bmatrix} U. $$
Em outras palavras, se você transformar para autobase da matriz $A$, o cálculo do exponencial de matriz será
equivalente a calcular o exponencial comum dos autovalores da matriz. Como muitas operações na computação
quântica envolvem a execução de exponenciais de matriz, esse truque de transformar em autobase de uma
matriz para simplificar a execução do operador exponencial aparece com frequência, sendo a base por trás de
muitos algoritmos quantum, como os métodos de simulação quantum do tipo Trotter-Suzuki discutidos
posteriormente neste guia.
Outra propriedade útil é se $B$ for unitária e Hermitiana, ou seja, $B=B^{-1}=B^\dagger$ então
$B^2=\boldone$. Ao aplicar essa regra à expansão acima do exponencial de matriz e agrupar os termos
$\boldone$ e $B$ juntos, pode-se ver que para qualquer valor real $x$, a identidade
$$e^{iBx}=\boldone \cos(x)+ iB\sin(x)$$
é mantida. Esse truque é especialmente útil, pois permite raciocinar sobre as ações exponenciais de matriz,
mesmo que a dimensão $B$ seja exponencialmente grande, para o caso especial em que $B$ é ao mesmo
tempo unitária e Hermitiana.
O qubit na computação quântica
29/04/2021 • 10 minutes to read

Assim como os bits são o objeto fundamental das informações na computação clássica, qubits (bits quânticos)
são o objeto fundamental das informações na computação quântica. Para entender essa correspondência, este
artigo analisa o exemplo mais simples: um qubit único.

Representando um qubit
Embora um bit, ou dígito binário, possa ter valor $0$ ou $1$, um qubit pode ter um valor que seja um destes ou
uma superposição quântica de $0$ e $1$.
O estado de um qubit único pode ser descrito por um vetor de coluna bidimensional da norma de unidade, ou
seja, a magnitude quadrada de suas entradas deve somar $1$. Esse vetor, chamado de vetor de estado quântico,
contém todas as informações necessárias para descrever o sistema quântico de qubit, assim como um único bit
contém todas as informações necessárias para descrever o estado de uma variável binária.
Qualquer vetor de coluna bidimensional de números reais ou complexos com a norma $1$ representa um
possível estado quântico mantido por um qubit. Assim, $\begin{bmatrix} \alpha \\ \beta \end{bmatrix}$
representa um estado qubit se $\alpha$ e $\beta$ são números complexos que satisfazem $|\alpha|^2 +
|\beta|^2 = 1$. Alguns exemplos de vetores de estado quântico válidos que representam qubits incluem
$$\begin{bmatrix} 1 \\ 0 \end{bmatrix}, \begin{bmatrix} 0 \\ 1 \end{bmatrix}, \begin{bmatrix} \frac{1}{\sqrt{2}} \\
\frac{1}{\sqrt{2}} \end{bmatrix}, \begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{-1}{\sqrt{2}} \end{bmatrix} \text{ e
}\begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{i}{\sqrt{2}} \end{bmatrix}.$$
Os vetores de estado quântico $\begin{bmatrix} 1 \\ 0 \end{bmatrix}$ e $\begin{bmatrix} 0 \\ 1 \end{bmatrix}$
usam uma função especial. Esses dois vetores formam uma base para o espaço vetorial que descreve o estado
do qubit. Isso significa que qualquer vetor de estado quântico pode ser escrito como uma soma desses vetores
de base. Especificamente, o vetor $\begin{bmatrix} x \\ y \end{bmatrix}$ pode ser gravado como $ x
\begin{bmatrix} 1 \\ 0 \end{bmatrix} + y \begin{bmatrix} 0 \\ 1 \end{bmatrix}$. Embora qualquer rotação desses
vetores sirva como uma base perfeitamente válida para o qubit, optamos por privilegiar esse, chamando-o de
base computacional.
Pegamos esses dois estados quânticos para corresponder aos dois estados de um bit clássico, a saber, $0$ e
$1$. A convenção padrão é escolher
$$0\equiv \begin{bmatrix} 1 \\ 0 \end{bmatrix}, \qquad 1 \equiv \begin{bmatrix} 0 \\ 1 \end{bmatrix},$$
embora a escolha oposta pudesse ser igualmente feita. Portanto, fora do número infinito de vetores de estado
quântico de qubit único, apenas dois correspondem a estados de bits clássicos; todos os outros estados
quânticos não.

Medindo um qubit
Agora que sabemos como representar um qubit, podemos obter alguma ideia sobre o que esses estados
representam discutindo o conceito de medida. Uma medida corresponde à ideia informal de "olhar" um qubit,
que reduz imediatamente o estado quântico a um dos dois estados clássicos $\begin{bmatrix} 1 \\ 0
\end{bmatrix}$ ou $\begin{bmatrix} 0 \\ 1 \end{bmatrix}$. Quando um qubit fornecido pelo vetor de estado
quântico $\begin{bmatrix} \alpha \\ \beta \end{bmatrix}$ é medido, obtemos o resultado $0$ com a
probabilidade $|\alpha|^2$ e o resultado $1$ com a probabilidade $|\beta|^2$. No resultado $0$, o novo
estado do qubit é $\begin{bmatrix} 1 \\ 0 \end{bmatrix}$ ; no resultado $1$, seu estado é $\begin{bmatrix} 0 \\
1 \end{bmatrix}$. Observe que essas probabilidades somam até $1$ devido à condição de normalização
$|\alpha|^2 + |\beta|^2 = 1$.
As propriedades de medida também significam que o sinal geral do vetor de estado quântico é irrelevante.
Negar um vetor é equivalente a $\alpha \rightarrow -\alpha$ e $\beta \rightarrow -\beta$. Como a
probabilidade de medir $0$ e $1$ depende da magnitude quadrada dos termos, inserir tais sinais não altera as
probabilidades. Essas fases costumam ser chamadas de ``fases globais'' e, mais geralmente, podem estar no
formato $e^{i \phi}$ em vez de apenas $\pm 1$.
Uma propriedade final importante da medição é que ela não necessariamente danifica todos os vetores de
estado quântico. Se começarmos com um qubit no estado $\begin{bmatrix} 1 \\ 0 \end{bmatrix}$, que
corresponde ao estado clássico $0$, medir esse estado sempre produzirá o resultado $0$ e deixará o estado
quântico inalterado. Nesse sentido, se tivermos apenas bits clássicos (por exemplo, qubits que sejam
$\begin{bmatrix} 1\\ 0 \end{bmatrix}$ ou $\begin{bmatrix} 0 \\ 1 \end{bmatrix}$ ), a medição não danificará o
sistema. Isso significa que podemos replicar dados clássicos e manipulá-los em um computador quântico da
mesma forma que em um computador clássico. No entanto, a capacidade de armazenar informações em ambos
os estados de uma vez é o que eleva a computação quântica para além do que é possível de forma clássica e
diminui ainda mais a capacidade de copiar dados quânticos indiscriminadamente, consulte também o teorema
da não clonagem.

Visualizando qubits e transformações usando a esfera de Bloch


Qubits também podem ser ilustrados em $3$D usando a representação de esfera de Bloch. A esfera de Bloch
fornece uma maneira de descrever um estado quântico de qubit único (que é um vetor complexo
bidimensional) como vetor de valor real tridimensional. Isso é importante porque permite visualizar estados de
qubit único e, assim, desenvolver um raciocínio que pode ser inestimável na compreensão de estados de vários
qubits (para os quais infelizmente a representação de esfera de Bloch falha). A esfera de Bloch pode ser
visualizada da seguinte maneira:
<largura de!---

{com = 50% } --->

As setas neste diagrama mostram a direção em que o vetor de estado quântico está apontando e cada
transformação da seta pode ser considerada como uma rotação de um dos eixos cardinal. Embora pensar em
uma computação quântica como sequência de rotações seja uma ideia poderosa, é desafiador usar essa ideia
para projetar e descrever algoritmos. Q# alivia esse problema fornecendo uma linguagem para descrever essas
rotações.

Operações de qubit único


Os computadores quânticos processam dados aplicando um conjunto universal de portões quânticos que
podem emular qualquer rotação do vetor de estado quântico. Essa noção de universalidade é semelhante à
noção de universalidade da computação tradicional (por exemplo, clássica), em que um conjunto de portões é
considerado universal se cada transformação dos bits de entrada puder ser executada usando um circuito de
comprimento finito. Na computação quântica, as transformações válidas que temos permissão para executar em
um qubit são transformações e medição de unitários. A operação adjoin ou a transposição de conjugado
complexo é de importância crucial para a computação quântica porque é necessária para inverter as
transformações quânticas.
Há apenas quatro funções que mapeiam um bit para um bit em um computador clássico. Por outro lado, há um
número infinito de transformações de unitários em um único qubit em um computador quântico. Portanto,
nenhum conjunto finito de operações primitivas quânticas, chamado de portões, pode replicar exatamente o
conjunto infinito de transformações de unitários permitidas na computação quântica. Isso significa que,
diferentemente da computação clássica, é impossível para um computador quântico implementar cada
programa quântico possível usando exatamente um número finito de portões. Assim, os computadores
quânticos não podem ser universais no mesmo sentido dos computadores clássicos. Como resultado, quando
dizemos que um conjunto de portões é universal para computação quântica, na verdade queremos dizer algo
um pouco mais fraco do que com a computação clássica. Quanto à universalidade, exigimos que um
computador quântico só se aproxime de cada matriz de unitários dentro de um erro finito usando uma
sequência de portão de comprimento finito. Em outras palavras, um conjunto de portões será um conjunto de
portões universal se qualquer transformação de unitários puder ser aproximadamente gravada como produto
de portões desse conjunto. Exigimos que, para qualquer erro prescrito limitado, existam portões $G_{1},
G_{2},\ldots, G_N$ do conjunto de portões de modo que
$$ G_N G_{N-1} \cdots G_2 G_1 \approx U. $$
Observe que, como a convenção para multiplicação de matriz é multiplicada da direita para a esquerda, a
primeira operação de portão nessa sequência, $G_N$, na verdade, é a última aplicada ao vetor de estado
quântico. Mais formalmente, dizemos que esse conjunto de portões é universal se para cada tolerância de erro
$\epsilon >0$ existe $G_1,\ldots, G_N$ de modo que a distância entre $G_N\ldots G_1$ e $U$ seja no máximo
$\epsilon$. Idealmente, o valor de $N$ necessário para alcançar essa distância de $\epsilon$ deve dimensionar
polilogaritmicamente com $1/\epsilon$.
Como é esse tipo de conjunto de portões universais na prática? O mais simples desse tipo de conjunto de
portões universais para portões de qubit único consiste em apenas dois portões: o portão Hadamard $H$ e o
chamado portão $T$ (também conhecido como portão $\pi/8$):
$$ H=\frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 &-1 \end{bmatrix},\qquad T=\begin{bmatrix} 1 & 0 \\ 0 &
e^{i\pi/4} \end{bmatrix}. $$
No entanto, por motivos práticos relacionados à correção de erro quântico, pode ser mais conveniente
considerar um conjunto de portões maior, ou seja, um que possa ser gerado usando $H$ e $T$. Podemos
classificar os portões quânticos em duas categorias: portões Clifford e o portão $T$. Essa subdivisão é útil
porque, em muitos esquemas de correção de erros quânticos, os chamados portões Clifford são fáceis de
implementar, ou seja, eles exigem muito poucos recursos em termos de operações e qubits para implementar a
falha de forma tolerante, enquanto portões não Clifford são muito dispendiosas quando exigem tolerância a
falhas. O conjunto padrão de portões Clifford de qubit único, incluído por padrão no Q#, inclui
$$ H=\frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 &-1 \end{bmatrix} ,\qquad S =\begin{bmatrix} 1 & 0 \\ 0 & i
\end{bmatrix}= T^2,\qquad X=\begin{bmatrix} 0 &1 \\ 1& 0 \end{bmatrix}= HT^4H, $$
$$ Y = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix}=T^2HT^4 HT^6, \qquad Z=\begin{bmatrix}1&0\\ 0&-1
\end{bmatrix}=T^4. $$
Aqui, as operações $X$, $Y$ e $Z$ são usadas especialmente de forma frequente e são nomeadas como
operadores Pauli em homenagem a seu criador Wolfgang Pauli. Junto com o portão não Clifford (o portão $T$),
essas operações podem ser compostas para aproximar qualquer transformação de unitários em um qubit único.
Para obter mais informações sobre essas operações, suas representações e implementações de esfera de Bloch
Q#, consulte operações e funções intrínsecas.
Como um exemplo de como as transformações de unitários podem ser criadas com base nesses primitivos, as
três transformações ilustradas nas esferas de Bloch acima correspondem à sequência de portão
$\begin{bmatrix} 1 \\ 0 \end{bmatrix} \mapsto HZH \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 0 \\
1 \end{bmatrix}$.
Embora o anterior constitua os portões primitivos mais populares para descrever as operações no nível lógico
da pilha (considere o nível lógico como o nível do algoritmo quântico), geralmente é conveniente considerar
menos operações básicas no nível de algoritmo, por exemplo, operações mais próximas de um nível de
descrição de função. Felizmente, Q# também tem métodos disponíveis para a implementação de unidades de
nível superior, que, por sua vez, permitem que algoritmos de alto nível sejam implementados sem decompor
explicitamente tudo em portões Clifford e $T$.
O mais simples desses primitivos é a rotação de qubit único. Três rotações de qubit único normalmente são
consideradas: $R_x$, $R_y$ e $R_z$. Para visualizar a ação da rotação $R_x(\theta)$, por exemplo, imagine
apontar o polegar direito ao longo da direção do eixo $x$ da esfera de Bloch e girar o vetor com sua mão em
um ângulo de $\teta/2$ radianos. Esse fator confuso de $2$ é devido ao fato de que os vetores ortogonais
estão separados por$180^\circs$ quando plotados na esfera de Bloch, mas estão, na verdade, separados por
$90^\circ$ graus geométricos. As matrizes de unitários correspondentes são:
\begin{align } &R_z(\theta) = e^{-i\theta Z/2} = \begin{bmatrix} e^{-i\theta/2} & 0\\ 0& e^{i\theta/2}
\end{bmatrix}, \\ &R_x(\theta) = e^{-i\theta X/2} = HR_z(\theta)H = \begin{bmatrix} \cos(\theta/2) & -
i\sin(\theta/2)\\ -i\sin(\theta/2) & \cos(\theta/2) \end{bmatrix}, \\ &R_y(\theta) = e^{-i\theta Y/2} =
SHR_z(\theta)HS^\dagger = \begin{bmatrix} \cos(\theta/2) & -\sin(\theta/2)\\ \sin(\theta/2) & \cos(\theta/2)
\end{bmatrix}. \end{align}
Assim como as três rotações podem ser combinadas para executar uma rotação arbitrária em três dimensões,
pode ser visto na representação da esfera de Bloch que qualquer matriz de unitários também pode ser gravada
como sequência de três rotações. Especificamente, para cada matriz de unitários $U$, existe
$\alpha,\beta,\gamma,\delta$ de forma que $U= e^{i\alpha} R_x(\beta)R_z(\gamma)R_x(\delta)$. Portanto
$R_z(\theta)$ e $H$ também formam um conjunto de portões universal, embora não seja um conjunto discreto,
porque $\theta$ pode usar qualquer valor. Por esse motivo, e devido a aplicativos na simulação quântica, esses
portões contínuos são cruciais para a computação quântica, especialmente no nível de design do algoritmo
quântico. Para obter a implementação de hardware tolerante a falhas, elas serão eventualmente compiladas em
sequências de portão discretas que se aproximam bastante dessas rotações.
Operações em vários qubits
19/05/2021 • 12 minutes to read

Este artigo revisa as regras usadas para criar Estados qubit de Estados de qubit único e discute as operações de
portão necessárias para incluir em um conjunto de portões para formar um computador Quantum universal de
muitos qubit. Essas ferramentas são absolutamente necessárias para entender os conjuntos de portões que
normalmente são usados no Q# código e para se aprofundar sobre por que os efeitos quantum, como
entrelaçamento ou interferência, renderizam a computação quântica mais potente do que a computação
clássica.

Portões de um qubit vs. de vários qubits


O verdadeiro poder da computação quântica só se torna evidente conforme aumentamos o número de qubits.
Os portões de qubit têm alguns recursos intuitivos, como a capacidade de estar em mais de um estado em um
determinado momento. No entanto, se tudo o que tínhamos em um computador quantum tivesse portões de
qubit único, uma calculadora e certamente um supercomputador clássico seria um anão da sua capacidade
computacional.
A capacidade de computação quântica surge, em parte, porque a dimensão do espaço vetorial dos vetores de
estado quantum aumenta exponencialmente com o número de qubits. Isso significa que, embora um qubit
possa ser modelado trivialmente, a simulação de uma computação quântica de 50 qubits expandiria,
possivelmente, os limites dos supercomputadores existentes. Aumentar o tamanho da computação por apenas
um qubit adicional dobra a memória necessária para armazenar o estado e aproximadamente dobra o tempo
computacional. Essa potência computacional que é rapidamente duplicada é o motivo pelo qual um computador
quantum com um número relativamente pequeno de qubits pode superar de longe os supercomputadores mais
potentes de hoje, amanhã e além para algumas tarefas computacionais.

Como representar dois qubits


Se recebermos dois qubits separados, um no estado $\psi=\begin{bmatrix} \alpha \\ \beta \end{bmatrix}$ e
outro no estado $\phi=\begin{bmatrix} \gamma \\ \delta \end{bmatrix}$, o estado correspondente de dois qubit
será fornecido pelo produto tensor (ou produto Kronecker de vetores, que é definido da seguinte maneira
$$ \psi \otimes \phi = \begin{bmatrix} \alpha \\ \beta \end{bmatrix} \otimes \begin{bmatrix} \gamma \\ \delta
\end{bmatrix} =\begin{bmatrix} \alpha \begin{bmatrix} \gamma \\ \delta \end{bmatrix} \\ \beta
\begin{bmatrix}\gamma \\ \delta \end{bmatrix} \end{bmatrix} = \begin{bmatrix} \alpha\gamma \\ \alpha\delta
\\ \beta\gamma \\ \beta\delta \end{bmatrix}. $$
Portanto, considerando dois estados de qubit único $\psi$ e $\phi$, cada um de dimensão 2, o estado
correspondente de dois qubit $\psi \otimes \phi$ será quadridimensional. O vetor
$$ \begin{bmatrix} \alpha {00} \\ \alpha {01} \\ \alpha {10} \\ \alpha {11} \end{bmatrix} $$
representa um estado quantum em dois qubits se $|\alpha {00}|^2+|\alpha {01}|^2+|\alpha {10}|^2+|\alpha
{11}|^2=1$. Em geral, você pode ver que o estado quantum de $n$ qubits é representado por um vetor de
unidade $v_1 \otimes v_2 \otimes \cdots \otimes v_n$ da dimensão $2 \cdot 2 \cdot 2 \cdots = 2^n$ usando
essa construção. Assim como ocorre com qubits único, o vetor de estado do quantum de vários qubits contém
todas as informações necessárias para descrever o comportamento do sistema. Para obter mais informações
sobre vetores e produtos tensoriais, confira Vetores e matrizes na computação quântica.
A base computacional para estados de dois qubits é formada pelos produtos tensor de estados de um qubit.
Neste exemplo, temos
\begin{align} 00 \equiv \begin{bmatrix}1 \\ 0 \end{bmatrix}\otimes \begin{bmatrix}1 \\ 0 \end{bmatrix} &=
\begin{bmatrix}1 \\ 0\\ 0\\ 0 \end{bmatrix},\qquad 01 \equiv \begin{bmatrix}1 \\ 0 \end{bmatrix}\otimes
\begin{bmatrix}0 \\ 1 \end{bmatrix} = \begin{bmatrix}0 \\ 1\\ 0\\ 0 \end{bmatrix},\\ 10 \equiv \begin{bmatrix}0
\\ 1 \end{bmatrix}\otimes \begin{bmatrix}1 \\ 0 \end{bmatrix} &= \begin{bmatrix}0 \\ 0\\ 1\\ 0
\end{bmatrix},\qquad 11 \equiv \begin{bmatrix}0 \\ 1 \end{bmatrix}\otimes \begin{bmatrix}0 \\ 1 \end{bmatrix}
= \begin{bmatrix}0 \\ 0\\ 0\\ 1 \end{bmatrix}. \end{align}
Observe que, embora possamos sempre pegar o produto tensor de dois estados de qubit único para formar um
estado de dois qubits, nem todos os estados quantum de dois qubits podem ser escritos como o produto tensor
de dois estados de qubit único. Por exemplo, não há estados $\psi=\begin{bmatrix} \alpha \\ \beta
\end{bmatrix}$ e $\phi=\begin{bmatrix} \gamma \\ \delta \end{bmatrix}$, por isso, seu produto tensor é o
estado
$$\psi\otimes \phi = \begin{bmatrix} 1/\sqrt{2} \\ 0 \\ 0 \\ 1/\sqrt{2} \end{bmatrix}.$$
Esse estado de dois qubit, que não pode ser escrito como o produto tensor de estados de qubit único, é
chamado de "estado entrelaçado"; os dois qubits são considerados entrelaçados. De modo flexível, como o
estado quantum não pode ser considerado como um produto tensor de estados de qubit único, as informações
que o estado mantém não são confinadas a nenhum dos qubits individualmente. Em vez disso, as informações
são armazenadas não localmente nas correlações entre os dois estados. Essa não localidade de informações é
um dos principais recursos que distinguem a computação quântica da computação clássica e é essencial para
vários protocolos quantum, incluindo o teletransporte quantum e a correção de erro quantum.

Medir os estados de dois qubits


Medir estados de dois qubit é muito semelhante a medir um só qubit. Como medir o estado
$$ \begin{bmatrix} \alpha {00} \\ \alpha {01} \\ \alpha {10} \\ \alpha {11} \end{bmatrix} $$
produz $00$ com probabilidade $|\alpha {00}|^2$, $01$ com probabilidade $|\alpha {01}|^2$, $10$ com
probabilidade $|\alpha {10}|^2$ e $11$ com probabilidade $|\alpha {11}|^2$. As variáveis $\alpha {00}, \alpha
{01}, \alpha {10},$ e $\alpha {11}$ foram deliberadamente nomeadas para tornar essa conexão clara. Após a
medição, se o resultado é $00$, o estado quantum do sistema de dois qubits foi recolhido e agora é
$$ 00 \equiv \begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \end{bmatrix}. $$
Também é possível medir apenas um qubit de um estado quantum de dois qubits. Nos casos em que você mede
apenas um dos qubits, o impacto da medição é ligeiramente diferente, pois o estado inteiro não é recolhido para
um estado de base computacional, em vez disso, ele é recolhido para apenas um subsistema. Em outras
palavras, nesses casos, medir apenas um qubit recolhe apenas um dos subsistemas, não todos eles.
Para ver isso, considere a possibilidade de medir o primeiro qubit do seguinte estado, que é formado pela
aplicação da transformação de Hadamard $H$ em dois qubits inicialmente definido como o estado "0": $$
H^{\otimes 2} \left( \begin{bmatrix}1 \\ 0 \end{bmatrix}\otimes \begin{bmatrix}1 \\ 0 \end{bmatrix} \right) =
\frac{1}{2}\begin{bmatrix}1 & 1 & 1 & 1 \\ 1 & -1 & 1 & -1 \\ 1 & 1 & -1 & -1 \\ 1 & -1 & -1 & 1
\end{bmatrix}\begin{bmatrix}1\\ 0\\ 0\\ 0\end{bmatrix} = \frac{1}{2}\begin{bmatrix}1\\ 1\\ 1\\ 1\end{bmatrix}
\mapsto \begin{cases}\text{resultado }=0 & \frac{1}{\sqrt{2}}\begin{bmatrix}1\\ 1\\ 0\\ 0 \end{bmatrix}\\
\text{resultado }=1 & \frac{1}{\sqrt{2}}\begin{bmatrix}0\\ 0\\ 1\\ 1 \end{bmatrix}\\ \end{cases}. $$ Os dois
resultados têm 50% de probabilidade de ocorrer. O resultado ser 50% de probabilidade para ambos pode ser
suposto pelo fato de que o vetor de estado inicial quantum é invariável sob a troca de $0$ por $1$ no primeiro
qubit.
A regra matemática para medir o primeiro ou o segundo qubit é simples. Se permitimos que $e_k$ sejam o
vetor de base computacional $k^{\rm th}$ e deixarmos que $S$ seja o conjunto de todos os $e_k$, modo que o
qubit em questão leve o valor $1$ para esse valor de $k$. Por exemplo, se estivermos interessados em medir o
primeiro qubit, $S$ consistiria em $e_1\equiv 10$ e $e_3\equiv 11$. Da mesma forma, se estivéssemos
interessados no segundo qubit $S$, ele consistiria em $e_2\equiv 01$ e $e_3 \equiv 11$. Em seguida, a
probabilidade de medir o qubit escolhido como $1$ é para o vetor de estado $\psi$
$$ P(\text{resultado}=1)= \sum_{e_k \text{ no conjunto de } S}\psi^\dagger e_k e_k^\dagger \psi. $$

NOTE
Neste documento, estamos usando o formato little endian para rotular a base computacional. No formato little endian, os
bits menos significativos são apresentados primeiro. Por exemplo, o número quatro no formato little endian é
representado pela cadeia de caracteres de bits 001.

Como cada medição de qubit só pode gerar $0$ ou $1$, a probabilidade de medir $0$ é simplesmente $1-
P(\text{resultado}=1)$. É por isso que só fornecemos explicitamente uma fórmula para a probabilidade de medir
$1$.
A ação que essa medida tem no estado pode ser expressa matematicamente como
$$ \psi \mapsto \frac{\sum_{e_k \text{ no conjunto } S} e_k e_k^\dagger \psi}{\sqrt{P(\text{resultado}=1)}}. $$
Um leitor cuidadoso pode se preocupar com o que acontece quando a probabilidade da medida é zero. Embora
o estado resultante seja tecnicamente indefinido nesse caso, nunca precisamos nos preocupar com essas
eventualidades porque a probabilidade é zero.
Se tomarmos $\psi$ como o vetor de estado uniforme fornecido acima e estivermos interessado em medir o
primeiro qubit, então
$$ P(\text{medição do primeiro qubit}=1) = (\psi^\dagger e_1)(e_1^\dagger \psi)+(\psi^\dagger e_3)
(e_3^\dagger \psi)=|e_1^\dagger \psi|^2+|e_3^\dagger \psi|^2. $$
Observe que essa é apenas a soma das duas probabilidades que seriam esperadas para medir os resultados
$10$ e $11$ se todos os qubits fossem ser medidos. Para nosso exemplo, isso é avaliado como
$$ \frac{1}{4}\left|\begin{bmatrix}0&0&1&0\end{bmatrix}\begin{bmatrix}1\\ 1\\ 1\\ 1\end{bmatrix}
\right|^2+\frac{1}{4}\left|\begin{bmatrix}0&0&0&1\end{bmatrix}\begin{bmatrix}1\\ 1\\ 1\\ 1\end{bmatrix}
\right|^2=\frac{1}{2}. $$
que corresponde perfeitamente ao que nossa intuição nos informa que deve ser a probabilidade. Da mesma
forma, o estado pode ser escrito como
$$ \frac{\frac{e_1}{2}+\frac{e_3}{2}}{\sqrt{\frac{1}{2}}}=\frac{1}{\sqrt{2}}\begin{bmatrix} 0\\ 0\\ 1\\
1\end{bmatrix} $$
novamente de acordo com nossa intuição.

Operações de dois qubits


Como no caso de qubit único, qualquer transformação unitária é uma operação válida em qubits. Em geral, uma
transformação unitária em $n$ qubits é uma matriz $U$ de tamanho $2^n \times 2^n$ (de modo que ela atua
em vetores de tamanho $2^n$), de modo que $U^{-1} = U^\dagger$. Por exemplo, o portão CNOT (controlada
não) é um portão de dois qubits comumente usada e é representada pela seguinte matriz unitária:
$$ \operatorname{CNOT} = \begin{bmatrix} 1\ 0\ 0\ 0 \\ 0\ 1\ 0\ 0 \\ 0\ 0\ 0\ 1 \\ 0\ 0\ 1\ 0 \end{bmatrix} $$
Também podemos formar portões de dois qubit aplicando portões de qubit único em ambos os qubits. Por
exemplo, se aplicarmos os portões
$$ \begin{bmatrix} a\ b\\ c\ d \end{bmatrix} $$
e
$$\begin{bmatrix} e\ f\\ g\ h \end{bmatrix} $$
ao primeiro e ao segundo qubit, respectivamente, isso será equivalente a aplicar o unitário de dois qubit
fornecido por seu produto tensor: $$\begin{bmatrix} a\ b\\ c\ d \end{bmatrix} \otimes \begin{bmatrix} e\ f\\ g\
h \end{bmatrix}= \begin{bmatrix} ae\ af\ be\ bf \\ ag\ ah\ bg\ bh \\ ce\ cf\ de\ df \\ cg\ ch\ dg\ dh
\end{bmatrix}.$$ Portanto, podemos formar portões de dois qubit assumindo o produto tensor de alguns
portões de qubit individual conhecidas. Alguns exemplos de portões de dois qubits incluem $H \otimes H$, $X
\otimes \boldone$ e $X \otimes Z$.
Observe que, embora qualquer um dos dois portões qubit defina um portão de dois qubits ao pegar seu
produto tensor, o inverso não é verdadeiro. Nem todos os portões de dois qubits podem ser escritas como o
produto tensor de portões de qubit único. Esse portão é chamado de portão de entrelaçamento. Um exemplo de
um portão de entrelaçamento é o portão CNOT.
A intuição por trás de um portão controlado não pode ser generalizada para portões arbitrários. Um portão
controlado em geral é um portão que atua como identidade (ou seja, não tem nenhuma ação), a menos que um
qubit específico seja $1$. Denotamos um unitário controlado, controlado neste caso no qubit rotulado $x$, com
um $\Lambda_x(U)$. Como exemplo, $\Lambda_0(U) e_{1}\otimes {\psi}=e_{1}\otimes U{\psi}$ e
$\Lambda_0(U) e_{0}\otimes {\psi}=e_{0}\otimes{\psi}$, em que $e_0$ e $e_1$ são os vetores de base
computacional para um qubit correspondente aos valores $0$ e $1$. Por exemplo, considere o seguinte portão
$Z$ controlado; então podemos expressar isso como $$ \Lambda_0(Z)=
\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\0&0&0&-1 \end{bmatrix}=(\boldone\otimes
H)\operatorname{CNOT}(\boldone\otimes H). $$
A criação eficiente de unitários controlados é um grande desafio. A maneira mais simples de implementar isso
requer formar um banco de dados de versões controladas de portões fundamentais e substituir cada portão
fundamental na operação unitária original por sua contraparte controlada. Geralmente, isso é muito inútil e
insights inteligentes muitas vezes podem ser usados apenas para substituir alguns portões por versões
controladas para obter o mesmo impacto. Por esse motivo, fornecemos em nossa estrutura a capacidade de
executar o método simples de controlar ou permitir que o usuário defina uma versão controlada do unitário se
uma versão de ajuste manual é conhecida.
Os portões também podem ser controlados usando informações clássicas. Um não portão controlado de modo
clássico, por exemplo, é apenas um não portão comum, mas só será aplicado se um bit clássico for $1$, em vez
de um bit quantum. Nesse sentido, um portão controlado de modo clássico pode ser considerado uma
instrução if no código quantum em que o portão é aplicado somente em uma ramificação do código.
Como no caso de qubit único, um conjunto de portões de dois qubit será universal se qualquer matriz unitária
de $4\times 4$ puder ser aproximada por um produto de portões desse conjunto para uma precisão arbitrária.
Um exemplo de um conjunto de portão universal é o portão de Hadamard, o portão T e o portão CNOT. Ao
pegar produtos desses portões, podemos aproximar qualquer matriz unitária em dois qubits.

Sistemas de vários qubit


Seguimos exatamente os mesmos padrões explorados no caso de dois qubit para criar estados quantum de
vários qubits com base em sistemas menores. Esses estados são criados formando produtos tensores com base
em estados menores. Por exemplo, considere a codificação da cadeia de caracteres de bits $1011001$ em um
computador quantum. Podemos codificá-lo como
$$ 1011001 \equiv \begin{bmatrix} 0 \\ 1 \end{bmatrix}\otimes \begin{bmatrix} 1 \\ 0 \end{bmatrix}\otimes
\begin{bmatrix} 0 \\ 1 \end{bmatrix}\otimes \begin{bmatrix} 0 \\ 1 \end{bmatrix} \otimes \begin{bmatrix} 1 \\ 0
\end{bmatrix}\otimes \begin{bmatrix} 1 \\ 0 \end{bmatrix}\otimes \begin{bmatrix} 0 \\ 1 \end{bmatrix}. $$
Os portões quantum funcionam exatamente da mesma maneira. Por exemplo, se quisermos aplicar o portão
$X$ ao primeiro qubit então executar um CNOT entre o segundo e o terceiro qubits, poderemos expressar essa
transformação como
\begin{align} &(X \otimes \operatorname{CNOT}_{12}\otimes \boldone\otimes \boldone \otimes \boldone
\otimes \boldone) \begin{bmatrix} 0 \\ 1 \end{bmatrix}\otimes \begin{bmatrix} 1 \\ 0 \end{bmatrix}\otimes
\begin{bmatrix} 0 \\ 1 \end{bmatrix}\otimes \begin{bmatrix} 0 \\ 1 \end{bmatrix} \otimes \begin{bmatrix} 1 \\ 0
\end{bmatrix}\otimes \begin{bmatrix} 1 \\ 0 \end{bmatrix}\otimes \begin{bmatrix} 0 \\ 1 \end{bmatrix}\\
&\qquad\qquad\equiv 0011001. \end{align}
Em muitos sistemas qubit, geralmente há a necessidade de alocar e desalocar qubits que servem como
memória temporária para o computador quantum. Esse qubit é chamado de ancilla. Por padrão, supomos que o
estado qubit seja inicializado para $e_0$ na alocação. Supomos que ele é retornado novamente para $e_0$
antes da desalocação. Essa suposição é importante porque, se um qubit ancilla se tornar entrelaçado com outro
registro de qubit quando ele ficar desalocado, o processo de desalocação danificará o ancilla. Por esse motivo,
sempre presumimos que esses qubits sejam revertidos para seu estado inicial antes de serem liberados.
Por fim, embora novos portões precisem ser adicionados ao nosso conjunto de portões para obter a
computação quântica universal para dois computadores quantum qubit, nenhum portão novo precisará ser
introduzido no caso de vários qubit. Os portões $H$, $T$ e CNOT formam um portão universal definido em
muitos qubits porque qualquer transformação unitária geral pode ser quebrada em uma série de duas rotações
de qubit. Então poderemos aproveitar a teoria desenvolvida para o caso de duas qubit e usá-la novamente aqui
quando tivermos muitos qubits.
Embora a notação linear algébricas que usamos até agora possa ser usada para descrever os estados de qubit,
ela se torna cada vez mais complicada à medida que aumentamos o tamanho dos estados. O vetor de coluna
resultante para uma cadeia de caracteres de comprimento de 7 bits, por exemplo, tem $128$ dimensões, o que
torna expressá-la usando a notação descrita anteriormente muito complicado. Por esse motivo, apresentamos
uma notação comum na computação quântica que nos permite descrever de modo conciso esses vetores
altamente dimensionais.
Notação dirac
28/04/2021 • 11 minutes to read

A notação de Dirac é uma linguagem para se adequar às necessidades exatas de expressar estados em mecânica
quântica. Os exemplos neste artigo são sugestões que podem ser usadas para expressar ideias quânticas de
forma concisa.

Limitações da notação de vetor de coluna


Embora a notação de vetor de coluna seja onipresente em algebra linear, ela geralmente é complicada na
computação quântica, especialmente ao lidar com vários qubits. Por exemplo, quando definimos $\psi$ como
um vetor, não está explicitamente claro se $\psi$ é um vetor de linha ou de coluna. Portanto, se $\phi$ e $\psi$
forem vetores, ele será igualmente incerto se $\phi \psi$ está até mesmo definido, pois as formas de $\phi$ e
$\psi$ podem não estar claras no contexto. Além da ambiguidade sobre as formas de vetores, expressar até
mesmo vetores simples usando a notação algébrica linear introduzida anteriormente pode ser muito
complicado. Por exemplo, se quisermos descrever um estado de $n$-qubit, em que cada qubit usa o valor $0$,
formalmente expressaríamos o estado como
$$\begin{bmatrix}1 \\ 0 \end{bmatrix}\otimes \cdots \otimes\begin{bmatrix}1 \\ 0 \end{bmatrix}. $$
É claro que avaliar esse produto tensorial é impraticável porque o vetor está em um espaço exponencialmente
grande e, portanto, essa notação é, na verdade, a melhor descrição do estado que pode ser fornecido usando a
notação anterior.

Tipos de vetores na notação de Dirac


Há dois tipos de vetores na notação de Dirac: o vetor bra e o vetor ket, portanto, nomeados porque, quando
agrupados, formam um braket ou produto interno. Se $\psi$ for um vetor de coluna, poderemos escrevê-lo na
notação de Dirac como $\ket{\psi}$, em que o $\ket{\cdot}$ denota que se trata de um vetor de coluna de
unidade, por exemplo, um vetor ket. Similarmente, o vetor de linha $\psi^\dagger$ é expresso como
$\bra{\psi}$. Em outras palavras, $\psi^\dagger$ é obtida aplicando conjugação complexa de entrada aos
elementos da transposição de $\psi$. A notação bra-ket implica diretamente que $\braket{\psi|\psi}$ é o
produto interno do vetor $\psi$ com ele mesmo, que é por definição $1$.
Em geral, se $\psi$ e $\phi$ são vetores de estado quântico, seu produto interno é $\braket{\phi|\psi}$, o que
implica que a probabilidade de medir o estado $\ket{\psi}$ como $\ket{\phi}$ é $|\braket{\phi|\psi}|^2$.
A convenção a seguir é usada para descrever os estados quânticos que codificam os valores de zero e um (os
estados de base computacional de qubit único):
$$ \begin{bmatrix} 1 \\ 0 \end{bmatrix} = \ket{0},\qquad \begin{bmatrix} 0 \\ 1 \end{bmatrix} = \ket{1}. $$

Exemplo: representar a operação de Hadamard com notação de Dirac


A notação a seguir é geralmente usada para descrever os estados resultantes da aplicação do portão de
Hadamard a $\ket{0}$ e $\ket{1}$ (que correspondem aos vetores de unidade nas direções $+x$ e $-x$ na
esfera de Bloch):
$$ \frac{1}{\sqrt{2}}\begin{bmatrix} 1 \\ 1 \end{bmatrix}=H\ket{0} = \ket{+},\qquad \frac{1}
{\sqrt{2}}\begin{bmatrix} 1 \\ -1 \end{bmatrix} =H\ket{1} = \ket{-} . $$
Esses estados também podem ser expandidos usando a notação de Dirac como somas de $\ket{0}$ e $\ket{1}$:
$$ \ket{+} = \frac{1}{\sqrt{2}}(\ket{0} + \ket{1}),\qquad \ket{-} = \frac{1}{\sqrt{2}}(\ket{0} - \ket{1}). $$

Vetores de base computacional


Isso demonstra por que esses estados geralmente são chamados de base computacional: cada estado quântico
sempre pode ser expresso como somas de vetores de base computacional e essas somas são facilmente
expressas usando a notação de Dirac. O inverso também é verdadeiro em que os estados $\ket{+}$ e $\ket{-}$
também formam uma base para os estados quânticos. Você pode ver isso pelo fato de que
$$ \ket{0} = \frac{1}{\sqrt{2}}(\ket{+} + \ket{-}),\qquad \ket{1} = \frac{1}{\sqrt{2}}(\ket{+} - \ket{-}). $$
Como exemplo de notação de Dirac, considere o braket $\braket{0 | 1}$, que é o produto interno entre $0$ e
$1$. Pode ser escrito como
$$ \braket{0 | 1}=\begin{bmatrix} 1 & 0 \end{bmatrix}\begin{bmatrix}0\\ 1\end{bmatrix}=0. $$
Isso diz que $\ket{0}$ e $\ket{1}$ são vetores ortogonais, o que significa que $\braket{0 | 1} = \braket{1 | 0}
=0$. Também por definição $\braket{0 | 0} = \braket{1 | 1}=1$, o que significa que os dois vetores de base
computacional também podem ser chamados de ortonormal.
Essas propriedades ortonormais serão úteis no exemplo a seguir. Se tivermos um estado $\ket{\psi} = {\frac{3}
{5}} \ket{1} + {\frac{4}{5}} \ket{0}$, então devido a $\braket{1 | 0} =0$, a probabilidade de medir $1$ é
$$ \big|\braket{1 | \psi}\big|^2= \left|\frac{3}{5}\braket{1 | 1} +\frac{4}{5}\braket{1 | 0}\right|^2=\frac{9}{25}. $$

Notação de produto tensorial


A notação de Dirac também inclui uma estrutura de produto tensorial implícita dentro dela. Isso é importante
porque, na computação quântica, o vetor de estado descrito por dois registros quânticos não correlacionados
são os produtos tensoriais dos dois vetores de estado. A descrição concisa da estrutura do produto tensorial, ou
a falta dela, é vital se você quiser explicar uma computação quântica. A estrutura do produto tensorial implica
que podemos escrever $\psi \otimes \phi$ para quaisquer dois vetores de estado quântico $\phi$ e $\psi$
como $\ket{\psi}\otimes\ket{\phi}$, no entanto, por convenção $\otimes$ a escrita entre os vetores é
desnecessária, e podemos simplesmente escrever $\ket{\psi} \ket{\phi} = \ket{\psi \phi} $. Para obter mais
informações sobre vetores e produtos tensoriais, consulte Vetores e matrizes na computação quântica. Por
exemplo, o estado com dois qubits inicializados para o estado zero é dado por
$$ \ket{0} \otimes \ket{0} = \ket{0} \ket{0} = \ket{00} = \begin{bmatrix} 1 \\ 0 \end{bmatrix} \otimes
\begin{bmatrix} 1 \\ 0 \end{bmatrix} = \begin{bmatrix} 1 \\ 0 \\ 0 \\ 0 \end{bmatrix}. $$
Da mesma forma, o estado $\ket{p}$ para o inteiro $p$ representa um estado quântico que codifica em
representação binária o inteiro $p$. Por exemplo, se quisermos expressar o número $5$ usando uma
codificação binária não assinada, poderíamos ser expressar igualmente como
$$ \ket{1}\ket{0}\ket{1} = \ket{101} = \ket{5}. $$
Nessa notação $\ket{0}$, não é necessário fazer referência a um estado de qubit único, mas um registro de
qubit armazenando uma codificação binária de $0$. As diferenças entre essas duas notações normalmente
ficam claras com o contexto. Essa convenção é útil para simplificar o primeiro exemplo que pode ser escrito de
qualquer uma das seguintes maneiras:
$$ \begin{bmatrix}1 \\ 0 \end{bmatrix}\otimes \cdots \otimes\begin{bmatrix}1 \\ 0 \end{bmatrix} = \ket{0}
\otimes \cdots \otimes \ket{0}= |0\cdots 0\rangle = \ket{0}^{\otimes n} $$
em que $\ket{0}^{\otimes n}$ representa o produto tensorial de $n$ $\ket{0}$ estados quânticos.

Exemplo: descrever a superposição com notação de Dirac


Como outro exemplo de como você pode usar a notação de Dirac para descrever um estado quântico, considere
as seguintes maneiras equivalentes de escrever um estado quântico que é uma superposição igual em cada
cadeia de caracteres de bits possível de comprimento $n$
$$ H^{\otimes n} \ket{0} = \frac{1}{2^{n/2}} \sum_{ j=0}^{2^n-1} \ket{ j} = \ket{+}^{\otimes n}. $$
Aqui você pode imaginar por que a soma vai de $0$ a $2^{n}-1$ para $n$ bits. Primeiro, observe que há
$2^{n}$ configurações diferentes que podem ser executadas por $n$ bits. Você pode ver isso observando que
um bit pode ocupar $2$ valores, mas dois bits podem ter $4$ valores e assim por diante. Em geral, isso significa
que há $2^n$ diferentes cadeias de caracteres de bits possíveis, mas o maior valor codificado em qualquer uma
delas $1\cdots 1=2^n-1$ e, portanto, é o limite superior para a soma. Como uma observação adicional, neste
exemplo, não usamos $\ket{+}^{\otimes n}=\ket{+}$ em analogia com $\ket{0}^{\otimes n} = \ket{0}$ porque
essa convenção de notação geralmente é reservada para o estado de base computacional com cada qubit
inicializado como zero. Embora essa convenção seja sensata nesse caso, ela não é empregada na literatura de
computação quântica.

Expressa linearidade com notação de Dirac


Outro recurso interessante da notação de Dirac é o fato de que ela é linear. Por exemplo, para dois números
complexos $\alpha$ e $\beta$, podemos escrever
$$ \ket{\psi} \otimes ( \alpha\ket{\phi} + \beta\ket{\chi})= \alpha\gamma \ket{\psi}\ket{\phi} +
\beta\ket{\psi}\ket{\chi}.$$
Ou seja, você pode distribuir a notação de produto tensorial na notação de Dirac para que a obtenção de
produtos tensoriais entre os vetores de estado termine de forma semelhante à multiplicação comum.
Os vetores bra seguem uma convenção semelhante de vetores ket. Por exemplo, o vetor $\bra{\psi}\bra{\phi}$ é
equivalente ao vetor de estado $\psi^\dagger \otimes \phi^\dagger=(\psi\otimes \phi)^\dagger$. Se o vetor
ket $\ket{\psi}$ for $\alpha \ket{0} + \beta \ket{1}$ então a versão do vetor bra do vetor é
$\bra{\psi}=\ket{\psi}^\dagger = (\bra{0}\alpha^* +\bra{1}\beta^*)$.
Como exemplo, imagine que desejamos calcular a probabilidade de medir o estado $\ket{\psi} = \frac{3}{5}
\ket{1} + \frac{4}{5} \ket{0}$ usando um programa quântico para medir estados como$\ket{+}$ ou $\ket{-}$.
Então, a probabilidade de o dispositivo gerar a saída do estado é $\ket{-}$ é
$$|\braket{- | \psi}|^2= \left|\frac{1}{\sqrt{2}}(\bra{0} - \bra{1})(\frac{3}{5} \ket{1} + \frac{4}{5} \ket{0})
\right|^2=\left|-\frac{3}{5\sqrt{2}} + \frac{4}{5\sqrt{2}}\right|^2=\frac{1}{50}.$$
O fato de que o sinal negativo aparece no cálculo da probabilidade é uma manifestação da interferência
quântica, que é um dos mecanismos pelos quais a computação quântica obtém vantagens sobre a computação
clássica.

ketbra ou produto externo


O item final que vale a pena discutir na notação de Dirac é o produto ketbra ou externo. O produto externo é
representado em notações de Dirac como $\ket{\psi} \bra{\phi}$ e, às vezes, chamado de ketbras porque bras e
kets ocorrem na ordem oposta como brakets. O produto externo é definido por meio da multiplicação de matriz
como $\ket{\psi} \bra{\phi} = \psi \phi^\dagger$ para vetores de estado quântico $\psi$ e $\phi$. O exemplo
mais simples, e comprovadamente mais comum dessa notação, é
$$ \ket{0} \bra{0} = \begin{bmatrix}1\\ 0 \end{bmatrix}\begin{bmatrix}1&0 \end{bmatrix}= \begin{bmatrix}1
&0\\ 0 &0\end{bmatrix} \qquad \ket{1} \bra{1} = \begin{bmatrix}0\\ 1 \end{bmatrix}\begin{bmatrix}0&1
\end{bmatrix}= \begin{bmatrix}0 &0\\ 0 &1\end{bmatrix}. $$
Ketbras geralmente são chamados de projetores porque projetam um estado quântico em um valor fixo. Como
essas operações não são de forma unitária (e nem mesmo preservam a norma de um vetor), não deve ser
surpreendente que um computador quântico não pode aplicar de forma determinística um projetor. No entanto,
os projetores fazem um lindo trabalho de descrever a ação que a medida tem em um estado quântico. Por
exemplo, se medirmos um estado $\ket{\psi}$ como $0$, a transformação resultante que o estado enfrenta
como resultado da medida é
$$\ket{\psi} \rightseta \frac{(\ket{0} \bra{0})\ket{\psi}}{|\braket{0 | \psi}|}= \ket{0},$$
como seria esperado se você pretendesse medir o estado e o encontrasse como $\ket{0}$. Para reiterar, esses
projetores não podem ser aplicados em um estado em um computador quântico de forma determinista. Em vez
disso, eles podem ser aplicados aleatoriamente com o resultado $\ket{0}$ que aparece com alguma
probabilidade fixa. A probabilidade de tal medida ser realizada com sucesso pode ser escrita como o valor de
expectativa do projetor quântico no estado
$$ \bra{\psi} (\ket{0} \bra{0})\ket{\psi} = |\braket{\psi | 0}|^2, $$
que ilustra que os projetores simplesmente fornecem uma nova maneira de expressar o processo de medição.
Se, em vez disso, considerarmos a medição do primeiro qubit de um estado qubit como sendo $1$, também
podemos descrever esse processo de forma conveniente usando os projetores e a notação de Dirac:
$$ P(\text{primeiro qubit = 1})= \bra{\psi}\left(\ket{1}\bra{1}\otimes \boldone^{\otimes n-1}\right) \ket{\psi}. $$
Aqui, a matriz de identidade pode ser convenientemente escrita em notação de Dirac como
$$ \boldone = \ket{0} \bra{0}+\ket{1} \bra{1}= \begin{bmatrix}1&0\\ 0&1 \end{bmatrix}. $$
Para o caso em que há dois qubits, o projetor pode ser expandido como
$$ \ket{1} \bra{1} \otimes \id = \ket{1}\bra{1} \otimes (\ket{0} \bra{0}+\ket{1} \bra{1})= \ket{10}\bra{10} +
\ket{11}\bra{11}. $$
Podemos ver que isso é consistente com a discussão sobre as chances de medição dos estados de vários qubits
usando a notação de vetor de coluna:
$$ P(\text{primeiro qubit = 1})= \psi^\dagger (e_{10}e_{10}^\dagger + e_{11}e_{11}^\dagger)\psi =
|e_{10}^\dagger \psi|^2 + |e_{11}^\dagger \psi|^2, $$
que corresponde à discussão sobre a medição de várias qubits. A generalização desse resultado para o caso de
vários qubits, no entanto, é um pouco mais simples de expressar usando notação de Dirac do que a notação de
vetor de coluna e é totalmente equivalente ao modo anterior.

Operadores de densidade
Outro operador útil para expressar usando a notação de Dirac é um operador de densidade, às vezes também
conhecido como operador de estado. Um operador de densidade para um vetor de estado quântico assume a
forma $\rho = \ket{\psi} \bra{\psi}$. Esse conceito de representar o estado como uma matriz, em vez de um
vetor, geralmente é conveniente porque fornece uma maneira conveniente de representar cálculos de
probabilidade e também permite que você descreva a incerteza estatística, bem como a incerteza quântica no
mesmo sentido formal. Os operadores de estado quântico geral, em vez de vetores, são onipresentes em
algumas áreas da computação quântica, mas não são necessários para entender os conceitos básicos do campo.
Para o leitor interessado, é recomendável ler um dos livros de referência fornecidos em Para mais informações.

Q# sequências de portões equivalentes a estados quânticos


Um ponto final que vale a pena salientar sobre a notação quântica e a linguagem de programação Q#: no início
deste documento, mencionamos que o estado quântico é o objeto fundamental das informações na computação
quântica. Então, pode ser uma surpresa que em Q# não há nenhuma noção de um estado quântico. Em vez
disso, todos os estados são descritos somente pelas operações usadas para prepará-los. O exemplo anterior é
uma ilustração excelente disso. Em vez de expressar uma superposição uniforme em cada cadeia de caracteres
de bits quânticos em um registro, podemos representar o resultado como $H^{\otimes n} \ket{0}$. Essa
descrição exponencialmente mais curta do estado não apenas tem a vantagem de que podemos, de forma
clássica, raciocinar a respeito disso, mas também define de forma concisa as operações necessárias para serem
propagadas por meio da pilha de software para implementar o algoritmo. Por esse motivo, Q# é projetado para
emitir sequências de portão em vez de estados quânticos; no entanto, em um nível teórico, as duas perspectivas
são equivalentes.
Operações de medição de Pauli de um e vários
qubits
19/05/2021 • 10 minutes to read

À medida que você trabalha com o Q#, as medições de Pauli são um tipo comum de medida, que generaliza as
medidas de base computacional para incluir medidas em outras bases e de paridade entre diferentes qubits.
Nesses casos, é comum discutir a medição de um operador de Pauli, em geral, um operador como $X,Y,Z$ ou
$Z\otimes Z, X\otimes X, X\otimes Y$ e assim por diante.
Discutir a medição em termos de operadores de Pauli é especialmente comum no subcampo de correção de
erro quantum. Em Q#, seguimos uma convenção semelhante. Agora, explicamos essa exibição alternativa de
medidas.

TIP
Em Q#, os operadores de Pauli de vários qubits geralmente são representados por matrizes do tipo Pauli[] . Por
exemplo, para representar $X \otimes Z \otimes Y$, você pode usar a matriz [PauliX, PauliZ, PauliY] .

Antes de se aprofundar nos detalhes de como pensar em uma medição de Pauli, é útil pensar no que a medição
de um só qubit dentro de um computador quantum faz com o estado quantum. Imagine que tenhamos um
estado quantum $n$-qubit; então, medir um qubit imediatamente descarta metade das possibilidades de
$2^n$ em que o estado poderia estar. Em outras palavras, a medição projeta o estado quantum em um dos dois
espaços. Podemos generalizar a maneira como pensamos na medição para refletir essa intuição.
Para identificar esses subespaços de modo conciso, precisamos de uma linguagem para descrevê-los. Uma
forma de descrever os dois subespaços é especificá-los por meio de uma matriz que tem apenas dois
eigenvalues exclusivos, tomados pela convenção como sendo $\pm 1$. Para obter um exemplo simples de
descrição de subespaços dessa forma, considere $Z$:
$$ \begin{align} Z & = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}. \end{align} $$
Lendo os elementos diagonais da matriz $Z$ de Pauli, podemos ver que $Z$ tem dois eigenvectors, $\ket{0}$ e
$\ket{1}$, com eigenvalues correspondentes $\pm 1$. Portanto, se medirmos o qubit e obtivermos Zero
(correspondente ao estado $\ket{0}$), saberemos que o estado de nosso qubit é um eigenstate $+1$ do
operador $Z$. Da mesma forma, se obtivermos One , saberemos que o estado de nosso qubit é um eigenstate
$-1$ de $Z$. Esse processo é mencionado na linguagem de medidas Pauli como "medição de Pauli $Z$" e é
totalmente equivalente a executar uma medição de base computacional.
Qualquer matriz $2\times 2$ que seja uma transformação unitária de $Z$ também atende a esses critérios. Ou
seja, também poderíamos usar uma matriz $A=U^\dagger Z U$, em que $U$ é qualquer outra matriz unitária,
para gerar uma matriz que define os dois resultados de uma medida em seus $\pm 1$ eigenvectors. A notação
de medições de Pauli faz referência a essa equivalência unitária identificando as medidas $X,Y,Z$ como medidas
equivalentes que poderiam ser feitas para obter informações de um qubit. Essas medidas são fornecidas abaixo
para sua conveniência.

T RA N SF O RM A Ç Ã O UN ITÁ RIA DE M EDIÇ Ã O DE PA UL I

$Z$ $\boldone$
T RA N SF O RM A Ç Ã O UN ITÁ RIA DE M EDIÇ Ã O DE PA UL I

$X$ $H$

$Y$ $HS^{\dagger}$

Ou seja, usar essa linguagem, "medida $Y$" é equivalente a aplicar $HS^\dagger$ e, depois, medir na base
computacional, em que S é uma operação quantum intrínseca às vezes chamada de "portão de fase" e pode
ser simulada pela matriz unitária
$$ \begin{align} S = \begin{bmatrix} 1 & 0 \\ 0 & i \end{bmatrix}. \end{align} $$
Também é equivalente a aplicar $HS^\dagger$ ao vetor de estado quantum e, depois, medir $Z$, de modo que
a seguinte operação seja equivalente a Measure([PauliY], [q]) :

operation MeasureY(qubit : Qubit) : Result {


mutable result = Zero;
within {
Adjoint S(q);
H(q);
} apply {
set result = M(q);
}
return result;
}

O estado correto seria então encontrado transformando de volta para a base computacional, o que se resume a
aplicar $SH$ ao vetor de estado quantum. No snippet acima, a transformação de volta para a base
computacional é processada automaticamente pelo uso do bloco within … apply .
Em Q#, dizemos que o resultado, ou seja, as informações clássicas extraídas da interação com o estado, é dado
por um valor Result $j \in \{\texttt{Zero}, \texttt{One}\}$, que indica se o resultado está no eigenspace $(-1)^j$
do operador de Pauli medido.

Medições de vários qubits


As medições de operadores de Pauli de vários qubits são definidas da mesma forma, como visto em:
$$ Z\otimes Z = \begin{bmatrix}1 &0 &0&0\\ 0&-1&0&0\\ 0&0&-1&0\\ 0&0&0&1\end{bmatrix}. $$
Assim, os produtos tensores de dois operadores $Z$ de Pauli formam uma matriz composta por dois espaços
que consistem em $+1$ e $-1$ eigenvalues. Assim como no caso de qubit único, ambos constituem um meio
espaço, o que significa que metade do espaço do vetor acessível pertence ao eigenspace $+1$ e a metade
restante, ao eigenspace $-1$. Em geral, é fácil ver a definição do produto tensor que qualquer produto tensor de
operadores $Z$ de Pauli e a identidade também obedece a isso. Por exemplo,
$$ \begin{align} Z \otimes \boldone = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & -1 & 0 \\ 0 &
0 & 0 & -1 \end{bmatrix}. \end{align} $$
Como antes, qualquer transformação unitária dessas matrizes também descreve dois meios espaços rotulados
por eigenvalues $\pm 1$. Por exemplo, $X\otimes X = H\otimes H(Z\otimes Z)H\otimes H$ da identidade que
$Z=HXH$. Semelhante ao caso qubit, todas as medidas de Pauli qubit podem ser escritas como $U^\dagger
(Z\otimes \id) U$ para matrizes unitárias $4\times 4$ $U$. Enumeramos as transformações na tabela a seguir.
NOTE
Na tabela a seguir, usamos $\operatorname{SWAP}$ para indicar a matriz > $$ \begin{align} \operatorname{SWAP} & =
\left(\begin{matriz} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 > \end{matriz}\right) > \end{align}
$$ usado para simular a operação intrínseca SWAP .

M EDIÇ Ã O DE PA UL I T RA N SF O RM A Ç Ã O UN ITÁ RIA

$Z\otimes \boldone$ $\boldone\otimes \boldone$

$X\otimes \boldone$ $H\otimes \boldone$

$Y\otimes \boldone$ $HS^\dagger\otimes \boldone$

$\boldone \otimes Z$ $\operatorname{SWAP}$

$\boldone \otimes X$ $(H\otimes \boldone)\operatorname{SWAP}$

$\boldone \otimes Y$ $(HS^\dagger\otimes \boldone)\operatorname{SWAP}$

$Z\otimes Z$ $\operatorname{CNOT}_{10}$

$X\otimes Z$ $\operatorname{CNOT}_{10}(H\otimes \boldone)$

$Y\otimes Z$ $\operatorname{CNOT}_{10}(HS^\dagger\otimes \boldone)$

$Z\otimes X$ $\operatorname{CNOT}_{10}(\boldone\otimes H)$

$X\otimes X$ $\operatorname{CNOT}_{10}(H\otimes H)$

$Y\otimes X$ $\operatorname{CNOT}_{10}(HS^\dagger\otimes H)$

$Z\otimes Y$ $\operatorname{CNOT}_{10}(\boldone \otimes


HS^\dagger)$

$X\otimes Y$ $\operatorname{CNOT}_{10}(H\otimes HS^\dagger)$

$Y\otimes Y$ $\operatorname{CNOT}_{10}(HS^\dagger\otimes
HS^\dagger)$

Aqui, a operação CNOT é exibida pelo motivo a seguir. Cada medida de Pauli que não inclui a matriz $\boldone$
é equivalente a um unitário de $Z\otimes Z$ pelo raciocínio acima. O eigenvalues de $Z\otimes Z$ dependem
apenas da paridade do qubits que compõem cada vetor base computacional, e as operações controladas não
servem para computar essa paridade e armazená-las no primeiro bit. Depois que o primeiro bit é medido,
podemos recuperar a identidade do espaço resultante, que é equivalente a medir o operador de Pauli.
Uma observação adicional: embora possa ser tentador pressupor que medir $Z\otimes Z$ é o mesmo que
medir sequencialmente $Z\otimes \mathbb{1}$ e então $\mathbb{1} \otimes Z$, essa suposição seria falsa. O
motivo é que medir $Z\otimes Z$ projeta o estado quantum no eigenstate $+1$ ou $-1$ desses operadores.
Medir $Z\otimes \mathbb{1}$ e então $\mathbb{1} \otimes Z$ projeta o vetor de estado quantum primeiro em
um meio espaço de $Z\otimes \mathbb{1}$ e depois em um meio espaço $\mathbb{1} \otimes Z$. Como há
quatro vetores de base computacional, a execução de ambas as medidas reduz o estado para um quarto de
espaço e, portanto, a reduz para um só vetor de computação.
Correlações entre qubits
Outra maneira de observar a medição de produtos tensor de matrizes de Pauli como $X\otimes X$ ou
$Z\otimes Z$ é que essas medidas permitem que você examine as informações armazenadas nas correlações
entre os dois qubits. Medir $X\otimes \id$ permite que você examine as informações armazenadas localmente
no primeiro qubit. Embora os dois tipos de medidas sejam igualmente valiosos na computação quântica, o
primeiro ilumina o poder da computação quântica. Ele revela que, na computação quântica, geralmente as
informações que você deseja aprender não são armazenadas em qualquer qubit único, mas, em vez disso,
armazenadas não localmente em todos os qubits de uma vez, portanto, apenas olhar para elas por meio de uma
medição conjunta (por exemplo, $Z\otimes Z$) faz essas informações se tornam manifestas.
Por exemplo, na correção de erros, muitas vezes desejamos saber qual erro enquanto não aprendemos nada
sobre o estado que estamos tentando proteger. O exemplo de código de inversão de bits mostra um exemplo de
como você pode fazer isso usando medidas como $Z \otimes Z \otimes \id$ e $\id \otimes Z \otimes Z$.
Operadores Pauli arbitrários, como $X\otimes Y \otimes Z \otimes \boldone$, também podem ser medidos.
Todos esses produtos tensores de operadores de Pauli têm apenas dois eigenvalues $\pm 1$ e ambos
eigenspaces constituem metades do espaço de vetor inteiro. Portanto, eles coincidem com os requisitos
declarados acima.
Em Q#, essas medidas retornam $j$ se a medida produz um resultado no eigenspace de sinal $(-1)^j$. Ter
medidas de Pauli como um recurso interno no Q# é útil porque a medição desses operadores requer cadeias
longas de portões e transformações de base NÃO controlados para descrever a diagonalização do portão $U$
necessária para expressar a operação como um produto tensor de $Z$ e $\id$. Ao ser capaz de especificar que
você deseja realizar uma dessas medidas predefinidas, você não precisa se preocupar em como transformar sua
base de modo que uma medida de base computacional forneça as informações necessárias. Q# manipula todas
as transformações de base necessárias automaticamente para você. Para obter mais informações, confira as
operações Measure e MeasurePaulis .

O teorema Sem Clonagem


As informações quantum são poderosas. Elas nos permitem fazer coisas incríveis, como fatorar números com
uma rapidez exponencialmente maior do que os melhores algoritmos clássicos conhecidos ou simular com
eficiência sistemas de elétrons correlacionados que tradicionalmente exigiam um custo exponencial para serem
simulados com precisão. No entanto, há limitações ao poder da computação quântica. Uma delas é fornecida
pelo teorema Sem Clonagem.
O teorema Sem Clonagem tem um nome inadequado. Ele não permite a clonagem de estados quantum
genéricos por um computador quantum. A prova do teorema é bastante simples. Embora uma prova completa
do teorema sem clonagem seja um pouco técnica para nossa discussão aqui, a prova no caso de nenhum qubits
auxiliar adicional está dentro de nosso escopo.
Para esse computador quantum, a operação de clonagem deve ser descrita por uma matriz unitária. Não
permitimos a medição, já que corromperia o estado quantum que estamos tentando clonar. Para simular a
operação de clonagem, queremos que a matriz unitária usada tenha a propriedade que $$ U \ket{\psi} \ket{0} =
\ket{\psi} \ket{\psi} $$ para qualquer estado $\ket{\psi}$. Em seguida, a propriedade linear da multiplicação de
matriz implica que, para qualquer segundo estado quantum $\ket{\phi}$,
$$ \begin{align} U \left[ \frac{1}{\sqrt{2}}\left(\ket{\phi}+\ket{\psi} \right) \right] \ket{0} & = \frac{1}{\sqrt{2}}
U\ket{\phi}\ket{0} + \frac{1}{\sqrt{2}} U\ket{\psi}\ket{0} \\ & = \frac{1}{\sqrt{2}} \left( \ket{\phi} \ket{\phi} +
\ket{\psi} \ket{\psi} \right) \\ & \ne \left( \frac{1}{\sqrt{2}}\left(\ket{\phi}+\ket{\psi} \right) \right) \otimes \left(
\frac{1}{\sqrt{2}}\left(\ket{\phi}+\ket{\psi} \right) \right). \end{align} $$
Isso fornece a intuição fundamental por trás do teorema Sem Clonagem: qualquer dispositivo que copia um
estado quantum desconhecido deve induzir erros em pelo menos alguns dos estados que ele copia. Embora a
principal suposição de que o clonador atue linearmente no estado de entrada possa ser violada por meio da
adição e da medição do qubits auxiliar, essas interações também vazam informações sobre o sistema por meio
de estatísticas de medição e impedem a clonagem exata nesses casos. Para obter uma prova mais completa do
teorema Sem Clonagem, confira Para obter mais informações.
O teorema Sem clonagem é importante para uma compreensão qualitativa da computação quântica porque, se
você puder clonar os estados quantum de modo não dispendioso, terá uma capacidade quase mágica de
aprender com os estados quantum. Na verdade, você poderia violar o louvado princípio de incerteza de
Heisenberg. Como alternativa, você pode usar um clonador ideal para pegar uma amostra de uma distribuição
do quantum complexa e aprender tudo o que puder sobre essa distribuição de apenas uma amostra. Isso seria
como jogar uma moeda e observar as caras e então contar a um amigo sobre o resultado pedindo para ele
responder, a "Ah, a distribuição dessa moeda deveria ser Bernoulli com $p=0,512643\ldots$!" Essa instrução
não teria sentido, pois um bit de informação (o resultado de cara) simplesmente não pode fornecer os muitos
bits de informações necessários para codificar a distribuição sem informações substanciais anteriores. Da
mesma forma, sem informações anteriores, não podemos clonar perfeitamente um estado quantum, da mesma
forma que não podemos preparar um conjunto dessas moedas sem saber $p$.
As informações não são gratuitas na computação quântica. Cada qubit medido fornece um bit de informação e
o teorema Sem Clonagem teorema mostra que não há nenhuma backdoor que possa ser explorada para
contornar os prós e contras fundamentais entre as informações obtidas sobre o sistema e o distúrbio invocado
sobre ele.
Diagramas de circuito quântico
01/05/2021 • 5 minutes to read

Este artigo aborda as convenções de diagramas de circuito quântico. As operações quânticas são mais fáceis de
entender em um diagrama do que na matriz de escrita equivalente,uma vez que você conheça as convenções
visuais.

Exemplo: transformação unitária


Considere a transformação unitária $ \text { CNOT } _ { 01 } (H \otimes 1) $. Essa sequência de portas é
fundamental para a computação quântica porque cria um estado de dois qubit entrelaçados ao máximo:
$$\mathrm{CNOT}_{01}(H\otimes 1)\ket{00} = \frac{1}{\sqrt{2}} \left(\ket{00} + \ket{11} \right),$$
Operações com essa ou mais complexidade são onipresentes em algoritmos quânticos e correção de erro
quântico. Um diagrama de circuito quântico é uma ferramenta conveniente para ilustrar as operações.
O diagrama de circuito para preparar esse estado quântico entrelaçado máximo é:

Regras de diagrama de circuito quântico


Em um diagrama de circuito, cada linha sólida representa um qubit ou, mais comumente, um registro de qubit.
Por regra, a linha superior é o qubit de registro $0$ e o restante é rotulado em sequência. O circuito de exemplo
acima foi representado como se estivesse agindo em dois qubits (ou dois registros equivalentes que consistem
em um qubit). As portas que atuam em um ou mais registros de qubit são indicadas como caixas. Por exemplo,
o símbolo

é uma operação deHadamard agindo em um registro de qubit único.


As portas quânticas são ordenadas em ordem cronológica com a porta mais à esquerda como a porta aplicada
pela primeira vez ao qubits. Em outras palavras, se você modelar os fios como se estivessem sustentando o
estado quântico, os fios trazem o estado quântico por meio de cada uma das portas no diagrama da esquerda
para a direita. Ou seja

é a matriz unitária $CBA$. A multiplicação de matriz obedece à regra oposta: a matriz mais à direita é aplicada
primeiro. Em diagramas de circuito quântico no entanto, a porta mais à esquerda é aplicada primeiro. Ás vezes
essa diferença pode confundir, portanto, é importante observar essa diferença significativa entre notação
algébrica linear e diagramas de circuito quântico.

Entradas e saídas de circuitos quântico


Todos os exemplos anteriores tinham exatamente o mesmo número de fios (qubits) de entrada para uma porta
quântica como o número de fios de saída da porta quântica. A princípio, pode parecer razoável que os circuitos
quânticos pudessem ter mais ou menos saídas do que as entradas em geral. No entanto, isso é impossível, pois
todas as operações quânticas, exceto a medição, são unitárias e, portanto, reversíveis. Se elas não tivessem o
mesmo número de saídas e entradas, não seriam reversíveis e, portanto, não seriam unitárias, o que é uma
contradição. Por esse motivo, qualquer caixa desenhada em um diagrama de circuito deve ter precisamente o
mesmo número de fios inserindo-a e retirando-a.
Os diagramas de circuito qubit seguem regras semelhantes para os qubit únicos. Para esclarecer, podemos
definir uma operação qubit unitária de dois $B$ a ser $ (H S\otimes X)$ e expressar o circuito equivalentemente
como

Também podemos exibir o $B$ representando uma ação em um único registro de duas qubit em vez de
registros qubit 1 e qubit 2, dependendo do contexto no qual o circuito é usado. Talvez a propriedade mais útil
desses diagramas de circuito abstrato seja que eles permitem que algoritmos quânticos complexos sejam
descritos em um alto nível sem precisar compilá-los em portas fundamentais. Isso significa que é possível intuir
sobre o fluxo de dados de um grande algoritmo quântico sem a necessidade de entender todos os detalhes de
como cada uma das sub-rotinas dentro do algoritmo funciona.

Portas controladas
Outro fundamento dos diagramas de vários qubit de circuito quântico é o controle. A ação de uma porta
quântica controlada individualmente, indicado em $ \Lambda(G)$, em que um único valor de qubit controla o
aplicativo de $G$, pode ser compreendido examinando o exemplo a seguir de uma entrada de estado do
produto $ \Lambda (G) ( \alpha \ket { 0 } + \beta \ket { 1 } ) \ket { \psi } = \alpha \ket { 0 } \ket { \psi } + \beta \ket
{ 1 } G \ket { \psi } $. Ou seja, a porta controlada aplica-se $G $ ao registro que contém $ \psi $, se e somente se,
o qubit de controle usar o valor $1$. Em geral, descrevemos essas operações controladas em diagramas de
circuito como

Aqui, o círculo preto indica o bit quântico no qual a porta é controlada e uma transmissão vertical denota o
unitário que é aplicado quando o qubit de controle usa o valor $1$. Para os casos especiais em que $G=X$ e
$G=Z$, apresentamos a seguinte notação para descrever a versão controlada das portas (observe que a porta-X
controlada é a porta $CNOT$ ):

Q# fornece métodos para gerar automaticamente a versão controlada de uma operação, o que evita que o
programador tenha que codificar essas operações manualmente. Um exemplo disso é mostrado abaixo:

operation PrepareSuperposition(qubit : Qubit) : Unit


is Ctl { // Auto-generate the controlled specialization of the operation
H(qubit);
}

Operador de medida
A operação restante a ser visualizada em diagramas de circuito é a medição. A medição usa um registro qubit,
mede-o e gera o resultado como informações clássicas. Uma operação de medição é indicada por um símbolo
de medidor e sempre usa como entrada um registro de qubit (indicado por uma linha sólida) e gera
informações clássicas (indicadas por uma linha dupla). Especificamente, esse sub-circuito é semelhante a:

Q# implementa um operador de medida para essa finalidade. Consulte a seção sobre Medições para obter mais
informações.
Da mesma forma, o sub-circuito

fornece um portão controlado de forma clássica, onde $G$ é aplicado condicionalmente no bit de controle
clássico, sendo o valor $1 $.

Diagrama de circuito de portabilidade


A portabilidade quântica talvez seja o melhor algoritmo quântico para ilustrar esses componentes. Você pode
aprender na prática com o correspondente do Quantum Kata.A portabilidade quântica é um método para mover
dados dentro de um computador quântico (ou até mesmo entre computadores quânticos distantes em uma
rede quântica) por meio do uso de entrelaçamento e medição. Curiosamente, a portabilidade pode mover um
estado quântico, como o valor em um determinado qubit, de um qubit para outro, sem nem mesmo saber qual
é o valor do qubit. Isso é necessário para que o protocolo funcione de acordo com as leis da mecânica quântica.
O circuito de portabilidade quântica é fornecido abaixo. Também fornecemos uma versão por escrito do circuito
para ilustrar como ler o circuito quântico.

H-Gate Measurement
Qubit 0
Qubit 1
space
Qubit 2 CNOT01

CNOT12 Classically controlled


quantum gates

time

Teleportation Circuit Annotated Teleportation Circuit


Trabalhar com Oracle Quantum e definir seus
parâmetros
13/05/2021 • 3 minutes to read

Um Oracle $O$ é uma operação de "caixa preta" que é usada como entrada para outro algoritmo. Muitas vezes,
essas operações são definidas usando uma função clássica $f : \{0, 1\}^n \to \{0, 1\}^m$ que usa uma entrada
binária de $n$ bits e produz uma saída binária de $m$ bits. Para fazer isso, considere uma entrada binária
específica $x = (x_{0}, x_{1}, \dots, x_{n-1})$. Podemos rotular estados qubit como $\ket{\vec{x}} = \ket{x_{0}}
\otimes \ket{x_{1}} \otimes \cdots \otimes \ket{x_{n-1}}$.
Primeiro devemos tentar definir $O$ para que $O\ket{x} = \ket{f(x)}$, mas isso traz alguns problemas. Em
primeiro lugar, $f$ pode ter um tamanho diferente de entrada e saída ($n \ne m$), de modo que aplicar $O$
alteraria o número de qubits no registro. Em segundo lugar, mesmo se $n = m$, a função pode não ser
invertível: se $f(x) = f(y)$ para qualquer $x \ne y$, então $O\ket{x} = O\ket{y}$ mas $O^\dagger O\ket{x} \ne
O^\dagger O\ket{y}$. Isso significa que não será possível construir a operação adjunta $ O^\dagger$ e os
Oracles precisarão ter uma adjunta definida para eles.

Definir um Oracle pelo seu efeito nos estados da base computacional


Podemos tratar esses dois problemas introduzindo um segundo registro de $m$ qubits para manter nossa
resposta. Em seguida, definimos o efeito do Oracle em todos os estados de base computacional: para todos os
$x \in \{0, 1\}^n$ e $y \in \{0, 1\}^m$,
$$ \begin{align} O(\ket{x} \otimes \ket{y}) = \ket{x} \otimes \ket{y \oplus f(x)}. \end{align} $$
Agora $O = O^\dagger$ por construção. Portanto, resolvemos os dois problemas anteriores.

TIP
Para ver que $O = O^{\dagger}$, observe que $O^2 = \boldone$ já que $a \oplus b \oplus b = a$ para todo $a, b \in {0,
1}$. Como resultado, $O \ket{x} \ket{y \oplus f(x)} = \ket{x} \ket{y \oplus f(x) \oplus f(x)} = \ket{x} \ket{y}$.

É importante ressaltar que definir um Oracle dessa maneira para cada estado de base computacional
$\ket{x}\ket{y}$ também define como o $O$ age para qualquer outro estado. Isso decorre imediatamente do
fato de que $O$, como todas as operações quantum, é linear no estado em que atua. Considere a operação
Hadamard, por exemplo, que é definida por $H \ket{0} = \ket{+}$ e $H \ket{1} = \ket{-}$. Se quisermos saber
como $H$ atua em $\ket{+}$, podemos usar $H$ como linear,
$$ \begin{align} H\ket{+} & = \frac{1}{\sqrt{2}} H(\ket{0} + \ket{1}) = \frac{1}{\sqrt{2}} (H\ket{0} + H\ket{1}) \\ &
= \frac{1}{\sqrt{2}} (\ket{+} + \ket{-}) = \frac12 (\ket{0} + \ket{1} + \ket{0} - \ket{1}) = \ket{0}. \end{align} $$
No caso de definir nosso Oracle $O$, podemos dizer de forma semelhante que qualquer estado $\ket{\psi}$ em
$n + m$ qubits pode ser escrito como
$$ \begin{align} \ket{\psi} & = \sum_{x \in \{0, 1\}^n, y \in \{0, 1\}^m} \alpha(x, y) \ket{x} \ket{y} \end{align} $$
em que $\alpha : \{0, 1\}^n \times \{0, 1\}^m \to \mathbb{C}$ representa os coeficientes do estado $\ket{\psi}$.
Assim,
$$ \begin{align} O \ket{\psi} & = O \sum {x \in \{0, 1\}^n, y \in \{0, 1\}^m} \alpha(x, y) \ket{x} \ket{y} \\ & =
\sum {x \in \{0, 1\}^n, y \in \{0, 1\}^m} \alpha(x, y) O \ket{x} \ket{y} \\ & = \sum_{x \in \{0, 1\}^n, y \in \{0,
1\}^m} \alpha(x, y) \ket{x} \ket{y \oplus f(x)}. \end{align} $$

Fase Oracle
Como alternativa, podemos codificar $f$ em um Oracle $O$ aplicando uma fase com base na entrada para $O$.
Por exemplo, poderemos definir $O$ de tal forma que $$ \begin{align} O \ket{x} = (-1)^{f(x)} \ket{x}. \end{align}
$$ Se uma fase Oracle age em um registro inicialmente em um estado de base computacional $\ket{x}$, essa
fase é uma fase global e, portanto, não observável. Mas esse Oracle pode ser um recurso muito poderoso se
aplicado a uma superposição ou como uma operação controlada. Por exemplo, considere uma fase Oracle
$O_f$ para uma função $f$ de qubit única. Então, $$ \begin{align} O_f \ket{+} & = O_f (\ket{0} + \ket{1}) /
\sqrt{2} \\ & = ((-1)^{f(0)} \ket{0} + (-1)^{f(1)} \ket{1}) / \sqrt{2} \\ & = (-1)^{f(0)} (\ket{0} + (-1)^{f(1) - f(0)}
\ket{1}) / \sqrt{2} \\ & = (-1)^{f(0)} Z^{f(0) - f(1)} \ket{+}. \end{align} $$

NOTE
Observe que $Z^{-1}=Z^{\dagger}=Z$ e portanto, $Z^{f(0)-f(1)}=Z^{f(1)-f(0)}.$

Em geral, as duas exibições Oracle podem ser ampliadas para representar funções clássicas que retornam
números reais em vez de apenas um único bit.
Escolher a melhor maneira de implementar um Oracle depende muito da forma como esse Oracle será usado
dentro de um determinado algoritmo. Por exemplo, o algoritmo Deutsch-Jozsa depende do Oracle
implementado da primeira forma, enquanto o algoritmo de Grover depende do Oracle implementado da
segunda maneira.
Para obter mais detalhes, sugerimos conversar em Gilyén et al. 1711.00465.
Algoritmo de pesquisa da Teoria de Grover
30/04/2021 • 7 minutes to read

Neste artigo, você encontrará uma explicação teórica detalhada dos princípios matemáticos que fazem com que
o algoritmo de Grover funcione.
Para uma implementação prática do algoritmo de Grover para resolver problemas matemáticos, você pode ler
nosso guia para implementar o algoritmo de pesquisa de Grover.

Declaração do problema
Qualquer tarefa de pesquisa pode ser expressa com uma função abstrata $f(x)$ que aceita itens de pesquisa
$x$. Se o item $x$ for uma solução para a tarefa de pesquisa, então $f(x)=1$. Se o item $x$ não for uma
solução, então $f(x)=0$. O problema de pesquisa consiste em localizar qualquer item $x_0$, tal que $f(x_0)=1$.
A tarefa que o algoritmo de Grover pretende resolver pode ser expressa da seguinte maneira: dada uma função
clássica $f(x):\{0,1\}^n \rightseta\{0,1\}$, em que $n$ é o tamanho do bit do espaço de pesquisa, encontre uma
entrada $x_0$ para a qual $f(x_0)=1$. A complexidade do algoritmo é medida pelo número de usos da função
$f(x)$. De modo clássico, no pior cenário, temos que avaliar $f(x)$ um total de $N-1$ vezes, em que $N=2^n$,
experimentando todas as possibilidades. Depois de $N-1$ elementos, sabemos que esse deve ser o último
elemento. Veremos que o algoritmo quântico de Grover pode resolver esse problema muito mais rapidamente,
garantindo uma velocidade quadrática. A quadrática aqui implica que apenas cerca de $\sqrt{N}$ avaliações
seriam necessárias, em comparação com $N$.

Estrutura do algoritmo
Suponha que temos $N=2^n$ itens qualificados para a tarefa de pesquisa e os indexamos atribuindo a cada
item um inteiro de $0$ a $N-1$. Além disso, suponha que sabemos que há $M$ entradas válidas diferentes, o
que significa que há $M$ entradas para as quais $f(x)=1$. As etapas do algoritmo são as seguintes:
1. Comece com um registro de $N$ qubits inicializados no estado $\ket{0}$.
2. Prepare o registro em uma sobreposição uniforme aplicando $H$ a cada qubit do registro:
$$|\text{registro}\rangle=\frac{1}{\sqrt{N}} \sum_{x=0}^{N-1}|x\rangle$$
3. Aplique as seguintes operações ao registro $N_{\text{ideal}}$ vezes:
a. A fase $O_f$ do oráculo que aplica uma mudança de fase condicional de $-1$ para os itens da
solução.
b. Aplique $H$ a cada qubit no registro.
c. Uma mudança de fase condicional de $-1$ a cada estado de base computacional, exceto $\ket{0}$.
Isso pode ser representado pela operação unitária $o_0$, já que $O_0$ representa a mudança de fase
condicional para $\ket{0}$ apenas.
d. Aplique $H$ a cada qubit no registro.
4. Meça o registro para obter o índice de um item que é uma solução com uma probabilidade muito alta.
5. Verifique se é uma solução válida. Caso contrário, comece novamente.
$N_\text{ideal} = \left\lfloor \frac{\pi}{4}\sqrt{\frac{N}{M}}-\frac{1}{2} \right\rfloor$ é o número ideal de
iterações que maximizam a probabilidade de obter o item correto medindo o registro.
NOTE
A aplicação conjunta das etapas 3.b, 3.c e 3.d normalmente é conhecida na literatura como operador de difusão de
Grover.

A operação unitária geral aplicada ao registro é:


$$(-H^{\otimes n}O_0H^{\otimes n}O_f)^{N_{\text{ideal}}}H^{\otimes n}$$

Seguindo o estado do registro, passo a passo


Para ilustrar o processo, vamos seguir as transformações matemáticas do estado do registro para um caso
simples no qual temos apenas dois qubits e o elemento válido é $\ket{01}.$
1. Começamos com o registro no estado: $$|\text{registro}\rangle=|00\rangle$$
2. Depois de aplicar $H$ a cada qubit, o estado do registro transforma-se em: $$|\text{registro}\rangle =
\frac{1}{\sqrt{4}} \sum_{i \in \{0,1\}^2}|i\rangle=\frac12(\ket{00}+\ket{01}+\ket{10}+\ket{11})$$
3. Em seguida, aplicamos a fase de oráculo para obter: $$|\text{registro}\rangle = \frac12(\ket{00}-
\ket{01}+\ket{10}+\ket{11})$$
4. Em seguida, $H$ atua em cada qubit novamente para fornecer: $$|\text{registro}\rangle =
\frac12(\ket{00}+\ket{01}-\ket{10}+\ket{11})$$
5. Agora aplicamos a mudança de fase condicional a todos os estados, exceto $\ket{00}$:
$$|\text{registro}\rangle = \frac12(\ket{00}-\ket{01}+\ket{10}-\ket{11})$$
6. Agora, terminamos a primeira iteração de Grover aplicando $H$ novamente para obter:
$$|\text{registro}\rangle = \ket{01}$$
Encontramos o item válido em uma única iteração. Como veremos mais adiante, isso ocorre porque, para
N=4 e um único item válido, $N_\text{ideal}=1$.

Explicação geométrica
Para ver por que o algoritmo de Grover funciona, vamos estudar o algoritmo de uma perspectiva geométrica.
Permita que $\ket{ruim}$ seja a sobreposição de todos os estados que não são uma solução para o problema de
pesquisa. Supondo que há $M$ soluções válidas, obtemos:
$$\ket{\text{ruim}}=\frac{1}{\sqrt{N-M}}\sum_{x:f(x)=0}\ket{x}$$
Definimos o estado $\ket{bom}$ como a sobreposição de todos os estados que são uma solução para o
problema de pesquisa:
$$\ket{\text{bom}}=\frac{1}{\sqrt{M}}\sum_{x:f(x)=1}\ket{x}$$
Como bom e ruim são conjuntos mutuamente exclusivos porque um item não pode ser válido e não válido, os
estados $\ket{bom}$ e $\ket{ruim}$ são ortogonais. Ambos os estados formam a base ortogonal de um plano
no espaço de vetor. Podemos usar esse plano para visualizar o algoritmo.
Agora, suponha que $\ket{\psi}$ seja um estado arbitrário que reside no plano, distribuído por
$\ket{\text{bom}}$ e $\ket{\text{ruim}}$. Qualquer estado que estiver nesse plano pode ser expresso como:
$$\ket{\psi} = \alpha \ket{\text{bom}} + \beta \ket{\text{ruim}}$$
em que $ \alpha$ e $ \beta$ são números reais. Agora, vamos introduzir o operador de reflexão $R_
{\ket{\psi}}$, em que $\ket{\psi}$ é qualquer estado qubit que esteja no plano. O operador é definido como:
$$R_{\ket{\psi}}=2\ket{\psi}\bra{\psi}-\mathcal{I}$$
Ele é chamado de operador de reflexão sobre $\ket{\psi}$, pois pode ser interpretado geometricamente como
reflexão sobre a direção de $\ket{\psi}$. Para vê-lo, use a base ortogonal do plano formado por $\ket{\psi}$ e
seu complemento ortogonal $\ket{\psi^{\perp}}$. Qualquer estado $\ket{\xi}$ do plano pode ser decomposto
de acordo com esta base:
$$\ket{\xi}=\mu \ket{\psi} + \nu {\ket{\psi^{\perp}}}$$
Se aplicarmos o operador $R_{\ket{\psi}}$ a $\ket{\xi}$, obteremos:
$$R_{\ket{\psi}}\ket{\xi}=\mu \ket{\psi} - \nu {\ket{\psi^{\perp}}}$$
O operador $R_\ket{\psi}$ inverte o componente ortogonal para $\ket{\psi}$, mas deixa o componente
$\ket{\psi}$ inalterado. Portanto, $R_\ket{\psi}$ é uma reflexão sobre $\ket{\psi}$.

No algoritmo de Grover, após a primeira aplicação de $H$ a cada qubit, começamos com uma sobreposição
uniforme de todos os estados. Isso pode ser escrito como:
$$\ket{\text{todos}} = \sqrt{\frac{M}{N}}\ket{\text{bom}} + \sqrt{\frac{N-M}{N}}\ket{\text{ruim}}$$
E, portanto, o estado reside no plano. Observe que a probabilidade de obter um resultado correto ao medir a
partir da sobreposição igual é apenas $|\braket{\text{bom}|{\text{todos}}}|^2=M/N$, que é o que esperamos de
uma estimativa aleatória.
O$O_f$ do oráculo adiciona uma fase negativa a qualquer solução para o problema de pesquisa. Portanto, ele
pode ser escrito como uma reflexão sobre o eixo $\ket{\text{ruim}}$:
$$O_f = R_{\ket{\text{ruim}}} = 2\ket{\text{ruim}}\bra{\text{ruim}} - \mathcal{I}$$
De forma análoga, a mudança de fase condicional $O_0$ é apenas uma reflexão invertida sobre o estado
$\ket{0}$:
$$O_0 = R_{\ket{0}}= -2\ket{0}\bra{0} + \mathcal{I}$$
Sabendo esse fato, é fácil verificar se a operação de difusão Grover $-H^{\otimes n} O_0 H^{\otimes n}$
também é uma reflexão sobre o estado $\ket{todos}$. Basta:
$$-H^{\otimes n} O_0 H^{\otimes n}=2H^{\otimes n}\ket{0}\bra{0}H^{\otimes n} -H^{\otimes
n}\mathcal{I}H^{\otimes n} = 2\ket{\text{todos}}\bra{\text{todos}} - \mathcal{I} = R_{\ket{\text{todos}}}$$
Acabamos de demonstrar que cada iteração do algoritmo de Grover é uma composição de dois reflexos
$R_\ket{\text{ruim}}$ e $R_\ket{\text{todos}}$.

O efeito combinado de cada iteração de Grover é uma rotação no sentido anti-horário de um ângulo $2\theta$.
Felizmente, o ângulo $\theta$ é fácil de encontrar. Como $\theta$ é apenas o ângulo entre $\ket{\text{todos}}$ e
$\ket{\text{ruim}}$, podemos usar o produto escalar para encontrar o ângulo. Sabemos que
$\cos{\theta}=\braket{\text{todos}|\text{ruim}}$, então, precisamos calcular $\braket{\text{todos}|\text{ruim}}$.
Da decomposição de $\ket{\text{todos}}$ em termos de $\ket{\text{ruim}}$ e $\ket{\text{bom}}$, vemos que:
$$\theta = \arccos{\left(\braket{\text{todos}|\text{ruim}}\right)}= \arccos{\left(\sqrt{\frac{N-M}{N}}\right)} $$
O ângulo entre o estado do registro e o estado $\ket{\text{bom}}$ diminuirá com cada iteração, resultando em
uma maior probabilidade de medir um resultado válido. Para calcular essa probabilidade, precisamos apenas
calcular $|\braket{\text{bom}|\text{registro}}|^2$. Denotando o ângulo entre $\ket{\text{bom}}$ e
$\ket{\text{registro}}$ $\gamma (k)$, em que $k$ é a contagem da iteração, obtemos:
$$\gamma (k) = \frac{\pi}{2}-\theta -k2\theta = \frac{\pi}{2} -(2k + 1) \theta $$
Portanto, a probabilidade de sucesso é:
$$P(\text{sucesso}) = \cos^2(\gamma(k)) = \sin^2\left[(2k +1)\arccos \left( \sqrt{\frac{N-M}{N}}\right)\right]$$

Número ideal de iterações


Como a probabilidade de sucesso pode ser escrita como uma função do número de iterações, podemos
encontrar o número de iterações $N_{\text{ideal}}$ calculando o menor inteiro positivo que (aproximadamente)
maximiza a função de probabilidade de sucesso.

Sabemos que $\sin^2{x}$ atinge seu máximo por $x=\frac{\pi}{2}$, portanto, só precisamos fazer:
$$\frac{\pi}{2}=(2k_{\text{ideal}} +1)\arccos \left( \sqrt{\frac{N-M}{N}}\right)$$
Isso nos fornece:
$$k_{\text{ideal}} = \frac{\pi}{4\arccos\left(\sqrt{1-M/N}\right)}-1/2 = \frac{\pi}{4}\sqrt{\frac{N}{M}}-\frac{1}{2}-
O\left(\sqrt\frac{M}{N}\right)$$
Em que, na última etapa, usamos o fato de que $\arccos \sqrt{1-x} = \sqrt{x} + O(x^{3/2})$.
Portanto, podemos escolher $N_\text{ideal}$ para ser $N_\text{ideal} = \left\lfloor \frac{\pi}{4}\sqrt{\frac{N}
{M}}-\frac{1}{2} \right\rfloor$.

Análise de complexidade
Da análise anterior, sabemos que precisamos de $O\left(\sqrt{\frac{N}{M}}\right)$ consultas do oráculo $O_f$
para encontrar um item válido. No entanto, o algoritmo pode ser implementado com eficiência em termos de
complexidade de tempo? $O_0$ se baseia em operações boolianas de computação em $n$ bits e é conhecido
como passível de implementação usando $O(n)$ portas. Também temos duas camadas de $n$ portas
Hadamard. Os dois componentes exigem, portanto, apenas $O(n)$ portas por iteração. Como $N=2^n$, temos
que $O(n)=O(log(N))$. Portanto, se precisarmos de $O\left(\sqrt{\frac{N}{M}}\right)$ iterações e precisarmos
de $O(log(N))$ portas por iteração, a complexidade do tempo total (sem levar em conta a implementação do
oráculo) é $O\left(\sqrt{\frac{N}{M}}log(N)\right)$.
A complexidade geral do algoritmo dependerá, em última análise, da complexidade da implementação do $O_f$
do oráculo. Se a avaliação de uma função for muito mais complicada em um computador quântico do que em
um clássico, o runtime geral do algoritmo será mais longo no caso quântico, embora use menos consultas
tecnicamente.

Referências
Se quiser continuar aprendendo o algoritmo de Grover, você pode verificar uma das seguintes fontes:
Artigo original de Lov K. Grover
Seção de algoritmos de pesquisa quânticos de Nielsen, M. A. & Chuang, I. L. (2010). Computação quântica e
informações do Quantum.
Algoritmo de Grover em Arxiv.org
Glossário de computação quântica
01/05/2021 • 7 minutes to read

Adjunto
A tranposição complexa conjugada de uma operação. Para operações que implementam um operador unitário,
o adjacente é o inverso da operação e é indicado por um símbolo de cruz. Por exemplo, se a operação U
representa o operador unitário $ U$, então Adjoint U representa $ U^ \dagger$. Para obter mais informações,
consulte aplicativo Functor.

Ancilla
Um qubit que serve como memória temporária para um computador quântico e é alocado e desalocado
conforme necessário. Para obter mais informações, consulte Qubits múltiplos.

Estado de sino
Um dos quatro estados quânticos entrelaçados máximos específicos de dois qubits. Os quatro estados são
definidos $\ket{\beta_{ij}} = (\mathbb{I}\otimes X^iZ^j) (\ket{00} + \ket{11}) / \sqrt{2}$. Um estado de sino
também é conhecido como um par EPR.

Esfera Bloch
Uma representação gráfica de um estado quântico dequbitcomo um ponto em uma esfera de unidade
tridimensional. Para mais informações, consulte Visualizando qubits e transformações usando a esfera Bloch.

Resgatável
Uma operação ou função no Q# idioma. Para mais informações, consulteQ# programas

Grupo Clifford
O conjunto de operações que ocupam os octantes da esfera Bloch e criam efeitos nas permutações dos
operadores Pauli. Isso inclui as operações $ X $ , $ Y $ , $ Z $ , $ H $ e $ S $ .

Controlado
Uma operação quântica que usa um ou mais qubits como habilitadores para a operação de destino. Para mais
informações, consulte aplicativo Functor.

Notação Dirac
Uma abreviação simbólica que simplifica a representação de estados quânticos, também chamada de notação
bra-ket. A porção bra representa um vetor de linha, por exemplo, um $ \bra{A} = \begin{bmatrix} A{_1} & A{_2}
\end{bmatrix}$ e a porção ket representa um vetor de coluna, $ \ket { B } = \begin{bmatrix} B { _1 } \\ B{ _2 }
\end{bmatrix} $. Para mais informações, consulte Notação de Dirac.

Autovalores
O fator pelo qual a magnitude de um autovetor de uma determinada transformação é alterada pelo aplicativo
da transformação. Dada uma matriz quadrada $M $ e um autovetor $v$, então, $Mv=cv$, onde $c$ é o
autovalor e pode ser um número complexo de qualquer argumento. Para mais informações, consulte Conceitos
de matriz avançada.

Autovetor
Um vetor cuja direção é inalterada por uma dada transformação e cuja magnitude é alterada por um fator
correspondente ao autovalordo vetor. Dada uma matriz quadrada$M$ e um autovalor $c$, então, $Mv=vc$,
onde $v$ é um autovetor da matriz e pode ser um número complexo de qualquer argumento. Para mais
informações, consulte Conceitos de matriz avançada.

Emaranhamento
As partículas quânticas, como qubits, podem ser conectadas ou entrelaçadas de modo que não possam ser
descritas independentemente uma da outra. Seus resultados de medição são correlacionados mesmo quando
estão separados infinitamente longe. O entrelaçamento é essencial para medir o estado de um qubit. Para mais
informações, consulte Conceitos de matriz avançada.

Par EPR
Um dos quatro estados quânticos entrelaçados ao máximo específicos de dois qubits. Os quatro estados são
definidos $\ket{\beta_{ij}} = (\mathbb{1} \otimes X^iZ^j) (\ket{00} + \ket{11}) / \sqrt{2}$. Um par EPR também é
conhecido como um estado de sino

Evolução
Como um estado quântico muda ao longo do tempo. Para obter mais informações, consulte Exponenciais da
matriz.

Função
Um tipo de subrotina no Q# idioma que é puramente determinístico. Embora as funções sejam usadas em
algoritmos quânticos, elas não podem agir em operações qubits ou chamada. Para mais informações,
consulteQ# programas

Porta
Um termo herdado para determinadas operaçõesquânticas intrínsecas, com base no conceito de portas lógicas
clássica. Um circuito quântico é uma rede de portas, com base no conceito semelhante de circuitos lógicos
clássicos.

Fase global
Quando dois estados são idênticos até um múltiplo de um número complexo $e ^{i\phi } $, eles são
considerados diferentes para uma fase global. Ao contrário das fases locais, as fases globais não podem ser
observadas por qualquer medida. Para mais informações, consulte O Qubit.

Hadamard
A operação Hadamard (também conhecida como portão ou transformação Hadamard) atua em um único qubit
e a coloca em uma superposição uniforme de $ \ket { 0 } $ ou $ \ket { 1 } $ se o qubit estiver inicialmente no
estado $ \ket {0} $. No Q#, essa operação é aplicada pela operação H predefinida.
Imutável
Uma variável cujo valor não pode ser alterado. Uma variável imutável no Q# é criada usando a let palavra-
chave. Para declarar variáveis que podem ser alteradas, use a palavra-chave mutável para declarar e a set
palavra-chave para modificar o valor.

Medida
Uma manipulação de um qubit (ou conjunto de qubits) que suspende o resultado de uma observação, na
verdade, obtendo um bit clássico. Para mais informações, consulte O Qubit.

Mutável
Uma variável cujo valor pode ser alterado após ser criada. Uma variável mutável no Q# é declarada usando a
mutable palavra-chave e modificada usando a set palavra-chave. As variáveis criadas com a let palavra-
chave são imutáveis e seu valor não pode ser alterado.

Namespace
Um rótulo para uma coleção de nomes relacionados (por exemplo, operações, funçõese tipos definidos pelo
usuário). Por exemplo, o namespace Microsoft. Quantum.Preparation rotula todos os símbolos definidos na
biblioteca padrão que ajudam na preparação de estados iniciais.

Operação
A unidade básica de computação quântica em Q#. É quase equivalente a uma função em C, C++ ou Python, ou a
um método estático em C# ou Java. Para mais informações, consulte Q#programas.

Oracle
Uma subrotina que fornece informações dependentes de dados para um algoritmo quântico em tempo de
execução. Normalmente, o objetivo é fornecer uma superposição de saídas correspondentes às entradas que
estão em superposição. Para mais informações, consulteOracles.

Aplicativo parcial
Chamar uma função ou operação sem todas as entradas necessárias. Isso retorna um novo resgatável que
precisa apenas dos parâmetros ausentes (indicados por um sublinhado) para ser fornecido durante um
aplicativo futuro. Para mais informações, consulte Aplicação parcial.

Operadores Pauli
Um conjunto de três matrizes unitárias 2 x 2 conhecidas como as X , Y e Z operações quânticas. A matriz de
identidade, $ I $, geralmente é incluída no conjunto também. $I = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}$,
$X = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}$, $Y = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix}$, $Z =
\begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}$. Para obter mais informações, consulte Operação de qubit único.

Diagramas de circuito quântico


Um método para representar graficamente a sequência de portas para programas quânticos simples, por
exemplo
.
Para mais informações, consulte Circuitos quânticos.

Bibliotecas quânticas
Coleções de operações, funções e tipos definidos pelo usuário para a criação Q# de programas. A Biblioteca
padrão é instalada por padrão. Outras bibliotecas disponíveis são a Biblioteca de química, a Biblioteca numérica
e a Biblioteca de aprendizado de máquina.

Estado quântico
O estado preciso de um sistema quântico isolado, do qual as probabilidades de medida podem ser extraídas. Na
computação quântica, o simulador quântico usa essas informações para simular como os qubits respondem às
operações. Para mais informações, consulte O Qubit.

O Qubit
Um qubit é uma unidade básica de informações quânticas, análoga a um bit na computação clássica. Para mais
informações, consulte O Qubit.

Repetição até o sucesso


Um conceito geralmente usado em algoritmos quânticos que consiste em aplicar repetidamente uma
computação até que uma determinada condição seja satisfeita. Quando a condição não for satisfeita, geralmente
uma correção é necessária antes de tentar novamente inserindo a próxima iteração. Para mais informações,
consulte o Q# guia do usuário

Bibliotecas padrão
Operações, funções e tipos definidos pelo usuário que são instalados com o Q# compilador durante a
instalação. A implementação padrão da biblioteca é independente com relação aos computadores de destino.
Para mais informações, consulte Bibliotecas padrão.

Superposição
O conceito na computação quântica que um qubit é uma combinação linear de dois estados, $\ket{ 0 }$ e $\ket{
1 }$, até que seja medido. Para obter mais informações, consulte Princípios básicos de computação quântica.

Computador de destino
Um destino de compilação que reduz um programa quântico abstrato em direção a hardware ou simulação. Isso
normalmente inclui regravações para muitas finalidades, incluindo substituição de porta, codificação para
correção de erros, layout geométrico e outros. Para obter mais informações, consulte Simuladores quânticos e
aplicativos de host.

Portabilidade
Um método para regenerar dados ou o estado quânticode um qubit de um lugar para outro sem mover
fisicamente o qubit, usando entrelaçamento e medição. Para mais informações, consulte Circuitos quânticos e o
respectivo kata no Quantum Katas.

Tupla
Uma coleção de valores separados por vírgula que atua como um único valor. O tipo de uma tupla é definido
pelos tipos de valores que ela contém. No Q#, as tuplas são imutáveis e podem ser aninhadas, conter matrizes
ou serem usadas em uma matriz. Para mais informações, consulte Tuplas.

Operador unitário
Um operador cujo inverso é igual a seu adjacente, por exemplo, $ UU^{ \dagger } = \id $.

Tipo definido pelo usuário


Um tipo personalizado que pode conter um ou mais itens nomeados ou anônimos. Para mais informações,
consulte [Type declarations]microsoft.quantum.qsharp.typedeclarations#type-declarations).
Instalar e usar o SDK do Python para otimização
18/05/2021 • 3 minutes to read

Este guia fornece uma visão geral básica de como instalar e usar o SDK do Python para otimização.

Pré-requisitos
Um espaço de trabalho do Azure Quantum criado em sua assinatura do Azure. Para criar um espaço de
trabalho, consulte Criar um espaço de trabalho no Azure Quantum.

Instalação do SDK do Python para otimização


O SDK do Python é distribuído como o pacote azure-quantum PyPI. Para instalar o pacote, você precisará seguir
as etapas abaixo:
1. Instale o Python 3.6 ou mais recente.
2. Instale o Pip, o instalador de pacote do Python e verifique se você tem a versão 19.2 ou superior
3. Instalar o pacote azure-quantum do Python:

pip install azure-quantum

Instalação de Jupyter Notebooks


Você também pode optar por interagir com a otimização do Azure Quantum usando Jupyter Notebooks. Para
isso, você precisará:
1. Instalar o SDK do Python para otimização (conforme descrito na seção anterior)
2. Instalar Jupyter Notebooks
3. No terminal de sua escolha, use o seguinte comando para iniciar um novo Jupyter Notebook:

jupyter notebook

Isso abrirá uma nova janela do navegador (ou uma nova guia) mostrando o Painel do Notebook, um tipo
de painel de controle que permite que você (entre outras coisas) selecione qual bloco de anotações deve
ser aberto.
4. No modo de exibição de navegador, selecione o botão suspenso no canto superior direito e selecione
Python 3 na lista. Isso deve criar um novo notebook.

Exemplo de uso
Se optar por resolver problemas de otimização usando Jupyter Notebooks ou um script Python, depois de
instalar os pré-requisitos das seções anteriores, você poderá seguir as instruções abaixo para executar um
problema de teste.

Conectar-se a um espaço de trabalho do Azure Quantum


Um Workspace representa o espaço de trabalho do Azure Quantum que você criou anteriormente e é a interface
principal para interagir com o serviço.

from typing import List


from azure.quantum.optimization import Term
from azure.quantum import Workspace

workspace = Workspace (
subscription_id = "", # Add your subscription_id
resource_group = "", # Add your resource_group
name = "", # Add your workspace name
location = "" # Add your workspace location (for example, "westus")
)

workspace.login()

Ao executar o comando workspace.login() pela primeira vez, você verá o seguinte exibido no console:

To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code [RANDOM-
CODE] to authenticate.

Depois de entrar em sua conta do Azure, suas credenciais serão armazenadas em cache para que você não
precise repetir esse processo em execuções futuras.

Expressar e resolver um problema simples


Para apresentar um problema simples a ser resolvido, crie uma instância de em Problem e defina problem_type
como ProblemType.ising ou ProblemType.pubo . Para obter mais informações, confira ProblemType .

from azure.quantum.optimization import Problem, ProblemType, Term, ParallelTempering

problem = Problem(name="My First Problem", problem_type=ProblemType.ising)

Em seguida, crie uma matriz de objetos Term e os adicione ao Problem :

terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0]),
Term(c=9, indices=[2,1]),
Term(c=2, indices=[3,0]),
Term(c=-4, indices=[3,1]),
Term(c=4, indices=[3,2])
]

problem.add_terms(terms=terms)

NOTE
Há várias maneiras de fornecer termos para o problema, e nem todos os termos devem ser adicionados ao mesmo
tempo.

Em seguida, estamos prontos para aplicar um solucionador . Neste exemplo, usaremos uma versão sem
parâmetros da deformação paralela. Você pode encontrar a documentação sobre esse solucionador e os outros
solucionadores disponíveis na Referência do provedor QIO da Microsoft.
solver = ParallelTempering(workspace, timeout=100)

result = solver.optimize(problem)
print(result)

Esse método enviará o problema para o Azure Quantum para otimização e esperará que ele seja resolvido de
forma síncrona. Você verá uma saída semelhante à seguinte na janela do seu terminal ou Jupyter Notebook:

{'configuration': {'0': 1, '1': 1, '2': -1, '3': 1}, 'cost': -32.0}

Próximas etapas
Documentação
Visão geral do solucionador
Expressando problemas e fornecendo termos
Interpretando resultados do solucionador
Gerenciamento de trabalho
Solucionar problemas de longa execução (envio de problema assíncrono)
Reutilizar as definições de problema
Autenticação com uma entidade de serviço
Referência de solucionadores para solucionadores de QIO da Microsoft
Exemplos e aprendizagem de ponta a ponta
Repositório de exemplos de QIO
Introdução
1QBit
QIO da Microsoft
Enviar exemplo de problema de carregamento
Módulo do Microsoft Learn de ponta a ponta
Código de exemplo
Problema de exemplo de agendamento da loja de trabalhos
Módulo do Microsoft Learn de ponta a ponta
Código de exemplo
Qual solucionador de otimização devo usar?
01/05/2021 • 2 minutes to read

O Azure Quantum oferece uma ampla gama de solucionadores para problemas de otimização. Você pode
consultar a lista completa na página de referência. No entanto, infelizmente não é possível determinar a priori
qual solucionador atenderá melhor um novo problema de otimização. A seguir, descrevemos nossa estratégia
sugerida para encontrar um solucionador adequado por meio de parâmetro de comparação.

Objetivo do parâmetro de comparação


O objetivo do parâmetro de comparação terá uma grande influência na seleção de um solucionador adequado.
Esse objetivo para os solucionadores é determinado pelo aplicativo. Por exemplo, um objetivo do parâmetro de
comparação é encontrar a solução mais próxima ao mínimo global. Outro objetivo pode ser encontrar a solução
mais próxima ao mínimo global, mas em um determinado intervalo de tempo ou para um runtime especificado.
Se um estiver interessado em resolver muitos problemas de um domínio semelhante, um objetivo do
parâmetro de comparação pode ser encontrar um solucionador que produza bons resultados para a maioria
das instâncias, em vez de retornar resultados muito bons para algumas instâncias, mas não dar uma boa
solução suficiente para as instâncias restantes.

Estratégia do parâmetro de comparação


Problemas de otimização da mesma área podem compartilhar recursos comuns. Portanto, sugerimos que você
comece com o solucionador que funcionava melhor para problemas anteriores do mesmo domínio e use-o
como referência. No entanto, mesmo nesse caso, faz sentido avaliar os outros solucionadores novamente de
tempos em tempos.
Recomendamos que você comece com os solucionadores sem parâmetros, pois eles não exigem ajuste de
parâmetro:
1. SA (recozimento simulado) sem parâmetros: fornece uma referência sólida para o runtime e mínimos
possíveis.
2. A PT (deformação paralela) sem parâmetros
Determinar automaticamente os parâmetros para os solucionadores é conveniente, mas também cria uma
sobrecarga de runtime. Se for necessário resolver muitos problemas semelhantes ou para obter um
desempenho melhor, os solucionadores com parâmetros devem ser considerados. Sugerimos que você comece
com o SA parametrizado se o solucionador do SA sem parâmetros tiver fornecido resultados melhores do que a
PT sem parâmetros, e também recomendamos começar com a PT parametrizada.
Depois, é recomendável fazer o parâmetro de comparação dos solucionadores restantes.
Funções de custo
30/04/2021 • 5 minutes to read

Um problema de otimização é descrito por um conjunto de variáveis, cada um com um conjunto (ou intervalo)
de valores possíveis. Eles descrevem as decisões que o otimizador deve tomar.
Uma solução atribui um valor a cada uma dessas variáveis. Elas descrevem a escolha de cada uma das decisões
mencionadas anteriormente.
A função custo associa um valor numérico ("pontuação") a cada solução possível para compará-los e selecionar
o mais favorável (normalmente identificado pelo valor de custo mais baixo).

NOTE
Na física, a mecânica hamiltoniana usa a função de custo e seu valor de custo é chamado de energia do sistema. Cada
opção de valores de variáveis é chamada de estado, e o estado de baixa energia é o estado fundamental.

Implementação
Em geral, a implementação da função de custo pode ser adiada para uma tabela de referência completa, uma
implementação de caixa preta ou até mesmo uma entrada externa. No entanto, uma abordagem frequente é
defini-la como uma expressão matemática das variáveis e dos parâmetros do problema.
Exemplo : encontre uma fração de inteiros $x$, $y$, que é próxima de $\pi$.
Duas variáveis: $x$, $y$ (inteiros $\in [1..100]$)
Função de custo: $$ \mathrm{cost} = \left| \frac{x}{y} - \pi \right| $$ (queremos que o custo seja mínimo
quando $x/y \approx \pi$).
Soluções possíveis: $[1..100] \times [1..100]$ (opções independentes de $x$, $y$)
Aproximação simples: $$ x=3\text{, }y=1 \quad\Rightarrow\quad \mathrm{cost}=\left|\frac31-
\pi\right|=0,14159 $$
Melhor solução neste intervalo de valores: $$ x=22\text{, }y=7 \quad\Rightarrow\quad \frac{22}{7}\approx
3.14286\text{, }\mathrm{cost}\approx 0.00126 $$

NOTE
Não é necessário que a solução ideal da função de custo tenha um $\mathrm{cost}=0$.

Restrições
Uma restrição é uma relação entre diversas variáveis que devem conter uma solução que deve ser considerada
válida.
Soluções que violam restrições podem ser atribuídas a um custo muito alto ("penalidade") pela função de custo
ou ser excluídas da amostragem explicitamente pelo otimizador.
Exemplo : no problema acima de encontrar uma fração perto de $\pi$, a multiplicação de$x$ e $y$ com o
mesmo número produz uma solução igualmente ideal (por exemplo, $44/14$). Podemos evitar isso
adicionando um termo de penalidade a frações não simplificadas:
$$ \mathrm{cost} = \left| \frac{x}{y} - \pi \right| + \underbrace{100(\gcd(x,y)-1)}_{\mathrm{penalty}}, $$
em que$\gcd(x,y)$ é o maior divisor comum de $x$ e $y$, de modo que o termo entre parênteses desaparece
em frações simplificadas. Com essa adição, a solução ideal, $22/7$, é exclusiva.

NOTE
As restrições em variáveis individuais normalmente são incorporadas em seu respectivo conjunto de valores permitidos
em vez de uma restrição.

Modelos com parâmetros


Problemas típicos de otimização consistem em muitas variáveis e vários termos que constituem a função de
custo. Portanto, é pertinente selecionar uma estrutura específica para a expressão matemática, enquanto denota
meramente os parâmetros e locais variáveis necessários para criar a função de custo.
Exemplo : divida um conjunto de $N$ números em dois grupos de soma igual.
Parâmetros de entrada: $w_0..w_{N-1}$ os números no conjunto
$N$ Variáveis: $x_0..x_{N-1}$ indica se o $i $-ésimo número está no primeiro ($x_i=+1$) ou segundo grupo
($x_i=-1$).
Função de custo de modelo: $$ \mathrm{cost} = \left| \sum_i w_i x_i \right| $$
Ou seja, sempre criamos uma função de custo do formulário no terceiro marcador, mas ajustamos os
parâmetros $w_i$ de acordo com a instância de problema específica que estamos resolvendo.
Por exemplo, os números $[18, 19, 36, 84, 163, 165, 243]$ resultarão na função de custo
$$ \mathrm{cost} = \left| 18x_0 + 19x_1 + 36x_2 + 84x_3 + 163x_4 + 165x_5 + 243x_6 \right|$$

NOTE
Essa instância tem apenas duas soluções com $\mathrm{cost}=0$ (uma espelhando a outra). Você consegue encontrá-
las?

Modelos com suporte


Os modelos implementados em nossos otimizadores incluem o modelo Isinge problemas de otimização binária
quadrática/polinomial irrestrita. Eles dão suporte a aplicativos versáteis porque vários outros problemas de
otimização podem ser mapeados para eles.
Exemplo : para o número acima, defina o problema de divisão. O usuário pode substituir o valor absoluto pelo
operador quadrado (que também tem seu menor valor em 0) para obter:
$$ \mathrm{cost}' = \left(\sum_i w_ix_i\right)^2 = \sum_{ij} w_iw_jx_ix_j\text{ .} $$
Quando multiplicada, essa função de custo tem mais termos do que a anterior, mas ela está na forma
(polinomial) compatível com nossos otimizadores (ou seja, uma função de custo Ising).
Função de custo Ising
As variáveis Ising usam os valores $x_i\in\{\pm1\}$, e a função de custo Ising parametrizada tem o formato:
$$ \mathrm{cost} = \sum_k \mathrm{term}_k = \sum_k c_k\prod_i x_i\text{ .} $$
Os parâmetros c e as ids das variáveis $x_i$ participando em cada termo $k$ estão listadas como parte da
entrada:
Por exemplo, a entrada:

"cost_function": {
"type": "ising",
"version": "2.0",
"terms": [
{ "c": 3, "ids": [0, 1, 2] },
{ "c": -2, "ids": [0, 3] },
{ "c": 1, "ids": [2, 3] }
]
}

descreve a função de custo de Ising com três termos: $$ \mathrm{cost} = 3x_0x_1x_2 -2x_0x_3 + x_2x_3\text{ .}
$$

NOTE
Uma matriz ids vazia (termo constante) ou com um único valor ("campo local") é permitida, mas a mesma variável id
não pode ser repetida dentro de um termo.

Cau t i on

A definição da função de custo Ising difere do modelo Ising canônico hamiltoniano $ \mathcal{H}=-
\sum_{ij}J_{ij}\sigma_i\sigma_j $ normalmente empregado na mecânica estatística (por um sinal global). Como
resultado, as constantes de termo negativo $c_k$ resultam na interação ferromagnética entre duas variáveis.
Função de custo de PUBO
Para problemas de otimização binária, as variáveis usam os valores $x_i\in\{0,1\}$, e a função de custo tem o
formato:
$$ \mathrm{cost} = \sum_k \mathrm{term}_k = \sum_k c_k\prod_i x_i\text{ .} $$
Por exemplo, a entrada:

"cost_function" {
"type": "pubo",
"version": "2.0",
"terms": [
{ "c": 3, "ids": [0, 1, 2] },
{ "c": -2, "ids": [0, 3] },
{ "c": 1, "ids": [2, 3] }
]
}

descreve uma função de custo PUBO com três termos: $$ \mathrm{cost} = 3x_0x_1x_2 -2x_0x_3 + x_2x_3\text{ .}
$$
Embora a forma da função de custo seja idêntica ao caso Ising, esta descreve um problema de otimização
diferente (o conjunto de valores de variáveis permitidos é diferente: $\{0,1\}$ vs $\{\pm1\}$).

NOTE
PUBO e QUBO são tratados pela mesma função de custo. Não há identificador separado "qubo" . Os problemas de
otimização binária quadrática são um caso especial de um PUBO em que cada matriz ids tem comprimento máximo de
2.
Modelo de Ising
13/05/2021 • 2 minutes to read

Na mecânica estatística, o modelo Ising é uma representação simplificada da interação entre momentos
magnéticos individuais em uma substância ferromagnética (por exemplo, um ímã de geladeira). Esses
momentos, chamados de spins, presume-se que apontem "para cima" ($+1$) ou "para baixo" ($-1$) e interajam
com outros spins dependendo de sua posição relativa.

O comportamento desse material magnético depende da temperatura:


1. Em alta temperatura, as excitações térmicas fazem com que os spins mudem rapidamente a orientação e há
pouca organização no sistema.
2. À medida que a temperatura diminui, os estados de energia baixa são favorecidos. Eles são alcançados
quando os spins vizinhos entram em acordo, causando a formação de pequenos domínios alinhados.
3. Quando esse alinhamento aumenta e toma uma parte substancial do sistema, os momentos individuais se
somam a um campo magnético geral.

<a name="ising-hamiltonian">Ising hamiltoniano


A energia de um sistema Ising é descrita pelo hamiltoniano
$$ \mathcal{H} = -\sum_{\langle i,j\rangle} J \sigma_i\sigma_j, $$
em que $\sigma_i \in \{\pm1\}$ são as variáveis do spin, a soma são os pares de spins que interagem
("vizinhos") e o $J>0$ é uma constante de interação (que descreve a intensidade com que os spins vizinhos
interagem). Para cada par de spins alinhados, obtemos uma contribuição de $-J$ para a energia geral.

NOTE
O modelo Ising não tem desordem na interação. Em baixa temperatura, todos os spins têm uma tendência de se
alinharem com seus respectivos vizinhos, e o estado de energia baixa ("estado base") é alcançado quando todas as
variáveis do spin têm o mesmo valor (por exemplo, todas são $+1$ ou todas são $-1$).

Dinâmica do sistema
A probabilidade de encontrar o sistema mecânico-estatístico em um estado específico é fornecida pela
distribuição de Boltzmann. Isso depende da energia desse estado (em comparação com a de todos os outros
estados possíveis) e a temperatura:
$$ p(\vec{\sigma}) = \frac{e^{-\mathcal{H}(\vec\sigma)/k_\mathrm{B}T}}{\sum_{\vec{s}}e^{-\mathcal{H}
(\vec{s})/k_\mathrm{B}T}} \propto e^{-\mathcal{H}(\vec\sigma)/k_\mathrm{B}T},$$
em que a soma da normalização no denominador é sobre todos os estados ("função de partição"),
$k_\mathrm{B}$ é a constante de Boltzmann (normalmente simplificada para $k_\mathrm{B}=1$ em modelos
teóricos) e $T$ é a temperatura atual (geralmente expressa como a temperatura inversa $\beta = 1/T$).

NOTE
Para fins de otimização, os principais detalhes a serem observados é que para $T\to 0$ ($\beta\to\infty$), o estado de
energia mais baixo domina a soma, e a chance de encontrar o sistema nesse estado base tende a
$p(\vec\sigma_{\mathrm{GS}})\to 1$.

Sistemas Ising desordenados


Embora o modelo de Ising (ferromagnético) ofereça uma dinâmica interessante, o sistema se tornará mais
complexo se deixarmos que as constantes de interação $J$ sejam dependentes de acoplamento (por exemplo,
usamos uma constante diferente para cada conjunto de spins de interação).
$$ \mathcal{H} = -\sum_{\langle i,j\rangle} J_{ij} \sigma_i\sigma_j. $$
Para esses assim chamados Vidros de Spin Ising,
Um termo com $J_{ij}<0$ representa a interação antiferromagnética: Spins que favorecem o
antialinhamento (alternando os valores de spin $+1$ e $-1$).
Uma seleção de $J_{ij}$ com sinais diferentes (e possivelmente magnitudes), juntamente com o grafo de
interação, pode levar a interações concorrentes: uma situação em que nenhuma atribuição de spin-
variável satisfaz todos os vínculos (isso é conhecido como frustração).
Ao encontrar o estado base, a escolha de como atribuir variáveis com interações concorrentes pode ter
um efeito cascata em todo o sistema (alterando a forma como os spins adjacentes devem ser
organizados, e por sua vez, afetando seus respectivos vizinhos).
Como resultado, encontrar o estado base é muito mais complicado nesses sistemas complexos.
Otimização binária
01/05/2021 • 3 minutes to read

A otimização binária é uma subclasse de problemas de otimização combinais mais gerais em que as variáveis
são restritas a um conjunto finito de valores, neste caso em particular apenas duas. Portanto, um problema de
otimização binária pode ser declarado como o esforço de minimizar uma função objetiva $H (\vec{x}) $ de $N $
variables com valores $x _i na \{0,1\}$ sujeito a algumas restrições de igualdade e/ou desigualdade. Essas
restrições restringem os valores que as variáveis podem executar, a fim de garantir que apenas soluções viáveis
sejam propostas. Alguns exemplos de restrições $f (\vec{x}) $ e $g (\vec{x}) $ estão incluídos abaixo.
$ $ \ min_ {\vec{x}}\left \ {H (\vec{x}) \quad | \quad \vec{x}\in \ {0, 1 \ } ^ N\right \ }, $ $ $ $f (\vec{x}) = 0, \ \ g
(\vec{x}) > 0. $ $
A otimização binária constitui uma ampla gama de problemas importantes de natureza científica e industrial,
como análise de rede social, otimização de portfólio em finanças, gerenciamento de tráfego e agendamento em
transporte, otimização de leads na descoberta de medicamentos farmacêutica e muito mais.

Otimização binária não restringida polinomial (PUBO)


Os PUBOs são um subconjunto de problemas de otimização binária sem restrições em que a função objetivo é
uma polinomial das variáveis. O grau $k$ de um polinomial é geralmente chamado de localidade do PUBO. Por
exemplo, a função objetiva de um PUBO $-local $k pode ser expressa da seguinte maneira:
$$H(\vec{x})= \sum_{i_1}^N J_{i_1}x_{i_1} + \frac{1}{2!}\sum_{i_1,i_2}^NJ_{i_1i_2}x_{i_1}x_{i_2}+\cdots +\frac{1}
{k!}\sum_{i_1,\ldots i_k}^NJ_{i_1i_2\ldots i_k}x_{i_1}\ldots x_{i_k},$$
em que $J _ {i_1i_2 \ldots i_n} $ representa as interações $n $-point entre as variáveis $x _ {i_1}, x_ {i_2}, \ldots,
x_ {i_n} $. Podemos interpretar as variáveis como (on) os nós e suas interações como as bordas de um grafo
subjacente. Observe que cada um dos $J _ {i_1i_2 \ldots i_n} $ é um tensor simétrico, já que qualquer
permutação dos índices deixará o termo correspondente na função objetiva acima inalterado, o motivo é que tal
permutação é equivalente a uma embaralhando dos valores $x _i $. Em termos com um número par de
variáveis, os termos diagonais podem ser ignorados devido ao fato de que $x _i ^ 2 = 1 $.

Otimização binária não restringida quadrática (QUBO)


Os QUBOs são casos especiais de PUBOs em que a ordem do polinomial é de $2 $, ou seja,
$$ \min_{\vec{x}}\left\{H(\vec{x})=\sum_{i}^N h_ix_i +\frac{1}{2}\sum_{i,j}^N J_{ij}x_i x_j\quad|\quad \vec{x}\in\
{-1,1\}^N\right\}. $$
Se os elementos da matriz de adjacência $J _ {IJ} $ forem desenhados a partir de uma distribuição aleatória, a
função objetiva acima será equivalente ao Hamiltonian do chamado vidro de rotação. Os vidros de rotação são
bem conhecidos na física para seu panorama de energia resistente, com muitos mínimo locais
exponencialmente, o que faz com que a localização do estado fundamental seja um problema de NP-rígido.
Muitos problemas de otimização não triviais como corte máximo, fluxos de rede, satisfação, embalagem
geométrica, cores de grafo e programação linear de inteiros podem ser formulados como QUBOs, embora, em
alguns casos, isso possa aumentar a dureza do problema.
Exemplo de um problema de QUBO
Aqui, mostramos o mapeamento QUBO para o famoso Problema do Caixeiro Viajante (TSP).
O TSP é um problema simples, mas importante, com aplicativos em transporte. Ele consiste em tentar encontrar
o caminho fechado mais curto entre um conjunto de $N$ sites $ \mathcal{S} $, de modo que cada site seja
visitado exatamente uma vez, exceto para o site inicial. Em outras palavras, o caminho não deve cruzar a si
mesmo. Supondo que a distância entre os sites $\alpha $ e $\beta$ seja $w _ {\alpha\beta}$, a função de
objetivo QUBO pode ser expressa da seguinte maneira:
$ $ H (\vec{x}) = \ sum_ {\alpha, \beta\in\mathcal{S}} \ sum_ {i = 1} ^ N w_ {\alpha\beta} x_ {\alpha, i} x_ {\beta, i
+ 1} + \lambda\ sum_ {i = 1} ^ N\left (1-\ sum_ {\alpha\in\mathcal{S}} x_ {\alpha, i} \right) ^ 2 + \lambda\ sum_
{\alpha\in\mathcal{S}}\left (1-\ sum_ {i = 1} ^ N x_ {\alpha, i} \right) ^ 2,$ $
em que a variável binária $x _ {\alpha,i} $ é igual a $1$ se o local $\alpha$ aparecer $i$th no caminho, caso
contrário, será $0$. O primeiro termo na expressão acima é simplesmente o comprimento do caminho,
enquanto os dois últimos termos impõem os requisitos que cada site em $\mathcal{S}$ aparece no caminho e
nenhum site é visitado mais de uma vez, respectivamente. Observe que o coeficiente de penalidade $\lambda $
deve ser um número positivo grande para que qualquer desvio formado pelas restrições seja muito suprimido.
Como contribuir com o QDK (Quantum
Development Kit)
13/05/2021 • 3 minutes to read

O Quantum development kit é mais do que uma coleção de ferramentas para a composição de programas
quânticos. Faz parte de uma ampla comunidade de pessoas que estão descobrindo a computação quântica,
realizando pesquisas em algoritmos quânticos, desenvolvendo novas aplicações para dispositivos quânticos e
que, de outro modo, estão trabalhando para aproveitar ao máximo a programação quântica. Como membro
dessa comunidade, o Quantum development kit visa oferecer aos desenvolvedores quânticos uma ampla gama
de contextos com os recursos necessários. Suas contribuições para o Quantum development kit ajudam a
concretizar essa meta, aprimorando as ferramentas usadas por outros desenvolvedores quânticos, a forma
como essas ferramentas são documentadas e, até mesmo, criando recursos e funcionalidades que ajudam a
fazer toda a comunidade de programação quântica um lugar melhor para descobertas e criações. Somos muito
gratos por suas generosas contribuições e pela oportunidade de trabalhar com você, visando tornar nossa
comunidade a melhor possível.
Neste guia, fornecemos algumas orientações sobre como tornar sua contribuição a mais útil possível para a
comunidade mais ampla de programação quântica.

Construção da comunidade
O primeiro aspecto relacionado a fazer uma contribuição é sempre ter em mente a comunidade com a qual
você está contribuindo. Agindo de maneira respeitosa e profissional com seus colegas na comunidade de
programação quântica e mais amplamente, você pode ajudar a garantir que seus esforços construam a melhor
e a mais acolhedora comunidade possíveis.
Como parte desse esforço, todos os projetos do Quantum development kit adotaram o Código de Conduta de
Software Livre da Microsoft. Para obter mais informações, confira as Perguntas frequentes sobre o Código de
Conduta ou entre em contato pelo email opencode@microsoft.com para enviar outras perguntas ou
comentários.

Que tipos de contribuições ajudam a comunidade?


Há várias maneiras diferentes de ajudar a comunidade de programação quântica por meio de suas
contribuições. Neste guia, nós nos concentraremos em três maneiras que são especialmente relevantes para o
Quantum development kit. Todas essas maneiras são críticas para a criação de uma comunidade quântica que
capacita as pessoas. Dito isso, essa definitivamente não é uma lista completa – incentivamos você a explorar
outras maneiras de ajudar a comunidade a crescer com a promessa da programação quântica.
Como relatar bugs. A primeira etapa para corrigir bugs e outros tipos de problemas é identificá-los. Se
você encontrou um bug no Quantum development kit, passar essa informação para nossa equipe nos ajuda
a corrigi-lo e a criar um conjunto melhor de ferramentas para a comunidade de programação quântica.
Como aprimorar a documentação. Qualquer conjunto de documentação sempre pode ficar melhor,
abranger mais detalhes, ficar mais acessível.
Como contribuir com um código. É claro que uma das maneiras mais diretas de fazer uma contribuição
é adicionar um novo código ao Quantum development kit.
Esses diferentes tipos de contribuições são todos extremamente valiosos e muito apreciados. No restante do
guia, ofereceremos orientações sobre como fazer cada tipo de contribuição.
Para onde vão as contribuições?
O Quantum development kit inclui várias partes diferentes que trabalham juntas para concretizar uma
plataforma para a composição de programas quânticos. Cada uma dessas diferentes partes encontra sua
própria casa em um repositório diferente, portanto, um dos primeiros aspectos a serem definidos é a que local
cada contribuição melhor pertence.
microsoft/Quantum : Amostras e ferramentas para ajudar a começar a usar o Quantum development kit.
microsoft/QuantumLibraries : Bibliotecas padrão e específicas de domínio para o Quantum development
kit.
microsoft/QuantumKatas : Exercícios individuais de programação para aprender computação quântica e a
linguagem de programação Q#.
microsoft/qsharp-compiler : O compilador Q#, a extensão do Visual Studio e a extensão do Visual Studio
Code.
microsoft/qsharp-runtime : Estrutura de simulação, geração de código e computadores de destino de
simulação para o Quantum development kit.
microsoft/iqsharp : Recurso de host Python e kernel do Jupyter para Q#, além de imagens do Docker para
usar o IQ# em ambientes de nuvem.
microsoft/qsharp-language : esse é o repositório no qual novos recursos do Q# são desenvolvidos e
especificados, e é nele que você pode compartilhar ideias e sugestões sobre a evolução futura da linguagem
Q# e das bibliotecas principais.
MicrosoftDocs/quantum-docs : código-fonte para a documentação publicada em docs.mcrosoft.com.

NOTE
No momento, não podemos aceitar contribuições de código e documentação no repositório microsoft/Quantum-NC ,
mas ainda apreciamos receber relatórios de bug.

Há também alguns outros repositórios mais especializados que se concentram na funcionalidade auxiliar
relacionada ao Quantum Development Kit.
msr-quarc/qsharp.sty : Compatível com a formatação do LaTeX para a sintaxe Q#.

Próximas etapas
Obrigado por fazer parte da comunidade do Quantum development kit. Aguardaremos ansiosamente suas
contribuições! Caso deseje saber mais sobre como fazer uma contribuição, prossiga para um dos guias a seguir.
Saiba como relatar bugs
Saiba como contribuir com uma documentação
Saiba como abrir solicitações de pull
Relatar bugs
19/05/2021 • 2 minutes to read

Se você acredita que encontrou um bug em um componente do Quantum development kit, gostaríamos muito
de receber um relatório. Afinal, outra pessoa também pode estar encontrando com o mesmo problema. Se você
nos informar, poderemos corrigi-lo para todos. A primeira etapa para relatar um bug é começar examinando os
problemas existentes encontrados em cada repositório, pois é muito possível que outras pessoas tenham
relatado o mesmo problema.
Se o problema já tiver sido encontrado antes, poderá ser útil fornecer informações ou contexto adicionais que
podem nos ajudar a diagnosticar e resolver o problema. Nesse caso, fique à vontade para participar de
discussões sobre o problema.
Se o seu bug não tiver sido relatado antes, gostaríamos que você abrisse um problema no repositório para o
componente em questão. Ao criar um problema, você será avisado com um modelo que sugere algumas
informações que geralmente são úteis para criar relatórios de bugs informativos:
Etapas para reproduzir o problema
O que você esperava que acontecesse
O que realmente aconteceu
Informações sobre seu ambiente de software (por exemplo: versões do Quantum development kit, do SO e
do .NET Core)
Você também pode criar uma solicitação de pull para corrigir o bug diretamente caso ele seja muito simples e
não valha a discussão (por exemplo, um erro de digitação).
Como aprimorar a documentação
19/05/2021 • 9 minutes to read

A documentação do Quantum development kit assume várias formas diferentes, de modo que essas
informações estão prontamente disponíveis para os desenvolvedores quantum.
Seguindo os princípios de docs como código, toda a documentação do Quantum development kit é formatada
como código e é gerenciada usando o Git da mesma forma que o código-fonte usado para criar o Quantum
development kit. Na maioria das vezes, a documentação de suporte de código consiste em várias formas de
Markdown, uma linguagem para escrever texto formatado de maneira sofisticada em um formato de texto sem
formatação fácil de usar na linha de comando, em IDEs e com controle do código-fonte. De modo semelhante,
adotamos a biblioteca MathJax para permitir a formatação de matemática na documentação usando a
linguagem LaTeX, conforme detalhado abaixo.
Dito isso, cada forma de documentação varia um pouco nos detalhes:
A documentação conceitual consiste em um conjunto de artigos que são publicados em
docs.microsoft.com e que descrevem tudo, desde os conceitos básicos da computação quântica até as
especificações técnicas para formatos de intercâmbio. Esses artigos são escritos em DFM (DocFX-Flavored
Markdown), uma variante de Markdown usada para criar conjuntos de documentação avançados.
A referência de API é um conjunto de páginas para cada função, operação e tipo Q# definido pelo usuário,
publicado em https://docs.microsoft.com/qsharp/api/. Essas páginas documentam as entradas e operações
em cada chamável, juntamente com exemplos e links para obter mais informações. A referência de API é
extraída automaticamente de pequenos documentos do DFM no código-fonte Q# como parte de cada
versão.
Os arquivos README .md incluídos em cada exemplo e kata descrevem como esse exemplo ou kata é
usado, o que ele abrange e como ele se relaciona com o restante do Quantum development kit. Esses
arquivos são escritos usando a GFM (GitHub Flavored Markdown), uma alternativa mais leve em relação ao
DFM que é popular para anexar documentação diretamente a repositórios de código.

Contribuir para a documentação conceitual


Para contribuir para um aprimoramento na documentação conceitual ou README, comece com uma solicitação
de pull no MicrosoftDocs/quantum-docs , Microsoft/Quantum ou Microsoft/QuantumKatas , conforme
apropriado. Descreveremos mais sobre as solicitações pull abaixo, mas, por enquanto, é bom lembrar de alguns
pontos ao aprimorar a documentação:
Os leitores vêm à documentação do Quantum development kit de uma grande variedade de contextos.
Todos, de alunos de ensino médio que buscam aprender algo novo e empolgante até o corpo docente que
realiza a pesquisa de computação quântica, devem ser capazes de extrair algo útil da leitura da
documentação. Na medida do possível, não presuma um amplo conhecimento por parte de seus leitores,
pois eles podem estar apenas começando. Será mais útil se você puder fornecer descrições claras e
acessíveis ou links para outros recursos para obter mais informações.
Os conjuntos de documentação não são dispostos como livros ou artigos, nesse sentido, os leitores chegarão
a eles no que pode parecer "a metade". Por exemplo, os mecanismos de pesquisa podem não sugerir o índice
ou podem ter recebido um link por um amigo tentando ajudá-lo. Tente ajudar seu leitor sempre dando um
contexto claro, além de links, quando apropriado.
Alguns leitores considerarão instruções e definições abstratas mais úteis, enquanto outros leitores trabalham
melhor extrapolando de exemplos concretos. Fornecer os exemplos de caso geral e específicos pode ajudar
os leitores a obter o máximo da programação quantum.
Especialmente se você também escreveu o código que está sendo documentado, algumas coisas podem ser
óbvias para você, mas não para o seu leitor. Não existe uma forma melhor de programar, portanto,
independentemente de quão inteligente ou experiente um leitor possa ser, ele não pode adivinhar quais
padrões de design você considerou mais úteis para expressar suas ideias no código. Ser claro sobre como
um leitor pode esperar fazer uso do seu código pode ajudar a fornecer esse contexto.
Muitos membros da comunidade de programação quantum são pesquisadores acadêmicos e são
reconhecidos principalmente por meio de citações pelas suas contribuições para a comunidade. Além de
ajudar os leitores a encontrar materiais adicionais, citar adequadamente trabalhos acadêmicos, como artigos,
palestras, postagens em blog e ferramentas de software, pode ajudar os colaboradores acadêmicos a
continuar a fazer o melhor trabalho para aprimorar a comunidade.
A comunidade de programação quantum é uma ampla e maravilhosamente diversificada. O uso de
pronomes com gênero em exemplos de terceira pessoa (por exemplo: "se um usuário..., eles serão...") pode
agir para excluir, em vez de incluir. Conhecer os nomes de pessoas em citações e links e realizar a inclusão
correta de caracteres não ASCII pode beneficiar a diversidade da comunidade, demonstrando respeito a seus
membros. Da mesma forma, muitas palavras no idioma inglês são frequentemente usadas em discurso de
ódio, de modo que seu uso na documentação técnica pode causar danos tanto aos leitores individuais
quanto à comunidade em geral.
Como fazer referência a código de exemplo de artigos conceituais
Se você quiser incluir código do repositório de exemplos, poderá fazer isso usando um comando especial de
DocFX-Flavored Markdown:

:::code language="qsharp" source="~/quantum/samples/algorithms/chsh-game/Game.qs" range="4-8":::

Esse comando importará as linhas 4 a 8 do arquivo Game.qs do exemplo chsh-game , marcando-as como código
Q# para fins de realce de sintaxe. Usando esse comando, você pode evitar duplicar o código entre artigos
conceituais e o repositório de exemplos, de modo que o código de exemplo na documentação sempre seja o
mais atualizado possível.
Arquivos de imagem colaboradores
IMPORTANTE : para que as imagens sejam renderizadas corretamente no modo escuro, você deve evitar
transparências.
Para arquivos .jpg. Você não precisa fazer nada, pois o formato .jpg não dá suporte a elementos
transparentes.
Para arquivos .png, você deve adicionar uma tela de fundo branca ou alterar o valor do canal alfa para 100 . A
maneira mais fácil de fazer isso no Windows é abrir o arquivo no Paint e salvá-lo, substituindo o arquivo
original.
Para arquivos .svg, você deve adicionar um retângulo branco na camada mais baixa. Você pode fazer isso
com o Inkcape :
1. Abra o arquivo .svg.
2. Selecione a ferramenta do quadrado e desenhe um retângulo branco em cima da figura original.
3. Selecione a ferramenta Selecionar e transformar objetos clicando na seta escura ou pressionando
F1 .
4. Enquanto estiver com o retângulo selecionado, clique no elemento da barra de ferramentas Seleção
inferior até a base (fim) .
5. Ajuste o retângulo com o mouse ou as teclas de direção.

Como contribuir para as referências de API


Para contribuir para o aprimoramento nas referências de API, é mais útil abrir uma solicitação de pull
diretamente no código que está sendo documentado. Cada função, operação ou tipo definido pelo usuário dá
suporte a um comentário de documentação (indicado com /// , em vez de // ). Quando compilamos cada
versão do Quantum development kit, esses comentários são usados para gerar a referência de API em
https://docs.microsoft.com/qsharp/api/, incluindo detalhes sobre as entradas e saídas de cada chamável, as
suposições que cada chamável faz e exemplos de como usá-los.

IMPORTANT
Não edite manualmente a documentação da API gerada, pois esses arquivos são substituídos a cada nova versão.
Valorizamos sua contribuição para a comunidade e queremos garantir que suas alterações continuem a ajudar os usuários
versão após versão.

Por exemplo, considere a função


ControlledOnBitString<'T> (bits : Bool[], oracle : ('T => Unit is Adj + Ctl)) : ((Qubit[], 'T) => Unit is Adj
+ Ctl)
. Um comentário de documentação deve ajudar um usuário a aprender a interpretar bits e oracle e para que
a função serve. Cada uma dessas diferentes partes de informações pode ser fornecida ao compilador Q# por
uma seção de Markdown especialmente nomeada no comentário da documentação. Para o exemplo
ControlledOnBitString , podemos escrever algo semelhante ao seguinte:
/// # Summary
/// Returns a unitary operation that applies an oracle on the target register if the
/// control register state corresponds to a specified bit mask.
///
/// # Description
/// The output of this function is an operation that can be represented by a
/// unitary transformation $U$ such that
/// \begin{align}
/// U \ket{b_0 b_1 \cdots b_{n - 1}} \ket{\psi} = \ket{b_0 b_1 \cdots b_{n-1}} \otimes
/// \begin{cases}
/// V \ket{\psi} & \textrm{if} (b_0 b_1 \cdots b_{n - 1}) = \texttt{bits} \\\\
/// \ket{\psi} & \textrm{otherwise}
/// \end{cases},
/// \end{align}
/// where $V$ is a unitary transformation that represents the action of the
/// `oracle` operation.
///
/// # Input
/// ## bits
/// The bit string to control the given unitary operation on.
/// ## oracle
/// The unitary operation to be applied on the target register.
///
/// # Output
/// A unitary operation that applies `oracle` on the target register if the control
/// register state corresponds to the bit mask `bits`.
///
/// # Remarks
/// The length of `bits` and `controlRegister` must be equal.
///
/// Given a Boolean array `bits` and a unitary operation `oracle`, the output of this function
/// is an operation that performs the following steps:
/// * apply an `X` operation to each qubit of the control register that corresponds to `false`
/// element of the `bits`;
/// * apply `Controlled oracle` to the control and target registers;
/// * apply an `X` operation to each qubit of the control register that corresponds to `false`
/// element of the `bits` again to return the control register to the original state.
///
/// The output of the `Controlled` functor is a special case of `ControlledOnBitString` where `bits` is
equal to `[true, ..., true]`.
///
/// # Example
/// The following code snippets are equivalent:
/// ```qsharp
/// ControlledOnBitString(bits, oracle)(controlRegister, targetRegister);
/// ```
/// and
/// ```qsharp
/// within {
/// ApplyPauliFromBitString(PauliX, false, bits, controlRegister);
/// } apply {
/// Controlled oracle(controlRegister, targetRegister);
/// }
/// ```
///
/// The following code prepares a state $\frac{1}{2}(\ket{00} - \ket{01} + \ket{10} + \ket{11})$:
/// ```qsharp
/// use register = Qubit[2];
/// ApplyToEach(H, register);
/// ControlledOnBitString([false], Z)(register[0..0], register[1]);
/// ```
function ControlledOnBitString<'T> (bits : Bool[], oracle : ('T => Unit is Adj + Ctl)) : ((Qubit[], 'T) =>
Unit is Adj + Ctl)
{
return ControlledOnBitStringImpl(bits, oracle, _, _);
}
Você pode ver a versão renderizada do código acima na documentação da API para a função
ControlledOnBitString .

Além da prática geral da escrita da documentação, escrever comentários sobre a documentação da API ajuda a
ressaltar alguns pontos:
O formato de cada comentário de documentação deve corresponder ao que o compilador Q# espera para
que sua documentação apareça corretamente. Algumas seções, como /// # Remarks , permitem conteúdo
em forma livre, enquanto seções como /// # See Also são mais restritivas.
Um leitor pode ler a documentação da API no site de referência da API principal, no resumo de cada
namespace ou mesmo de dentro de seu IDE usando informações em foco. Garantir que /// # Summary não
seja maior do que uma frase ajuda seu leitor a definir com rapidez se ele precisa ler mais ou procurar em
outro lugar e pode ajudar a examinar rapidamente os resumos de namespace.
Sua documentação pode acabar sendo muito mais longa do que o próprio código, mas tudo bem. Mesmo
uma pequena parte do código pode ter efeitos inesperados para os usuários que não conhecem o contexto
no qual o código existe. Ao fornecer exemplos concretos e explicações claras, você pode ajudar os usuários a
usar o código que está disponível para eles da melhor forma possível.
Abrir solicitações de pull
19/05/2021 • 3 minutes to read

Toda a documentação do Quantum development kit é gerenciada usando o sistema de controle de versão do Git
usando vários repositórios hospedados no GitHub. O uso do Git e do GitHub juntos torna mais fácil colaborar
amplamente no Quantum development kit. Em particular, qualquer repositório Git pode ser clonado ou
bifurcado para fazer uma cópia completamente independente desse repositório. Isso permite que você trabalhe
em sua contribuição com as ferramentas e no ritmo que preferir.
Quando estiver pronto, você poderá nos enviar uma solicitação para incluir sua contribuição em nosso
repositório, usando a funcionalidade de solicitação de pull do GitHub. A página de cada solicitação de pull inclui
detalhes de todas as alterações que fazem sua contribuição, uma lista de comentários sobre sua contribuição e
um conjunto de ferramentas de revisão que outros membros da comunidade podem usar para fornecer
comentários e conselhos.

NOTE
Embora um tutorial completo sobre o Git esteja além do escopo deste guia, podemos sugerir os seguintes links para
obter mais recursos no aprendizado do Git:
Saiba mais sobre o Git: um conjunto de tutoriais do Git da Atlassian.
Controle de versão no Visual Studio Code: um guia sobre como usar o Visual Studio Code como uma GUI para Git.
Laboratório de aprendizado do GitHub: um conjunto de cursos online para usar Git, GitHub e tecnologias relacionadas.
Visualizar o Git: uma ferramenta interativa para visualizar como os comandos do Git alteram o estado de um
repositório.
Controle de versão com o Git (EPQIS 2016): um tutorial do Git voltado para a computação científica.
Saiba mais sobre a ramificação do Git: um conjunto interativo de quebra-cabeças para refazer a base para ajudar você
a aprender novos recursos do Git.

O que é uma solicitação de pull?


Dito isso, é útil levar alguns minutos para dizer o que é uma solicitação de pull. Ao trabalhar com o Git, todas as
alterações são representadas como commits que descrevem como essas alterações estão relacionadas ao
estado do repositório antes dessas alterações. Em geral, desenhamos diagramas nos quais as confirmações são
desenhadas como círculos com setas de confirmações anteriores.
Suponha que você tenha iniciado uma contribuição em uma ramificação chamada feature . Em seguida, sua
bifurcação do Microsoft/Quantum pode ser semelhante a esta:
Se você fizer as alterações em seu repositório local, poderá efetuar pull de alterações de outro repositório em
seu para obter as alterações que ocorreram upstream.

As solicitações de pull funcionam da mesma forma, mas na ordem inversa: quando você abre uma solicitação de
pull, solicita que o repositório upstream efetue o pull da sua contribuição.

Quando você abre uma solicitação de pull para um de nossos repositórios, o GitHub dará a oportunidade para
que outras pessoas na comunidade vejam um resumo das suas alterações para comentar sobre elas e dar
sugestões de como ajudar a fazer uma contribuição ainda melhor.
O uso desse processo nos ajuda a usar a funcionalidade do GitHub para aprimorar as contribuições e manter
um produto de alta qualidade para a comunidade de programação do Quantum.

Como fazer uma solicitação de pull


Há duas maneiras principais de fazer uma solicitação de pull.
Para pequenas alterações que afetam apenas um arquivo, a interface da Web do GitHub pode ser usada para
fazer uma solicitação pull totalmente online. Basta navegar até o arquivo que você deseja editar e usar o ícone
Editar.
Para contribuições mais complicadas, costuma ser mais fácil clonar o repositório em seu computador local para
se preparar primeiro para a solicitação de pull primeiro.

Próximas etapas
Parabéns por usar o Git para ajudar a comunidade do Quantum development kit. Para saber mais sobre como
contribuir com código, continue com o guia a seguir.
Aprenda a contribuir com código
Código de contribuição
15/05/2021 • 5 minutes to read

Além de relatar problemas e aprimorar a documentação, contribuir com um código para o Quantum
Development Kit pode ser uma maneira muito direta de ajudar colegas na comunidade de programação
quântica. Ao contribuir com um código, você pode ajudar a corrigir problemas, fornecer novos exemplos, tornar
as bibliotecas existentes mais fáceis de usar ou até mesmo adicionar recursos totalmente novos.
Neste guia, detalharemos um pouco do que procuramos quando revisamos as solicitações de pull para fazer
com que a sua contribuição ajude o máximo possível.

O que procuramos
Uma contribuição de código ideal se baseia no trabalho existente em um repositório do Quantum Development
Kit para corrigir problemas, expandir os recursos existentes ou adicionar novos recursos dentro do escopo de
um repositório. Quando aceitamos uma contribuição de código, ele se torna parte do próprio Quantum
Development Kit, de modo que novos recursos serão lançados, mantidos e desenvolvidos da mesma maneira
que o restante do Quantum Development Kit. Portanto, quando uma funcionalidade é adicionada por uma
contribuição é importante que ela seja testada e documentada.
Testes de Unidade
As funções, as operações e os tipos definidos pelo usuário do Q# que compõem bibliotecas como o cânone são
testados automaticamente como parte do desenvolvimento no repositório Microsoft/QuantumLibraries .
Quando uma nova solicitação de pull for aberta, por exemplo, nossa configuração do Azure Pipelines verificará
se as alterações na solicitação de pull não interrompem nenhuma funcionalidade existente da qual a
comunidade de programação quântica dependa.
Com a versão mais recente de Q#, os testes de unidade são definidos usando o atributo
@Test("QuantumSimulator") . O argumento pode ser "QuantumSimulator", "ToffoliSimulator", "TraceSimulator" ou
qualquer nome totalmente qualificado que especifica o destino de execução. Vários atributos que definem
destinos de execução diferentes podem ser anexados ao mesmo chamador. Alguns de nossos testes ainda usam
o pacote Microsoft.Quantum.Xunit preterido que expõe todas as funções e operações de Q# que terminam em
Test para a estrutura xUnit. Este pacote não é mais necessário para definir testes de unidade.

A função a seguir é usada para garantir que as funções Fst function e Snd function retornam as saídas corretas
em um exemplo representativo. Se a saída de Fst ou Snd estiver incorreta, a instrução fail será usada para
fazer com que o teste falhe.

@Test("QuantumSimulator")
function PairTest () : Unit {
let pair = (12, PauliZ);

if (Fst(pair) != 12) {
let actual = Fst(pair);
fail $"Expected 12, actual {actual}.";
}

if (Snd(pair) != PauliZ) {
let actual = Snd(pair);
fail $"Expected PauliZ, actual {actual}.";
}
}
Condições mais complicadas podem ser verificadas usando as técnicas na seção de teste do guia de bibliotecas
padrão. Por exemplo, o teste a seguir verifica se H(q); X(q); H(q); chamado por ApplyWith operation faz o
mesmo que Z(q) .

@Test("QuantumSimulator")
operation TestApplyWith() : Unit {
let actual = ApplyWith(H, X, _);
let expected = Z;
AssertOperationsEqualReferenced(ApplyToEach(actual, _), ApplyToEachA(expected, _), 4);
}

Ao adicionar novos recursos, é recomendável também adicionar novos testes para garantir que a contribuição
faça o que deveria fazer. Isso ajuda o restante da comunidade a manter e desenvolver seu recurso, além de
ajudar outros desenvolvedores a saber que eles podem contar com esse recurso.

NOTE
Isso também funciona da forma contrária! Se um recurso existente não tiver passado por alguns testes, sua ajuda para
adicionar cobertura de teste seria uma grande contribuição para a comunidade.

Localmente, os testes de unidade podem ser executados usando o Visual Studio Test Explorer ou o comando
dotnet test , para que você possa verificar a contribuição antes de abrir uma solicitação de pull.

Solicitações de pull
Quando estiver pronto para contribuir com seu trabalho, envie uma solicitação de pull por meio do GitHub para
o repositório correspondente. A equipe revisará e fornecerá comentários. Todos os comentários precisam ser
respondidos e resolvidos e todas as verificações precisam ser aprovadas antes que o código seja mesclado ao
branch main .

Quando rejeitamos uma solicitação de pull


Às vezes, rejeitaremos a solicitação de pull para uma contribuição. Se isso acontecer com você, não significa que
ela é inadequada, pois há vários motivos para não aceitarmos uma contribuição específica. O mais comum é
que a contribuição para a comunidade de programação quântica é realmente boa, mas os repositórios do
Quantum Development Kit não são o lugar certo para desenvolvê-la. Nesses casos, recomendamos que você
crie seu próprio repositório – um dos pontos fortes do Quantum Development Kit é que é fácil fazer e distribuir
suas próprias bibliotecas usando o GitHub e o NuGet.org, da mesma forma que distribuímos as bibliotecas do
cânone e de química atualmente.
Em outras ocasiões, podemos rejeitar uma boa contribuição simplesmente porque ainda não estamos prontos
para mantê-la e desenvolvê-la. Pode ser difícil fazer tudo, então planejamos quais recursos podemos trabalhar
da melhor forma como um roteiro. Isso pode ser outro caso em que a liberação de um recurso como uma
biblioteca de terceiros pode fazer sentido. Por outro lado, podemos pedir ajuda para modificar um recurso para
se ajustar melhor ao nosso roteiro, para que possamos fazer o melhor trabalho possível.
Também solicitaremos alterações a uma solicitação de pull se precisar de mais testes de unidade ou de
documentação para nos ajudar a usá-lo ou se ele tiver um estilo diferente do restante das bibliotecas de Q# que
dificulte para os usuários encontrarem o recurso. Nesses casos, ofereceremos conselhos de revisões de código
sobre o que pode ser adicionado ou alterado para facilitar a inclusão da sua contribuição.
Por fim, não aceitaremos contribuições que prejudique a comunidade de computação quântica, conforme
descrito no Código de Conduta de Software Livre da Microsoft. Queremos garantir que as contribuições
atendam a toda a comunidade de computação quântica, tanto em sua atual diversidade maravilhosa quanto no
futuro, à medida que ela cresce para se tornar ainda mais inclusiva. Agradecemos pela ajuda em concretizar
essa meta.

Próximas etapas
Obrigado por ajudar a tornar o Quantum Development Kit um excelente recurso para toda a comunidade de
programação quântica! Para saber mais, acesse o guia sobre o estilo de Q# a seguir.
Saiba mais sobre as diretrizes do estilo de Q#
Dependendo do tipo de código na sua contribuição, você pode considerar algumas coisas que podem ajudá-lo a
fazer com que a contribuição traga o máximo possível de benefícios para a comunidade.
Saiba mais sobre amostras de contribuição
Como contribuir com o QDK (Quantum
development kit)
19/05/2021 • 3 minutes to read

Se você estiver interessado em contribuir com código para o repositório de exemplos, agradecemos por tornar
a comunidade de desenvolvimento do Quantum um lugar melhor!

O repositório de exemplos do Quantum development kit


Para ajudá-lo a preparar sua contribuição para ajudar o máximo possível, é útil dar uma olhada rápida em como
o repositório de exemplos é apresentado:

microsoft/Quantum
samples/
algorithms/
chsh-game/
CHSHGame.csproj
Game.qs
Host.cs
host.py
README.md

arithmetic/
characterization/
chemistry/

Ou seja, os exemplos no repositório Microsoft/Quantum são divididos por área de assunto em pastas diferentes,
como algorithms/ , arithmetic/ ou characterization/ . Dentro da pasta de cada área de assunto, cada amostra
consiste em uma só pasta que coleta tudo de que um usuário precisará para explorar e usar esse exemplo.

Como os exemplos são estruturados


Examinando os arquivos que compõem cada pasta, vamos nos aprofundar no exemplo algorithms/chsh-game/ .

A RQ UIVO DESC RIÇ Ã O

CHSHGame.csproj Projeto Q# usado para criar o exemplo com o SDK do .NET


Core

Game.qs Operações e funções Q# para o exemplo

Host.cs Programa de host C# usado para executar o exemplo

host.py Programa de host Python usado para executar o exemplo

README.md Documentação sobre o que o exemplo faz e como usá-lo

Nem todos os exemplos terão exatamente o mesmo conjunto de arquivos (por exemplo, alguns exemplos
podem ser somente em C#, outros podem não ter um host Python ou alguns exemplos podem exigir que
arquivos de dados auxiliar funcionem).
Anatomia de um arquivo README útil
No entanto, um arquivo especialmente importante é o README.md , pois é dele que os usuários precisam para
começar a usar o seu exemplo.
Cada README.md deve começar com alguns metadados que ajudam a docs.microsoft.com/samples a encontrar
sua contribuição.
Veja como o exemplo chsh-game é renderizado
Esses metadados são fornecidos como um cabeçalho YAML que indica em quais idiomas sua amostra abrange
(normalmente, isso será qsharp , csharp e python ) e quais produtos sua amostra cobre (normalmente, apenas
qdk ).

---
page_type: sample
languages:
- qsharp
- python
- csharp
products:
- qdk
description: "This sample uses the CHSH game to demonstrate how Q# programs can be used to prepare and
work with entanglement."
urlFragment: validating-quantum-mechanics
---

IMPORTANT
A chave page_type: sample no cabeçalho é necessária para que seu exemplo apareça em docs.microsoft.com/samples.
Da mesma forma, as chaves product e language são críticas para ajudar os usuários a localizar e executar seu
exemplo.

Depois disso, é útil dar uma breve introdução que diz o que a sua nova amostra faz:

# Validating Quantum Mechanics with the CHSH Game

This sample demonstrates:


- How to prepare entangled states with Q#.
- How to measure part of an entangled register.
- Using Q# to understand superposition and entanglement.

In this sample, you can use Q# to prepare qubits in an entangled state, and to check that measuring
these qubits lets you win a game known as the _CHSH game_ more often than you can without entanglement.
This game helps us understand entanglement, and has even been used experimentally to help test that the
universe really is quantum mechanical in nature.

Os usuários do seu exemplo também apreciarão saber do que precisam para executá-lo (por exemplo, os
usuários precisam apenas do próprio Quantum development kit ou precisam de software adicional, como
Node.js?):

## Prerequisites

- The Microsoft [Quantum Development Kit](https://docs.microsoft.com/quantum/install-guide/).

Com tudo em vigor, você pode dizer aos usuários como executar seu exemplo:
## Running the Sample

This sample can be run in a number of different ways, depending on your preferred environment.

### Python in Visual Studio Code or the Command Line

At a terminal, run the following command:

```powershell
python host.py
```

### C# in Visual Studio Code or the Command Line

At a terminal, run the following command:

```powershell
dotnet run
```

### C# in Visual Studio 2019

Open the folder containing this sample in Visual Studio ("Open a local folder"
from the Getting Started screen or "File → Open → Folder..." from the menu bar)
and set `CHSHGame.csproj` as the startup project.
Press Start in Visual Studio to run the sample.

Por fim, é útil informar aos usuários o que cada arquivo do seu exemplo faz e em que local eles podem obter
mais informações:

## Manifest

- [Game.qs](https://github.com/microsoft/Quantum/blob/main/samples/algorithms/chsh-game/Game.qs): Q# code
implementing the game.
- [host.py](https://github.com/microsoft/Quantum/blob/main/samples/algorithms/chsh-game/host.py): Python
host program to call into the Q# sample.
- [Host.cs](https://github.com/microsoft/Quantum/blob/main/samples/algorithms/chsh-game/Host.cs): C# code to
call the operations defined in Q#.
- [CHSHGame.csproj](https://github.com/microsoft/Quantum/blob/main/samples/algorithms/chsh-
game/CHSHGame.csproj): Main C# project for the sample.

## Further resources

- [Measurement concepts](https://docs.microsoft.com/quantum/concepts/pauli-measurements)

WARNING
Use as URLs absolutas aqui, pois seu exemplo será exibido em uma URL diferente quando renderizado em
docs.microsoft.com/samples.
Guia de estilo do Q#
11/05/2021 • 20 minutes to read

Convenções gerais
As convenções sugeridas neste guia destinam-se a facilitar a leitura e o entendimento dos programas e
bibliotecas escritos na linguagem de programação Q#.

Diretrizes
Nossa sugestão:
Nunca desconsidere uma convenção, a menos que a intenção seja fornecer códigos mais legíveis e
compreensíveis para os usuários.

Convenções de nomenclatura
Ao oferecer o Quantum Development Kit, buscamos nomes de função e operação que ajudam os
desenvolvedores de Quantum a escrever programas que sejam fáceis de ler e que minimizam imprevistos. Uma
parte importante disso é que, quando escolhemos nomes para funções, operações e tipos, estamos
estabelecendo o vocabulário que os programadores usarão para expressar conceitos de quântico; com nossas
escolhas, nós os ajudamos ou atrapalhamos seus esforços para uma comunicação clara. Isso faz com que seja
de nossa responsabilidade garantir que os nomes ofereçam clareza, em vez de ambiguidade. Nesta seção,
detalhamos como atendemos a essa obrigação em termos de diretrizes explícitas que nos ajudam a fazer o
melhor para a comunidade de desenvolvimento de Q#.
Operações e funções
Uma das primeiras coisas que um nome deve estabelecer é se um determinado símbolo representa uma função
ou uma operação. A diferença entre funções e operações é essencial para entender o comportamento de um
bloco de código. Para comunicar a distinção entre as funções e as operações para os usuários, nós usamos
como base que Q# modela operações quânticas por meio do uso de efeitos colaterais. Ou seja, uma operação
faz algo.
Por outro lado, as funções descrevem as relações matemáticas entre os dados. A expressão Sin(PI() / 2.0) é
1.0 e não há implicação sobre o estado de um programa ou seu qubits.

Resumindo, as operações fazem coisas enquanto as funções são coisas. Essa distinção sugere que as operações
sejam verbos e as funções sejam substantivos.

NOTE
Quando um tipo definido pelo usuário é declarado, uma nova função que constrói instâncias desse tipo é definida
implicitamente ao mesmo tempo. Dessa perspectiva, os tipos definidos pelo usuário devem ser nomeados como
substantivos para que o próprio tipo e a função de construtor tenham nomes consistentes.

Quando for razoável, verifique se os nomes de operação começam com verbos que indicam claramente o efeito
executado pela operação. Por exemplo:
MeasureInteger
EstimateEnergy
SampleInt

Um caso que merece menção especial é quando uma operação usa outra operação como entrada e faz a
chamada. Nesses casos, a ação executada pela operação de entrada não é clara quando a operação externa é
definida, pois o verbo certo não está imediatamente claro. Recomendamos o verbo Apply , como em ApplyIf ,
ApplyToEach e ApplyToFirst . Outros verbos também podem ser úteis nesse caso, como em
IterateThroughCartesianPower .

VERB O EF EITO ESP ERA DO

Aplicar Uma operação fornecida como entrada é chamada

Assert Uma hipótese sobre o resultado de uma possível medição


quântica é verificada por um simulador

Estimativa Um valor clássico é retornado, representando uma


estimativa extraída de uma ou mais medidas

Medida Uma medida quântica é executada e o resultado é retornado


para o usuário

Preparar Um determinado registro de qubits é inicializado em um


estado específico

Amostra Um valor clássico é retornado aleatoriamente a partir de


alguma distribuição

Para funções, sugerimos evitar o uso de verbos em favor de substantivos comuns (confira as diretrizes abaixo
sobre nomes próprios) ou adjetivos:
ConstantArray
Head
LookupFunction

Em particular, em quase todos os casos e quando apropriado, sugerimos usar particípio passado para indicar
que um nome de função está fortemente vinculado a uma ação ou um efeito colateral em outro lugar em um
programa quântico. Por exemplo, ControlledOnInt usa a forma do particípio passado do verbo "control" para
indicar que a função atua como um adjetivo para modificar seu argumento. Esse nome tem o benefício adicional
de corresponder à semântica do functor Controlled interno, conforme discutido abaixo. Da mesma forma, os
substantivos do agente podem ser usados para construir nomes de função e UDT a partir de nomes de
operação, como no caso do nome Encoder para uma UDT que esteja fortemente associada a Encode .

Diretrizes
Exemplos

Nossa sugestão:
Use verbos para nomes de operação.
Use substantivos ou adjetivos para nomes de função.
Use substantivos para atributos e tipos definidos pelo usuário.
Para todos os nomes que podem ser chamados, use preferencialmente CamelCase em vez de pascalCase ,
snake_case ou ANGRY_CASE . Em particular, confirme que os nomes que podem ser chamados começam com
letras maiúsculas.
Para todas as variáveis locais, use preferencialmente pascalCase em vez de CamelCase , snake_case ou
ANGRY_CASE . Em particular, confirme que as variáveis locais começam com letras minúsculas.
Evite o uso de sublinhados _ em nomes de função e de operação. Quando níveis adicionais de hierarquia
são necessários, use namespaces e aliases de namespace.
Pontos de entrada
Ao definir um ponto de entrada em um programa Q#, o compilador Q# reconhece o @EntryPoint() atributo,
exigindo que os pontos de entrada tenham um nome específico (por exemplo: main , Main ou __main__ ). Ou
seja, da perspectiva de um desenvolvedor de Q#, os pontos de entrada são operações comuns anotadas com
@EntryPoint() . Além disso, os pontos de entrada de Q# podem ser pontos de entrada para um aplicativo inteiro
(por exemplo, em programas executáveis autônomos de Q#) ou podem ser uma interface entre um programa
Q# e o programa host para um aplicativo (por exemplo, ao usar Q# com Python ou .NET), de modo que o nome
"main" pode ser enganoso quando aplicado a um ponto de entrada de Q#.
Sugerimos que a nomenclatura de pontos de entrada seja usada para refletir a função do atributo
@EntryPoint() usando as orientações gerais para a nomenclatura de operações listadas acima.

Diretrizes
Exemplos

Nossa sugestão:
Não nomeie operações de ponto de entrada como "main".
Nomeie operações de ponto de entrada como operações comuns.
Abreviações
Apesar das orientações acima, há muitas formas de abreviação de uso comum e generalizado na computação
quântica. Sugerimos usar a abreviação existente e comum quando ela existe, especialmente para operações
intrínsecas à operação de um computador de destino. Por exemplo, escolhemos o nome X em vez de ApplyX e
Rz em vez de RotateAboutZ . Ao usar esse formato abreviado, os nomes de operação devem estar em letras
maiúsculas (por exemplo, MAJ ).
É necessário atenção ao aplicar essa convenção no caso de acrônimos comumente usados e inicialismos como
"QFT" para "transformação de Fourier quântico". Sugerimos as seguintes convenções gerais do .NET para o uso
de acrônimos e inicialismos em nomes completos, que prescrevem que:
acrônimos e inicialismos de duas letras são nomeados em letras maiúsculas (por exemplo, BE para "big-
endian"),
todos os acrônimos e inicialismos mais longos são nomeados em CamelCase (por exemplo, Qft para
"transformação de Fourier quântico")
Assim, uma operação que implementa a QFT poderia ser chamada com a abreviação QFT ou escrita como
ApplyQft .

Para operações e funções comumente usadas, é recomendável fornecer um nome abreviado como um alias
para um formato mais longo:

operation CCNOT(control0 : Qubit, control1 : Qubit, target : Qubit)


is Adj + Ctl {
Controlled X([control0, control1], target);
}

Diretrizes
Exemplos

Nossa sugestão:
Considere os nomes abreviados comumente aceitos e amplamente usados quando apropriado.
Use maiúsculas para abreviar.
Use maiúsculas para acrônimos e inicialismos curtos (duas letras).
Use CamelCase para acrônimos e inicialismos mais longos (três ou mais letras).
Substantivos próprios em nomes
Embora na física seja comum nomear coisas em homenagem à primeira pessoa que publicar sobre elas, a
maioria das pessoas fora da física não está familiarizada com os nomes e a história. Basear-se muito nas
convenções de nomenclatura da física pode acabar criando uma barreira substancial para a entrada, pois os
usuários de outras áreas precisam aprender diversos nomes aparentemente sem sentido para usar operações e
conceitos comuns.
Portanto, é recomendável que, sempre que possível, substantivos comuns que descrevem um conceito sejam
adotados em preferência a nomes próprios que descrevem a história da publicação de um conceito. Como um
exemplo específico, a troca controlada individualmente e as operações não controladas duplamente são
chamadas de operações "Fredkin" e "Toffoli" na literatura acadêmica, mas são identificadas em Q#
principalmente como CSWAP e CCNOT . Nos dois casos, os comentários de documentação da API fornecem
nomes sinônimos com base em nomes próprios, junto com todas as citações apropriadas.
Essa preferência é especialmente importante, uma vez que o uso de nomes próprios sempre será necessário —
Q# segue a tradição definida por muitas linguagens clássicas, por exemplo, e refere-se a tipos Bool em
referência à lógica booliana, que, por sua vez, é nomeada em homenagem a George Boole. Alguns conceitos de
quântico são nomeados de maneira semelhante, incluindo o tipo interno Pauli para a linguagem Q#. Ao
minimizar o uso de nomes próprios quando não for essencial, reduzimos o impacto nos casos em que os nomes
próprios não podem ser evitados.

Diretrizes
Exemplos

Nossa sugestão:
Evite o uso de substantivos próprios em nomes.
Conversões de tipo
Como Q# é uma linguagem fortemente tipada de forma estática, um valor de um tipo só pode ser usado como
um valor de outro tipo usando uma chamada explícita para uma função de conversão de tipo. Isso é diferente de
linguagens que permitem que valores alterem tipos implicitamente (por exemplo, promoção de tipos) ou por
meio de conversão. Como resultado, as funções de conversão de tipo têm um papel importante no
desenvolvimento da biblioteca de Q# e compreendem uma das decisões mais comumente encontradas sobre a
nomenclatura. No entanto, observamos que, como as conversões de tipo são sempre determinísticas, elas
podem ser escritas como funções e, portanto, se enquadram nas orientações acima. Em particular, sugerimos
que as funções de conversão de tipo não sejam nomeadas como verbos (por exemplo, ConvertToX ) ou locuções
prepositivas adverbiais ( ToX ), mas devem ser nomeadas como locuções prepositivas adjetivas que indicam os
tipos de origem e de destino ( XAsY ). Ao listar tipos de matriz em nomes de função de conversão de tipo,
recomendamos a abreviatura Arr . Ao bloquear circunstâncias excepcionais, recomendamos que todas as
funções de conversão de tipo sejam nomeadas usando As para que possam ser identificadas rapidamente.

Diretrizes
Exemplos

Nossa sugestão:
Se uma função converter um valor do tipo X em um valor do tipo Y , use o nome AsY ou XAsY .
Nomes particulares ou internos
Em muitos casos, um nome destina-se estritamente ao uso interno de uma biblioteca ou um projeto e não é
uma parte garantida da API oferecida por uma biblioteca. É importante indicar claramente que esse é o caso ao
nomear funções e operações para que dependências acidentais em código somente interno sejam óbvias. Se
uma operação ou função não for destinada ao uso direto, mas precisar ser usada por um chamador
correspondente que age por aplicativo parcial, considere usar um nome que comece com a palavra-chave
internal para o chamador parcialmente aplicado.

Diretrizes
Exemplos

Nossa sugestão:
Quando uma função, uma operação ou um tipo definido pelo usuário não fizer parte da API pública para
uma biblioteca ou um programa Q#, verifique se está marcado(a) como interno(a) colocando a palavra-
chave internal antes da declaração function , operation ou newtype .
Variantes
Embora essa limitação possa não persistir em versões futuras do Q#, atualmente é o caso em que haverá
grupos de operações ou funções relacionadas que são diferenciados pelos functors permitidos pelas entradas
ou pelos tipos concretos dos argumentos. Esses grupos podem ser diferenciados usando o mesmo nome de
raiz, seguidos por uma ou duas letras que indicam a variante.

SUF IXO SIGN IF IC A DO

A Entrada esperada para dar suporte a Adjoint

C Entrada esperada para dar suporte a Controlled

CA Entrada esperada para dar suporte a Controlled e


Adjoint

I Entrada ou entradas são do tipo Int

D Entrada ou entradas são do tipo Double

L Entrada ou entradas são do tipo BigInt

Diretrizes
Exemplos

Nossa sugestão:
Se uma função ou operação não estiver relacionada a funções ou operações semelhantes por tipos e suporte
de functor de suas entradas, não use um sufixo.
Se uma função ou operação estiver relacionada a funções ou operações semelhantes pelos tipos e suporte
de functor de suas entradas, use sufixos para distinguir variantes, como mostrado na tabela acima.
Argumentos e variáveis
Uma das principais metas do código Q# para uma função ou operação é que ela seja facilmente lida e
compreendida. Da mesma forma, os nomes de entradas e argumentos de tipo devem comunicar como uma
função ou um argumento será usado quando fornecido.
Diretrizes
Exemplos
Nossa sugestão:
Para todos os nomes de variáveis e de entrada, use preferencialmente pascalCase em vez de CamelCase ,
snake_case ou ANGRY_CASE .
Os nomes de entrada devem ser descritivos; evite nomes de uma ou duas letras sempre que possível.
Operações e funções que aceitam exatamente um argumento de tipo devem indicar esse argumento de tipo
por T quando a função for óbvia.
Se uma função ou operação usar vários argumentos de tipo, ou se a função de um argumento de tipo único
não for óbvia, use uma palavra curta em maiúscula precedida por T (por exemplo, TOutput ) para cada tipo.
Não inclua nomes de tipos em nomes de argumentos e variáveis. Essas informações podem e devem ser
fornecidas pelo ambiente de desenvolvimento.
Indique tipos escalares por seus nomes literais ( flagQubit ) e tipos de matriz por um plural ( measResults ).
Para matrizes de qubits em particular, considere a possibilidade de indicar esses tipos por Register , em que
o nome se refere a uma sequência de qubits fortemente relacionados de alguma maneira.
As variáveis usadas como índices em matrizes devem começar com idx e devem estar no singular (por
exemplo, things[idxThing] ). Evite usar nomes de variáveis de letra única como índices e considere usar pelo
menos idx .
As variáveis usadas para conter comprimentos de matrizes devem começar com n e devem estar no plural
(por exemplo, nThings ).
Itens nomeados em tipo definido pelo usuário
Os itens nomeados em tipos definidos pelo usuário devem ser nomeados como CamelCase , mesmo em entrada
para construtores de UDT. Isso ajuda a separar claramente os itens nomeados de referências a variáveis com
escopo local ao usar a notação de acessador (por exemplo, callable::Apply ) ou a notação de copiar e atualizar (
set arr w/= Data <- newData ).

Diretrizes
Exemplos

Nossa sugestão:
Os itens nomeados nos construtores de UDT devem ser nomeados como CamelCase , ou seja, eles devem
começar com uma letra maiúscula inicial.
Os itens nomeados que resolvem para as operações devem ser nomeados como frases verbais.
Os itens nomeados que não resolvem para as operações devem ser nomeados como frases nominais.
Para UDTs que encapsulam as operações, um único item nomeado chamado Apply deve ser definido.

Convenções de entrada
Quando um desenvolvedor chama uma operação ou função, as várias entradas para essa operação ou função
devem ser especificadas em uma determinada ordem. Isso aumenta a carga cognitiva que um desenvolvedor
enfrenta para fazer uso de uma biblioteca. Em particular, a tarefa de memorizar as ordens de entrada é, muitas
vezes, uma distração da tarefa em questão: programação de uma implementação de um algoritmo quântico.
Embora o suporte avançado para IDE possa reduzir isso de forma considerável, um bom design e a adesão às
convenções comuns também podem ajudar a minimizar a carga cognitiva imposta por uma API.
Sempre que possível, pode ser útil reduzir o número de entradas esperado por uma operação ou função. Assim,
a função de cada entrada será mais óbvia imediatamente para os desenvolvedores que chamam essa operação
ou função e para os desenvolvedores que lerão esse código mais tarde. Principalmente quando não for possível
ou razoável reduzir o número de argumentos para uma operação ou função, é importante ter uma ordenação
consistente que minimize a surpresa de um usuário ao prever a ordem das entradas.
Recomendamos que as convenções de ordenação de entrada derivem amplamente da reflexão do aplicativo
parcial como uma generalização de currying ( , ) ≡ ( )( ). Portanto, a aplicação parcial dos primeiros
argumentos deve resultar em um chamador que seja útil por si só, sempre que for razoável. Seguindo esse
princípio, considere usar esta ordem de argumentos:
Argumentos clássicos que não podem ser chamados, como ângulos, vetores de potências etc.
Argumentos que podem ser chamados (funções e argumentos). Se as funções e as operações forem
executadas como argumentos, considere colocar as operações após as funções.
Coleções executadas por argumentos que podem ser chamados de maneira semelhante a Map , Iter ,
Enumerate e Fold .
Argumentos qubit usados como controles.
Argumentos qubit usados como destinos.
Considere uma operação ApplyPhaseEstimationIteration para uso na estimativa de fase que usa um ângulo e
um oracle, passa o ângulo para Rz modificado por uma matriz de fatores de dimensionamento diferentes e
depois controla os aplicativos da oracle. Faríamos a ordem de entradas para ApplyPhaseEstimationIteration da
seguinte maneira:

operation ApplyPhaseEstimationIteration(
angle : Double,
callable : (Qubit => () is Ctl),
scaleFactors : Double[],
controlQubit : Qubit,
targetQubits : Qubit[]
)
: Unit
...

Como um caso especial para minimizar imprevistos, algumas funções e operações imitam o comportamento
dos functors internos Adjoint e Controlled . Por exemplo, ControlledOnInt<'T> tem tipo
(Int, ('T => Unit is Adj + Ctl)) => ((Qubit[], 'T) => Unit is Adj + Ctl) , de forma que
ControlledOnInt<Qubit[]>(5, _) age como o functor Controlled , mas na condição que o registro de controle
representa o estado $\ket{5} = \ket{101}$. Portanto, um desenvolvedor espera que as entradas para
ControlledOnInt coloquem o chamador que está sendo transformado por último e que a operação resultante
execute (Qubit[], 'T) como a entrada – a mesma ordem, conforme seguida pela saída do functor Controlled .

Diretrizes
Exemplos

Nossa sugestão:
Use ordenações de entrada consistentes com o uso de aplicativo parcial.
Use ordenações de entrada consistentes com functors internos.
Coloque todas as entradas clássicas antes de qualquer entrada quântica.

Convenções de documentação
A linguagem Q# permite anexar documentação a operações, funções e tipos definidos pelo usuário por meio do
uso de comentários de documentação especialmente formatados. Indicados por barras triplas ( /// ), esses
comentários de documentação são documentos DocFX Flavored Markdown pequenos, que podem ser usados
para descrever a finalidade de cada operação, função e tipo definido pelo usuário, qual entrada cada um espera
e assim por diante. O compilador fornecido com o Quantum Development Kit extrai esses comentários e os usa
para ajudar a compor a documentação semelhante àquela em docs.microsoft.com. Da mesma forma, o servidor
de linguagem fornecido com o Quantum Development Kit usa esses comentários para ajudar os usuários
quando eles passam o mouse sobre símbolos no código Q#. Usar os comentários de documentação pode
ajudar os usuários a entender o código ao fornecer uma referência útil para os detalhes que não são facilmente
expressos usando as outras convenções deste documento.
Referência de sintaxe no comentário de documentação.
Para usar efetivamente essa funcionalidade para ajudar os usuários, considere algumas coisas ao escrever
comentários de documentação.

Diretrizes
Exemplos

Nossa sugestão:
Cada função pública, operação e tipo definido pelo usuário deve ser imediatamente precedido por um
comentário de documentação.
Cada comentário de documentação deve incluir, pelo menos, as seguintes seções:
Resumo
Entrada
Saída (se aplicável)
Verifique se os resumos têm duas frases ou menos. Se mais espaço for necessário, forneça uma seção
# Description imediatamente após # Summary com detalhes completos.
Quando for razoável, não inclua matemática em resumos, pois nem todos os clientes têm suporte à notação
TeX em resumos. Observe que, ao escrever documentos de texto (por exemplo, TeX ou Markdown), pode ser
preferível usar linhas mais longas.
Forneça todas as expressões matemáticas relevantes na seção # Description .
Ao descrever as entradas, não repita os tipos de cada entrada, pois elas podem ser inferidas pelo compilador
e podem introduzir inconsistências.
Forneça exemplos conforme apropriado, cada um em sua própria seção # Example .
Descreva brevemente cada exemplo antes de listar o código.
Cite todas as publicações acadêmicas relevantes (por exemplo: artigos, procedimentos, postagens no blog e
implementações alternativas) em uma seção # References como uma lista com marcadores de links.
Verifique se, quando possível, todos os links de citação são de identificadores permanentes e imutáveis (DOIs
ou números de arXiv com versão).
Quando uma operação ou função estiver relacionada a outras operações ou funções por variantes functor,
liste outras variantes como marcadores na seção # See Also .
Deixe uma linha de comentário em branco entre as seções de nível 1 ( /// # ), mas não deixe uma linha em
branco entre as seções de nível 2 ( /// ## ).

Convenções de formatação
Além das sugestões anteriores, usar regras de formatação consistentes ajuda a deixar o código o mais legível
possível. Essas regras de formatação tendem a ser, por natureza, arbitrárias e fortemente baseadas em estética
pessoal. No entanto, é recomendável manter um conjunto consistente de convenções de formatação dentro de
um grupo de colaboradores, especialmente para projetos grandes de Q#, como o próprio Quantum
Development Kit. Essas regras podem ser aplicadas automaticamente usando a ferramenta de formatação
integrada com o compilador Q#.

Diretrizes
Exemplos

Nossa sugestão:
Use quatro espaços em vez de tabulação para portabilidade. Por exemplo, no VS Code:
"editor.insertSpaces": true,
"editor.tabSize": 4

Quebre a linha após 79 caracteres, quando for razoável.


Use espaços ao redor de operadores binários.
Use espaços em qualquer lado dos dois-pontos usados para anotações de tipo.
Use um único espaço após as vírgulas usadas em literais de matriz e de tupla (por exemplo, em entradas
para funções e operações).
Não use espaços após a função, a operação ou os nomes de UDT ou depois de @ em declarações de
atributo.
Cada declaração de atributo deve estar em uma linha própria.
Q# Princípios de design da API
19/05/2021 • 15 minutes to read

Introdução
Como uma linguagem e como uma plataforma, o Q# permite que os usuários escrevam, executem,
compreendam e explorem os aplicativos quantum. Para capacitar os usuários, quando criamos bibliotecas Q#,
seguimos um conjunto de princípios de design de API para guiar nossos designs e nos ajudar a criar bibliotecas
utilizáveis para a comunidade de desenvolvimento quantum. Este artigo lista esses princípios e apresenta
exemplos para ajudar você a aplicá-los na criação de APIs Q#.

TIP
Este é um documento bastante detalhado destinado a ajudar a orientar o desenvolvimento da biblioteca e as
contribuições detalhadas da biblioteca. Você provavelmente vai considerar mais útil se estiver escrevendo suas bibliotecas
em Q# ou se estiver contribuindo com recursos maiores para o repositório de bibliotecas Q#.
Por outro lado, se você estiver procurando aprender a contribuir com o Quantum development kit de um modo mais
geral, sugerimos começar com o guia de contribuição. Se você estiver procurando informações mais gerais sobre como
recomendamos formatar seu código Q#, talvez esteja interessado em verificar o guia de estilo.

Noções básicas gerais


Princípio chave: expor APIs que colocam o foco em aplicativos quantum.
✅ ESCOLHA os nomes de função e operação que refletem a estrutura de alto nível de algoritmos e
aplicativos.
️ NÃO exponha APIs que se concentram principalmente em detalhes de implementação de nível baixo.

Princípio chave: inicie cada design de API com casos de uso de exemplo para garantir que o uso das APIs seja
intuitivo.
✅ VERIFIQUE se cada componente de uma API pública tem um caso de uso correspondente, em vez de
tentar projetar para todos os usos possíveis desde o início. Em outras palavras, não introduza APIs
públicas caso elas sejam úteis, mas verifique se cada parte de uma API tenha um exemplo concreto em
que ela será útil.
Exemplos:
@"microsoft.quantum.canon.applytoeachca" pode ser usado como ApplyToEachCA(H, _) para
preparar os registros em um estado de sobreposição uniforme, uma tarefa comum em muitos
algoritmos quantum. A mesma operação também pode ser usada para muitas outras tarefas nos
algoritmos de preparação, numéricos e baseados em oráculo.
✅ FAÇA um brainstorm e um workshop de novos designs de API para verificar se são intuitivos e
atendem aos casos de uso propostos.
Exemplos:
Inspecione o código Q# atual para ver como os novos designs de API podem simplificar e esclarecer
as implementações existentes.
Examine os designs de API propostos com os representantes de públicos principais.
Princípio chave: crie APIs para dar suporte e incentivar o código legível.
✅ VERIFIQUE se o código é legível por especialistas no domínio e por não especialistas.
✅ COLOQUE o foco nos efeitos de cada operação e função dentro do algoritmo de alto nível usando a
documentação para se aprofundar nos detalhes da implementação conforme apropriado.
✅ SIGA o guia de estilo comum de Q# sempre que aplicável.
Princípio chave: crie APIs para serem estáveis e oferecerem compatibilidade com o futuro.
✅ DESCONTINUE APIs antigas normalmente quando forem necessárias alterações significativas.
✅ FORNEÇA operações e funções de "shim" que permitem que o código de usuário opere
corretamente durante a reprovação.
Exemplos:
Ao renomear uma operação chamada EstimateExpectation como EstimateAverage , introduza uma
operação chamada EstimateExpectation que chame a operação original em seu novo nome de modo
que o código existente possa continuar funcionando de modo correto.
✅ USE o atributo @"microsoft.quantum.core.deprecated" para comunicar reprovações ao usuário.
✅ Ao renomear uma operação ou função, FORNEÇA o novo nome como uma entrada de cadeia de
caracteres para @Deprecated .
⛔ NÃO remova funções ou operações existentes sem um período de substituição de pelo menos seis

meses para versões prévias ou pelo menos dois anos para versões com suporte.

Funções e operações
Princípio chave: verifique se cada função e operação tem uma finalidade bem definida dentro da API.
️ NÃO exponha funções e operações que executem várias tarefas não relacionadas.

Princípio chave: projete funções e operações para serem o mais reutilizáveis possível e prever necessidades
futuras.
✅ FAÇA funções e operações de design para compor bem com outras funções e operações, tanto na
mesma API quanto em bibliotecas já existentes.
Exemplos:
A operação @"microsoft.quantum.canon.delay" faz suposições mínimas sobre sua entrada, portanto,
pode ser usada para atrasar aplicativos de operações em toda a Q# biblioteca padrão ou conforme
definido pelos usuários.
✅ EXPONHA a lógica clássica puramente determinística como funções, em vez de operações.
Exemplos:
Uma sub-rotina que eleva ao quadrado sua entrada de ponto flutuante pode ser escrita de modo
determinístico e, portanto, deve ser exposta ao usuário como Squared : Double -> Double , em vez de
como uma operação Square : Double => Double . Isso permite que a sub-rotina seja chamada em mais
lugares (por exemplo, dentro de outras funções) e fornece informações de otimização úteis para o
compilador que podem afetar o desempenho e as otimizações.
ForEach<'TInput, 'TOutput>('TInput => 'TOutput, 'TInput[]) => 'TOutput[] e
Mapped<'TInput, 'TOutput>('TInput -> 'TOutput, 'TInput[]) -> 'TOutput[] diferem nas garantias feitas
em relação ao determinante. Ambos são úteis em diferentes circunstâncias.
Rotinas de API que transformam o aplicativo de operações quantum geralmente podem ser
executadas de maneira determinística e, portanto, podem ser disponibilizadas como funções como
CControlled<'T>(op : 'T => Unit) => ((Bool, 'T) => Unit) .
✅ GENERALIZE o tipo de entrada tanto quanto razoável para cada função e operação usando
parâmetros de tipo conforme necessário.
Exemplos:
tem o tipo <'T>(('T => Unit), 'T[]) =>
ApplyToEach Unit , em vez do tipo específico de seu aplicativo
mais comum, ((Qubit => Unit), Qubit[]) => Unit .

TIP
É importante prever as necessidades futuras, mas também é importante resolver problemas concretos para seus usuários.
Agir com base nesse princípio fundamental, portanto, sempre requer uma consideração cuidadosa e balanceamento para
evitar o desenvolvimento de APIs "só por precaução".

Princípio chave: escolha os tipos de entrada e saída para funções e operações que são previsíveis e que
comunicam a finalidade de um chamável.
✅ USE tipos de tupla para agrupar logicamente entradas e saídas que apenas têm significado quando
consideradas juntas. Considere o uso de um tipo definido pelo usuário nesses casos.
Exemplos:
Uma função para produzir a mínimo local de outra função pode precisar usar limites de um intervalo
de pesquisa como entrada, de modo que
LocalMinima(fn : (Double -> Double), (left : Double, right : Double)) : Double pode ser uma
assinatura apropriada.
Uma operação para estimar uma derivada de um classificador de machine learning usando a técnica
de mudança de parâmetro pode precisar usar os vetores de parâmetro deslocados e não deslocados
como entradas. Uma entrada semelhante a (unshifted : Double[], shifted : Double[]) pode ser
apropriada nesse caso.
✅ PEÇA itens em tuplas de entrada e saída de modo consistente em diferentes funções e operações.
Exemplos:
Se você estiver considerando duas ou funções ou operações que usem, cada uma, um ângulo de
rotação e um qubit de destino como entrada, elas deverão estar na mesma ordem em cada tupla de
entrada. Ou seja, prefira ApplyRotation(angle : Double, target : Qubit) : Unit is Adj + Ctl e
DelayedRotation(angle : Double, target : Qubit) : (Unit => Unit is Adj + Ctl) a
ApplyRotation(target : Qubit, angle : Double) : Unit is Adj + Ctl e
DelayedRotation(angle : Double, target : Qubit) : (Unit => Unit is Adj + Ctl) .

Princípio chave: projete funções e operações para funcionarem bem com os recursos da linguagem Q#, como
aplicativos parciais.
✅ ORDENE os itens em tuplas de entrada de modo que as entradas mais aplicadas ocorram primeiro
(por exemplo, de modo que o aplicativo parcial atue de maneira semelhante ao currying).
Exemplos:
Uma operação ApplyRotation que usa um número de ponto flutuante e um qubit como entradas
geralmente pode ser parcialmente aplicada com a entrada de ponto flutuante primeiro para uso com
operações que esperam uma entrada do tipo Qubit => Unit . Portanto, uma assinatura de
operation ApplyRotation(angle : Double, target : Qubit) : Unit is Adj + Ctl funcionaria de maneira
mais consistente com a aplicação parcial.
Normalmente, essas diretrizes significam colocar todos os dados clássicos antes de todos os qubits
nas tuplas de entrada, mas use o bom senso e examine como sua API é chamada na prática.

Tipos definidos pelo usuário


Princípio chave: use tipos definidos pelo usuário para ajudar a tornar as APIs mais expressivas e convenientes
de usar.
✅ INTRODUZA tipos definidos pelo usuário para fornecer uma seleção útil para tipos longos e/ou
complicados.
Exemplos:
Em casos em que um tipo de operação com três entradas de matriz qubit costuma ser usado como
uma entrada ou retornado como uma saída, fornecendo um UDT como
newtype TimeDependentBlockEncoding = ((Qubit[], Qubit[], Qubit[]) => Unit is Adj + Ctl) pode
ajudar a fornecer uma seleção útil.
✅ INTRODUZA tipos definidos pelo usuário para indicar que um determinado tipo base só deve ser
usado em um sentido muito específico.
Exemplos:
Uma operação que deve ser interpretada especificamente como uma operação que codifica dados
clássicos em um registro quantum pode ser apropriada para rotular com um tipo definido pelo
usuário newtype InputEncoder = (Apply : (Qubit[] => Unit)) .
✅ APRESENTE tipos definidos pelo usuário com itens nomeados que permitem extensibilidade futura
(por exemplo: uma estrutura de resultados que pode conter itens nomeados adicionais no futuro).
Exemplos:
Quando uma operação TrainModel expõe muitas opções de configuração, expor essas opções como
um novo UDT TrainingOptions e fornecer uma nova função
DefaultTrainingOptions : Unit -> TrainingOptions permite aos usuários substituir itens nomeados
específicos em valores UDT TrainingOptions e, ao mesmo tempo, permitir que os desenvolvedores de
biblioteca adicionem novos itens UDT conforme apropriado.
✅ DECL ARE itens nomeados para novos tipos definidos pelo usuário em preferência a exigir que os
usuários saibam a desconstrução correta da tupla.
Exemplos:
Ao representar um número complexo em sua decomposição polar, prefira
newtype ComplexPolar = (Magnitude: Double, Argument: Double) a
newtype ComplexPolar = (Double, Double) .

Princípio chave: use tipos definidos pelo usuário de maneira a reduzir a carga cognitiva e que não exigem que
o usuário aprenda conceitos e nomenclatura adicionais.
⛔ NÃO introduza tipos definidos pelo usuário que exijam que o usuário faça uso frequente do

operador sem encapsulamento ( ! ) ou que normalmente exijam vários níveis de desencapsulamento. As
estratégias de mitigação possíveis incluem:
Ao expor um tipo definido pelo usuário com um só item, considere definir um nome para esse
item. Por exemplo, considere newtype Encoder = (Apply : (Qubit[] => Unit is Adj + Ctl)) em
preferência a newtype Encoder = (Qubit[] => Unit is Adj + Ctl) .
Garantir que outras funções e operações possam aceitar as instâncias UDT "encapsuladas"
diretamente.
⛔ NÃO introduza tipos definidos pelo usuário que dupliquem tipos internos sem fornecer uma

expressividade adicional.
Exemplos:
Um UDT newtype QubitRegister = Qubit[] não fornece nenhuma expressividade adicional Qubit[] ,
portanto, é mais difícil de usar sem nenhum benefício discernido.
Um UDT newtype LittleEndian = Qubit[] documenta como o registro subjacente deve ser usado e
interpretado, portanto, fornece uma expressividade adicional sobre seu tipo base.
️ NÃO introduza funções de acessador, a menos que seja estritamente necessário; prefira fortemente

os itens nomeados nesse caso.
Exemplos:
Ao introduzir um UDT newtype Complex = (Double, Double) , prefira modificar a definição para
newtype Complex = (Real : Double, Imag : Double) a introduzir funções GetReal : Complex -> Double
e GetImag : Complex -> Double .

Namespaces e organização
Princípio chave: escolha os nomes de namespace que são previsíveis e que informam claramente a finalidade
de funções, operações e tipos definidos pelo usuário em cada namespace.
✅ NOMEIE os namespaces como Publisher.Product.DomainArea .
Exemplos:
As funções, as operações e os UDTs publicados pela Microsoft como parte do recurso de simulação
quantum do Quantum development kit são colocados no namespace Microsoft.Quantum.Simulation .
Microsoft.Quantum.Math representa um namespace publicado pela Microsoft como parte do Quantum
development kit pertencente à área de domínio matemática.
✅ COLOQUE operações, funções e tipos definidos pelo usuário usados para funcionalidade específica
em um namespace que descreva essa funcionalidade, mesmo quando essa funcionalidade é usada em
diferentes domínios de problema.
Exemplos:
As APIs de preparação de estado publicadas pela Microsoft como parte do Quantum development kit
seriam colocadas em Microsoft.Quantum.Preparation .
As APIs de simulação do Quantum publicadas pela Microsoft como parte do Quantum development
kit seriam colocadas em Microsoft.Quantum.Simulation .
✅ COLOQUE operações, funções e tipos definidos pelo usuário usados somente dentro de domínios
específicos em namespaces que indicam seu domínio do utilitário. Se necessário, use subnamespaces
para indicar tarefas focadas em cada namespace específico de domínio.
Exemplos:
A biblioteca de Machine Learning do Quantum publicada pela Microsoft é amplamente colocada no
namespace @"microsoft.quantum.machinelearning", mas conjuntos de dados de exemplo são
fornecidos pelo namespace @"microsoft.quantum.machinelearning.datasets".
As APIs de química do Quantum publicadas pela Microsoft como parte do Quantum development kit
devem ser colocadas em Microsoft.Quantum.Chemistry . A funcionalidade específica para implementar
a decomposição de Jordan--Wigner pode ser colocada em Microsoft.Quantum.Chemistry.JordanWigner ,
de modo que a interface primária da área de domínio de química quântica não se preocupa com as
implementações.
Princípio chave: use namespaces e modificadores de acesso juntos para serem intencionais sobre a superfície
de API exposta aos usuários e para ocultar detalhes internos relacionados à implementação e ao teste de suas
APIs.
✅ Sempre que for razoável, COLOQUE todas as funções e operações necessárias para implementar
uma API no mesmo namespace que a API que está sendo implementada, mas marcada com as palavras-
chave "privado" ou "interno" para indicar que elas não fazem parte da superfície da API pública para uma
biblioteca. Use um nome que comece com um sublinhado ( _ ) para distinguir visualmente operações e
funções internas e particulares de chamadas públicas.
Exemplos:
O nome da operação _Features indica uma função que é privada para um determinado namespace e
assembly e deve ser acompanhado pela palavra-chave internal .
✅ No caso raro de um amplo conjunto de funções ou operações privadas serem necessárias para
implementar a API para um determinado namespace, COLOQUE -as em um novo namespace
correspondente ao namespace que está sendo implementado e terminando em .Private .
✅ COLOQUE todos os testes de unidade em namespaces correspondentes ao namespace em teste e
terminando em .Tests .

Convenções de nomenclatura e vocabulário


Princípio chave: escolha nomes e terminologia que sejam claros, acessíveis e legíveis em uma variedade
diversificada de públicos, incluindo iniciantes e especialistas em quantum.
⛔ NÃO use nomes de identificador discriminados ou de exclusão, nem terminologia em comentários

de documentação da API.
✅ USE comentários de documentação da API para fornecer contexto, exemplos e referências relevantes,
especialmente para conceitos mais difíceis.
⛔ NÃO use nomes de identificador desnecessariamente herméticos ou que exigem um conhecimento

significativo de algoritmos quantum para leitura.
Exemplos:
Prefira "iteração de amplificação de amplitude" a "iteração Grover".
✅ ESCOLHA operações e nomes de função que comunicam claramente o efeito pretendido de um
chamável e não sua implementação. Observe que a implementação pode e deve ser documentada nos
comentários de documentação da API.
Exemplos:
Prefira "estimar sobreposição" a "teste de Hadamard", já que este comunica como o primeiro é
implementado.
✅ USE palavras de maneira consistente em todas as APIs Q#:
Verbos:
Asser t : verifique se uma suposição sobre o estado de um computador de destino e seu
qubits se sustenta, possivelmente usando recursos não físicos. As operações que usam esse
verbo devem ser sempre removíveis com segurança sem afetar a funcionalidade de
bibliotecas e programas executáveis. Observe que, ao contrário dos fatos, as asserções
podem, em geral, depender do estado externo, como o estado de um registro de qubit, o
ambiente de execução ou assim por diante. Como a dependência do estado externo é um
tipo de efeito colateral, as asserções devem ser expostas como operações, em vez de
funções.
Estimate : usando uma ou mais medições possivelmente repetidas, estimar uma
quantidade clássica dos resultados da medição.
Exemplos:
@"microsoft.quantum.characterization.estimatefrequency"
@"microsoft.quantum.characterization.estimateoverlapbetweenstates"
Prepare : aplicar uma operação quantum ou sequência de operações a um ou mais qubits
presumidos para iniciar em um estado inicial específico (normalmente $\ket{00\cdots 0}$),
fazendo com que o estado desses qubits evolua para um estado final desejado. Em geral,
agir em outros estados que não o estado inicial especificado PODE resultar em uma
transformação unitária indefinida, mas ainda DEVE preservar essa operação e seu
"cancelamento" adjacente e aplicar um não op.
Exemplos:
@"microsoft.quantum.preparation.preparearbitrarystate"
@"microsoft.quantum.preparation.prepareuniformsuperposition"
Measure : aplique uma operação quantum ou uma sequência de operações a um ou mais
qubits, lendo os dados clássicos de volta.
Exemplos:
Measure operation
@"microsoft.quantum.arithmetic.measurefxp"
@"microsoft.quantum.arithmetic.measureinteger"
Apply : aplicar uma operação quantum ou sequência de operações a um ou mais qubits,
fazendo com que o estado desses qubits seja alterado de maneira coerente. Esse verbo é o
verbo mais geral na nomenclatura Q# e NÃO DEVE SER usado quando um verbo mais
específico é mais diretamente relevante.
Substantivos :
Fact : uma condição booliana que depende apenas de suas entradas e não do estado de um
computador de destino, seu ambiente ou o estado do qubits do computador. Ao contrário
de uma asserção, um fato só é sensível aos valores fornecidos a ele. Por exemplo:
Exemplos:
@"microsoft.quantum.diagnostics.equalityfacti": representa um fato de igualdade sobre
duas entradas de inteiro; os inteiros fornecidos como entrada são iguais ou não são,
independentemente de qualquer outro estado de programa.
Options: um UDT que contém vários itens nomeados que podem atuar como "argumentos
opcionais" para uma função ou operação. Por exemplo:
Exemplos:
O UDT @"microsoft.quantum.machinelearning.trainingoptions" inclui itens nomeados
para taxa de aprendizado, tamanho de minilote e outros parâmetros configuráveis para
treinamento de ML.
Adjetivos :
⛔ New : esse adjetivo NÃO DEVE ser usado para evitar confusão com seu uso como um

verbo em muitas linguagens de programação (por exemplo: C++, C#, Java, TypeScript,
PowerShell).
Preposições: em alguns casos, as preposições podem ser usadas para eliminar a ambiguidade ou
esclarecer as funções de substantivos e verbos em nomes de função e de operação. Porém, é
preciso cuidar para fazer isso com moderação e consistência.
As: representa que a entrada e a saída de uma função representam as mesmas
informações, mas que a saída representa essas informações como um X, em vez de sua
representação original. Isso é especialmente comum para funções de conversão de tipo.
Exemplos:
IntAsDouble(2) indica que a entrada ( 2 ) e a saída ( 2.0 ) representam qualitativamente
as mesmas informações, mas que usam diferentes tipos de dados Q# para fazer isso.
From: para garantir a consistência, essa preposição não deve ser usada para indicar
funções de conversão de tipo ou qualquer outro caso em que for apropriado.
⛔ To: essa preposição não deve ser usada para confusão com seu uso como um verbo

em muitas linguagens de programação.
Notas sobre a versão do Microsoft Quantum
Development Kit
18/05/2021 • 32 minutes to read

Este artigo contém informações sobre cada versão do Quantum Development Kit.
Para obter instruções de instalação, consulte o guia de instalação.
Para obter instruções de atualização, consulte o guia de atualização.

Versão 0.15.2103.133969
Data do lançamento: 30 de março de 2021
Emissão de QIR lançada como recurso experimental.
Restrições mais flexíveis na operação AllowAtMostNCallsCA
(https://github.com/microsoft/QuantumLibraries/pull/431).
Foram adicionadas APIs ausentes para a biblioteca de matemática
(https://github.com/microsoft/QuantumLibraries/issues/413).
GetQubitsAvailableToBorrow operation e GetQubitsAvailableToUse operation removidos
(https://github.com/microsoft/QuantumLibraries/issues/418).
Corrigida a falha do servidor de idioma Q# durante a inicialização no Visual Studio devido a
JsonReaderException (https://github.com/microsoft/qsharp-compiler/issues/885).
Adicionado suporte para vários pontos de entrada.

Versão 0.15.2102.129448
Data de lançamento: 22 de fevereiro de 2021
Melhorada a experiência de depuração do usuário IQ# adicionando uma barra de rolagem horizontal para
rolar as visualizações do caminho de execução e do estado de base.
Novas funções para representar o produto e o inverso do grupo no grupo de Clifford de qubit único, para
definir rapidamente operadores comuns de Clifford de qubit único e para aplicar operadores de Clifford de
qubit único como operações. Para saber mais, confira o problema 409.
Resolução do problema de segurança na extensão Microsoft Quantum Development Kit para Visual Studio
Code. Para mais obter detalhes, consulte a CVE-2021-27082.

Versão 0.15.2102.128318
Data de lançamento: 12 de fevereiro de 2021
Correção do erro "'npm' não é reconhecida como um comando interno ou externo" durante a criação de
projetos Q# com a extensão Visual Studio Code. Consulte o problema 848.

Versão 0.15.2101.126940
Data de lançamento: 29 de janeiro de 2021
Modelos de projeto adicionados ao compilador Q# adicionados para executáveis direcionados a provedores
IonQ e Honeywell
Atualizar realce da sintaxe do kernel IQ# para incluir alterações na sintaxe Q# introduzida na versão
0.15.2101125897
Correção de bug para dar suporte à passagem de matrizes como argumentos de entrada para programas
Q# enviados para o Azure Quantum por meio do %azure.execute , consulte o problema 401
Corrigir o erro "permissão negada" encontrado usando az dentro de imagens do Docker iqsharp-base ,
consulte o problema 404

Versão 0.15.2101125897
Data de lançamento: 26 de janeiro de 2021
Alocação de qubit simplificada, fornecendo uma sintaxe mais conveniente para alocar qubits, consulte os
detalhes no repositório de idioma Q#.
Criado o repositório QDK-Python que inclui azure-quantum , o cliente Python para enviar trabalhos de
otimização inspirados por Quantum para o serviço do Azure Quantum, bem como qdk , incluindo
qdk.chemistry , uma camada de conveniência baseada em Python para a biblioteca de química Q# que inclui
visualização e funcionalidade moleculares para gerar arquivos de entrada para vários pacotes de química,
como NWChem, Psi4 e OpenMolcas.
Os parênteses agora são opcionais para a operação e tipos de função e instruções if , elif , while e
until . Parênteses para as instruções for , use e borrow foram preteridos.
Estimativas de largura aprimoradas para profundidade ideal, consulte detalhes.
Aplicar a operação de unitário fornecida como matriz explícita usando ApplyUnitary
(QuantumLibraries#391, contribuição externa por Dmytro Fedoriaka)
Corrigido https://github.com/microsoft/iqsharp/issues/387 pela redução do impacto no desempenho na
inicialização do kernel IQ#.

Versão 0.14.2011120240
Data de lançamento: 25 de novembro de 2020
Melhorado o desempenho do compilador devido ao carregamento mais rápido da referência.
Adicionada uma gramática ANTLR para Q# na especificação da linguagem Q#.
Atualizado o Microsoft.Quantum.Preparation namespace para ser mais consistente com o guia de estilo e os
princípios de design da API e para dar suporte a estados mistos purificados com dados adicionais (consulte
proposta, notas de revisão e PRs #212, #322, #375 #376).
Os parênteses em volta de expressões de chamada repetidas agora são opcionais: (Foo(x))(y) podem ser
escritos como Foo(x)(y) .
Os usuários das extensões do Visual Studio ou do Visual Studio Code que instalaram o .NET 5 ou o Visual
Studio 16.8 podem ser solicitados a instalar o .NET Core 3.1 para continuar a trabalhar com as extensões.
Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

Versão 0.13.20111004
Data de lançamento: 10 de novembro de 2020
Esta versão desabilita os recursos do IntelliSense para arquivos Q# no Visual Studio e Visual Studio Code
quando um arquivo de projeto não está presente. Isso resolve um problema em que os recursos do IntelliSense
podem parar de funcionar depois de adicionar um novo arquivo Q# a um projeto (consulte qsharp-
compiler#720).
Versão 0.13.20102604
Data do lançamento: 27 de outubro de 2020
Esta versão contém o seguinte:
A estimativa de recursos agora emite estimativas de profundidade e largura alcançáveis simultaneamente,
além da contagem de qubits. Confira aqui para obter detalhes.
Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

Versão 0.12.20100504
Data do lançamento: 5 de outubro de 2020
Esta versão corrige um bug que afeta a carga de notebooks Q# (consulte iqsharp#331).

Versão 0.12.20092803
Data de lançamento: 29 de setembro de 2020
Esta versão contém o seguinte:
A especificação de anúncio e rascunho da Representação intermediária do Quantum (QIR) criada como um
formato comum entre diferentes front e back-ends. Veja também nossa postagem no blog sobre QIR.
Lançamento do nosso novo Q# repositório de linguagem que contém também a Q# documentação
completa.
Melhorias de desempenho do QuantumSimulator para programas que envolvem um grande número de
qubits: melhor aplicação de decisões de fusão de portões; maior paralelização no sistema Linux; adição de
agendamento inteligente de execução de portões; correções de bugs.
Agora há suporte para os recursos do IntelliSense para arquivos Q# no Visual Studio e Visual Studio Code
mesmo sem um arquivo de projeto.
Vários aprimoramentos de interoperabilidade Q#/Python e correções de bugs, incluindo melhor suporte
para tipos de dados NumPy.
Melhorias no namespace Microsoft.Quantum.Arrays (consulte microsoft/QuantumLibraries#313).
Adicionada uma nova amostra Repeat-Until-Success que usa apenas duas qubits.
Desde a última versão, a ramificação padrão em cada um de nossos repositórios de software livre foi
renomeada para main .
Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

Versão 0.12.20082513
Data de lançamento: 25 de agosto de 2020
Esta versão contém o seguinte:
Novo namespace Microsoft.Quantum.Random, fornecendo uma maneira mais conveniente de coletar
amostras de valores aleatórios em programas Q#. (QuantumLibraries#311, qsharp-runtime#328)
Melhorado o namespace Microsoft.Quantum.Diagnostics com nova DumpOperation operação e novas
operações para restringir a alocação de qubits e as chamadas do Oracle. (QuantumLibraries#302)
Novo %project comando mágico em IQ# e qsharp.projects API no Python para dar suporte a referências a
projetos Q# fora da pasta do espaço de trabalho atual. Consulte iqsharp#277 para obter as limitações atuais
desse recurso.
Suporte para carregamento automático de arquivos .csproj para hosts Q#/Python, que permite que
referências a projetos ou pacotes externos sejam carregadas no momento da inicialização. Consulte o guia
para uso Q# com Python e Jupyter Notebooks para obter mais detalhes.
Amostra de ErrorCorrection.Syndrome adicionada.
Adicionado acoplamento ajustável ao SimpleIsing.
Amostra de HiddenShift atualizada.
Amostra adicionada para resolver o Sudoku com o algoritmo do Grover (contribuição externa)
Correções gerais de bugs.
Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

Versão 0.12.20072031
Data de lançamento: 21 de julho de 2020
Esta versão contém o seguinte:
Os namespaces abertos em notebooks Q# agora estão disponíveis ao executar todas as células futuras. Isso
permite, por exemplo, que os namespaces sejam abertos uma vez em uma célula na parte superior do
notebook, em vez de precisarem abrir namespaces relevantes em cada célula de código. Um novo comando
mágico %lsopen exibe a lista de namespaces abertos no momento.

Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

Versão 0.12.20070124
Data de lançamento: 2 de julho de 2020
Esta versão contém o seguinte:
Nova ferramenta qdk-chem para converter formatos de serialização de problemas de estrutura eletrônica
herdados (por exemplo, FCIDUMP) para Broombridge
Novas funções e operações no namespace Microsoft.Quantum.Synthesis para aplicar de forma coerente
Oracles clássicos usando algoritmos de síntese baseados em transformação e descomposição.
Agora IQ# permite argumentos para %simulate , %estimate e outros comandos mágicos. Consulte a
%simulate referência do comando mágico para obter mais detalhes.
Novas opções de exibição de fase em IQ#. Consulte a %config referência do comando mágico para obter
mais detalhes.
IQ# e o pacote qsharp do Python agora são fornecidos por meio de pacotes Conda (qsharp e iqsharp) para
simplificar a instalação local da funcionalidade Python e Jupyter Q# em um ambiente Conda. Consulte os
guias de instalação do Q# Jupyter Notebooks e Q# com o Python para obter mais detalhes.
Ao usar o simulador, qubits não precisam mais estar no estado |0 no lançamento, mas podem ser
redefinidos automaticamente se eles forem medidos imediatamente antes do lançamento.
Atualizações para tornar mais fácil para os usuários IQ# consumirem pacotes de biblioteca com diferentes
versões de QDK, exigindo apenas que os números de versões principais e secundárias correspondam, e não
exatamente a mesma versão
Namespace preterido Microsoft.Quantum.Primitive.* removido
Operações movidas:
Microsoft.Quantum.Intrinsic.Assert agora é Microsoft.Quantum.Diagnostics.AssertMeasurement
Microsoft.Quantum.Intrinsic.AssertProb agora é
Microsoft.Quantum.Diagnostics.AssertMeasurementProbability
Correções de bug
Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

Versão 0.11.2006.403
Data de lançamento: 4 de junho de 2020
Esta versão corrige um bug que afeta a compilação de projetos em Q#.

Versão 0.11.2006.207
Data de lançamento: 3 de junho de 2020
Esta versão contém o seguinte:
Os programas de host de notebooks de Q# e Python não falharão mais quando um ponto de entrada Q#
estiver presente
Atualizações para a biblioteca padrão para usar modificadores de acesso
Agora, o compilador permite o plug-in de etapas de regravação entre as etapas de regravação internas
Várias funções e operações preteridas foram removidas seguindo o agendamento descrito em nossos
Princípios de API. Os programas e as bibliotecas do Q# que desenvolvem sem avisos na versão
0.11.2004.2825 continuarão a funcionar sem modificações.
Consulte a lista completa de PRs encerrados para bibliotecas, compilador, tempo de execução, amostras, IQ# e
Katas.

NOTE
Esta versão contém um bug que afeta a compilação de projetos em Q#. É recomendável atualizar para uma versão mais
recente.

Versão 0.11.2004.2825
Data de lançamento: 30 de abril de 2020
Esta versão contém o seguinte:
Novo suporte para aplicativos Q#, que não exigem mais um arquivo host do C# ou Python. Para obter mais
informações sobre a introdução aos aplicativos Q#, confira esta página.
Atualização do início rápido do gerador de número quântico aleatório para não exigir mais um arquivo host
do C# ou Python. Confira o Início Rápido atualizado
Aprimoramentos de desempenho de imagens do Docker do IQ#

NOTE
No momento, os aplicativos Q# que usam o novo atributo @EntryPoint() não podem ser chamados de programas de
host do Python ou .NET. Confira os guias de interoperabilidade do Python e do .NET para obter mais informações.

Versão 0.11.2003.3107
Data de lançamento: 31 de março de 2020
Esta versão contém pequenas correções de bugs da versão 0.11.2003.2506.

Versão 0.11.2003.2506
Data de lançamento: 26 de março de 2020
Esta versão contém o seguinte:
Novo suporte para modificadores de acesso em Q#. Para obter mais informações, confira Modificadores de
acesso
Atualizado para SDK do .NET Core 3.1
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.10.2002.2610
Data de lançamento: 27 de fevereiro de 2020
Esta versão contém o seguinte:
Nova biblioteca de Aprendizado de Máquina Quântico. Para obter mais informações, acesse nossa página de
documentos de QML
Correções de bugs do IQ#, levando a uma melhoria de 10 a 20 vezes no desempenho ao carregar pacotes
NuGet
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.10.2001.2831
Data de lançamento: 29 de janeiro de 2020
Esta versão contém o seguinte:
Novo pacote NuGet Microsoft.Quantum.SDK, que substituirá o pacote Microsoft.Quantum.Development.Kit
ao criar projetos. O pacote NuGet Microsoft.Quantum.Development.Kit continuará tendo suporte para
projetos existentes.
Suporte para extensões de compilador de Q#, habilitadas pelo novo pacote NuGet Microsoft.Quantum.SDK.
Para obter mais informações, confira a documentação no GitHub, o exemplo de extensões de compilador e o
Q# blog de desenvolvimento
Suporte adicionado para o .NET Core 3.1. É altamente recomendável ter a versão 3.1.100 instalada, pois a
criação com versões mais antigas do SDK do .NET Core pode causar problemas
Novas transformações de compilador disponíveis em Microsoft.Quantum.QsCompiler.Experimental
Nova funcionalidade para expor vetores de estado de saída como HTML em IQ#
Adicionado suporte para EstimateFrequencyA para Microsoft.Quantum.Characterization para testes de SWAP
e Hadamard
O namespace AmplitudeAmplification agora usa o guia de estilo do Q#
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.10.1912.0501
Data de lançamento: 5 de dezembro de 2019
Esta versão contém o seguinte:
O novo atributo de teste para teste de unidade Q#, confira a documentação da API aqui e o teste atualizado e
o guia de depuração aqui
O rastreamento de pilha foi adicionado no caso de um erro de execução do programa Q#
Compatibilidade com pontos de interrupção no Visual Studio Code devido a uma atualização na extensão do
Visual Studio Code em C# do OmniSharp
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.10.1911.1607
Data de lançamento: 17 de novembro de 2019
Esta versão contém o seguinte:
Correção de desempenho para Quantum katas e Jupyter Notebooks
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.10.1911.307
Data de lançamento: 1º de novembro de 2019
Esta versão contém o seguinte:
Atualizações para extensões do Visual Studio Code e Visual Studio para implantar o servidor de linguagem
como um arquivo executável independente, eliminando a dependência de versão do SDK do .NET Core
Migração para o .NET Core 3.0
Alteração significativa em Microsoft.Quantum.Simulation.Core.IOperationFactory com a introdução do novo
método Fail . Ele afeta apenas simuladores personalizados que não estendem o SimulatorBase. Para obter
mais detalhes, exiba a solicitação de pull no GitHub.
Novo suporte para atributos preteridos
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.9.1909.3002
Data de lançamento: 30 de setembro de 2019
Esta versão contém o seguinte:
Novo suporte para preenchimento de código Q# no Visual Studio 2019 (versões 16.3 e posteriores) e no
Visual Studio Code
Novo Quantum Kata para somadores de quantum
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.

Versão 0.9 (PackageReference 0.9.1908.2902)


Data de lançamento: 29 de agosto de 2019
Esta versão contém o seguinte:
Novo suporte para instruções de conjugação em Q#
Novas ações de código no compilador, como: "substituir por", "adicionar documentação" e uma atualização
simples de item de matriz
Adicionados modelo de instalação e novos comandos de projeto para a extensão do Visual Studio Code
Adicionadas novas variantes do combinador ApplyIf, como Microsoft.Quantum.Canon.ApplyIfOne
Quantum Katas adicional convertidos em Jupyter Notebooks
A Extensão do Visual Studio agora requer o Visual Studio 2019
Consulte a lista completa de PRs encerrados para bibliotecas, compilado, tempo de execução, amostras e Katas.
As alterações estão resumidas aqui, bem como instruções para atualizar seus programas existentes. Leia mais
sobre essas alterações no Q#blog de desenvolvimento .

Versão 0.8 (PackageReference 0.8.1907.1701)


Data de lançamento: 12 de julho de 2019
Esta versão contém o seguinte:
Nova indexação para divisão de matrizes; consulte a referência da linguagem para obter mais informações.
Adicionado Dockerfile hospedado no Microsoft Container Registry; consulte o repositório IQ# para obter
mais informações
Alteração da falha no simulador de rastreamento, atualização das definições de configuração, alterações de
nome; consulte o Navegador da API .NET para obter os nomes atualizados.
Consulte a lista de PRs encerrados para bibliotecas e amostras.

Versão 0.7 (PackageReference 0.7.1905.3109)


Data de lançamento: 31 de maio de 2019
Esta versão contém o seguinte:
adições à linguagem Q#,
atualizações da biblioteca de química,
uma nova biblioteca de numéricos.
Consulte a lista de PRs encerrados para bibliotecas e amostras.
As alterações estão resumidas aqui, bem como instruções para atualizar seus programas existentes. Leia mais
sobre essas alterações no Q#blog de desenvolvimento .
Sintaxe da linguagem Q#
Esta versão adiciona a nova sintaxe da linguagem Q#:
Adicionar itens nomeados para [tipos definidos pelo
usuário]microsoft.quantum.qsharp.typedeclarations#type-declarations).
Agora, os construtores de tipos definidos pelo usuário podem ser usados como funções.
Adição de suporte para copy-and-update e apply-and-reassign em tipos definidos pelo usuário.
Agora, o bloco de correção para loops de repeat-until-success é opcional.
Agora, há suporte para loops em funções (não em operações).
Biblioteca
Esta versão adiciona uma biblioteca numérica: Saiba mais sobre como usar a nova biblioteca de numéricos e
experimente os novos exemplos. PR #102.
Essa versão reorganiza as extensões e atualizações da biblioteca de química:
Melhora a modularidade de componentes, extensibilidade, limpeza de código geral. PR #58.
Adicionado suporte para wavefunctions com várias referências; wavefunctions com várias referências
esparsas e cluster acoplado unitário. PR #110.
(Obrigado!) Colaborador de 1QBit (@valentinS4t1qbit): Avaliação de energia usando ansatz de variação. PR
#120.
Atualizando o esquema Broombridge para a nova versão 0.2, adicionando a especificação de cluster
acoplado unitário. Problema #65.
Adicionando interoperabilidade de Python a funções da biblioteca de química. Experimente esta amostra.
Problema #53 PR #110.

Versão 0.6.1905
Data de lançamento: 3 de maio de 2019
Esta versão contém o seguinte:
faz alterações na linguagem Q#,
reestrutura as bibliotecas do Quantum Development Kit,
adiciona novas amostras e
corrige bugs. Vários PRs encerrados para bibliotecas e amostras.
As alterações estão resumidas aqui, bem como instruções para atualizar seus programas existentes. Leia mais
sobre essas alterações em devblogs.microsoft.com/qsharp.
Sintaxe da linguagem Q#
Esta versão adiciona a nova sintaxe da linguagem Q#:
Adicionar uma maneira abreviada de expressar especializações de operações quânticas (control e adjoints)
com operadores + . A sintaxe antiga foi preterida. Os programas que usam a sintaxe antiga (por exemplo,
: adjoint ) continuarão funcionando, mas um aviso de tempo de compilação será gerado.
Adicionar um novo operador ternário para copy-and-update, w/ <- , pode ser usado para expressar a
criação da matriz como uma modificação de uma matriz existente.
Adicione a instrução de aplicação-e-reatribuição comum, por exemplo, += , w/= .
Adicionar uma maneira de especificar um nome curto para namespaces em diretivas abertas.
Com esta versão, não permitimos mais que um elemento de matriz seja especificado no lado esquerdo de uma
instrução SET. Isso ocorre porque essa sintaxe implica que as matrizes são mutáveis quando, na verdade, o
resultado da operação sempre foi a criação de uma nova matriz com a modificação. Em vez disso, um erro do
compilador será gerado com uma sugestão para usar o novo operador copy-and-update, w/ , para chegar ao
mesmo resultado.
Reestruturação de biblioteca
Esta versão reorganiza as bibliotecas para possibilitar seu crescimento de forma consistente:
Renomeia o namespace Microsoft.Quantum.Primitive para Microsoft.Quantum.Intrinsic. Essas operações
são implementadas pelo computador de destino. O namespace Microsoft.Quantum.Primitive foi
preterido. Um aviso de tempo de execução informará quando os programas chamarem operações e
funções usando nomes preteridos.
Renomeia o pacote Microsoft.Quantum.Canon para Microsoft.Quantum.Standard. Este pacote contém
namespaces comuns à maioria dos programas em Q#. Isso inclui:
Microsoft.Quantum.Canon para operações comuns
Microsoft. Quantum.Arithmetic para operações aritméticas de uso geral
Microsoft.Quantum.Preparation para operações usadas para preparar o estado do qubit
Microsoft.Quantum.Simulation para a funcionalidade de simulação
Com essa alteração, os programas que incluem uma única instrução "aberta" para o namespace
Microsoft.Quatum.Canon poderão encontrar erros de build se o programa fizer referência a operações que
foram movidas para os outros três novos namespaces. Adicionar instruções abertas adicionais aos três novos
namespaces é uma maneira simples de resolver esse problema.
Vários namespaces foram preteridos, pois as operações neles foram reorganizadas para outros
namespaces. Os programas que usam esses namespaces continuarão funcionando e um aviso de tempo
de compilação informará o namespace em que a operação está definida.
O namespace Microsoft.Quantum.Arithmetic foi normalizado para usar o tipo definido pelo usuário
LittleEndian user defined type. Use a função BigEndianAsLittleEndian quando necessário para converter
para little endian.
Os nomes de vários chamadores (funções e operações) foram alterados para manter a conformidade
com o Q#Guia de Estilo de . Os nomes que podem ser chamados antigos foram preteridos. Os
programas que usam nomes que podem ser chamados antigos continuarão funcionando com um aviso
de tempo de compilação.
Novas amostras
Adicionamos uma amostra de uso de Q# com o driver de F#.
Obrigado! ao seguinte colaborador com nossa base de código aberto em
http://github.com/Microsoft/Quantum. Essas contribuições complementam significativamente os exemplos
avançados de código Q#:
Mathias Soeken (@msoeken): Síntese de função do Oracle. PR #135.
Migração de projetos existentes para 0.6.1905.
Consulte o guia de instalação para atualizar o QDK.
Se você tiver projetos em Q# existentes na versão 0.5 do Quantum Development Kit, veja a seguir as etapas
para migrar esses projetos para a versão mais recente.
1. Os projetos precisam ser atualizados na ordem. Se você tiver uma solução com vários projetos, atualize
cada projeto na ordem em que eles são referenciados.
2. Em um prompt de comando, execute dotnet clean para remover todos os arquivos intermediários e
binários existentes.
3. Em um editor de texto, edite o arquivo. csproj para alterar a versão de todos os PackageReference
"Microsoft.Quantum" para a versão 0.6.1904 e altere o nome do pacote "Microsoft.Quantum.Canon" para
"Microsoft.Quantum.Standard"; por exemplo:

<PackageReference Include="Microsoft.Quantum.Standard" Version="0.6.1905.301" />


<PackageReference Include="Microsoft.Quantum.Development.Kit" Version="0.6.1905.301" />

4. No prompt de comando, execute este comando: dotnet msbuild

5. Depois de executá-lo, talvez você ainda precise resolver os erros manualmente devido às alterações
listadas acima. Em muitos casos, esses erros também serão relatados pelo IntelliSense no Visual Studio
ou Visual Studio Code.
Abra a pasta raiz do projeto ou a solução que o contém no Visual Studio 2019 ou o Visual Studio
Code.
Depois de abrir um arquivo .qs no editor, você deverá ver a saída da extensão da linguagem Q# na
janela de saída.
Depois que o projeto for carregado com êxito (indicado na janela de saída), abra cada arquivo
manualmente para resolver todos os problemas restantes.
NOTE
Para a versão 0.6, o servidor de idiomas incluído com o Quantum Development Kit não dá suporte a vários
workspaces.
Para trabalhar com um projeto no Visual Studio Code, abra a pasta raiz que contém o projeto em si e todos os
projetos referenciados.
Para trabalhar com uma solução no Visual Studio, todos os projetos contidos na solução precisam estar na mesma
pasta que a solução ou em uma de suas subpastas.
As referências entre projetos migrados para o 0.6 e superior e os projetos que usam versões mais antigas do pacote
não têm suporte.

Versão 0.5.1904
Data de lançamento: 15 de abril de 2019
Esta versão contém correções de bugs.

Versão 0.5.1903
Data de lançamento: 27 de março de 2019
Esta versão contém o seguinte:
Adiciona suporte para Jupyter Notebook, o que oferece uma ótima maneira de aprender sobre Q#.
Confira as novas amostras de Jupyter Notebook e saiba como escrever seus próprios Notebooks.
Adiciona aritmética de somador de inteiros à biblioteca Quantum Canon. Consulte também um Jupyter
Notebook que descreve como usar os novos somadores de inteiros.
Correção de bug para o problema com DumpRegister relatado pela Comunidade (#148).
Adicionada a capacidade de retornar de dentro de uma instrução using e borrowing.
Remodelado o guia de introdução.

Versão 0.5.1902
Data de lançamento: 27 de fevereiro de 2019
Esta versão contém o seguinte:
Adiciona suporte para um host Python de plataforma cruzada. O pacote qsharp para Python facilita a
simulação de operações e funções de Q# em Python. Saiba mais sobre a interoperabilidade de Python.
As extensões do Visual Studio e do Visual Studio Code agora dão suporte à renomeação de símbolos (por
exemplo, funções e operações).
A extensão do Visual Studio agora pode ser instalada no Visual Studio 2019.

Versão 0.4.1901
Data de lançamento: 30 de janeiro de 2019
Esta versão contém o seguinte:
adiciona suporte para um novo tipo primitivo, BigInt, que representa um inteiro com sinal de tamanho
arbitrário. Saiba mais sobre o Bigint.
adicionar o novo simulador Toffoli, um simulador rápido com finalidade especial que pode simular
operações quânticas X, CNOT e X com vários controlados com números muito grandes de qubits. Saiba mais
sobre o simulador Toffoli.
adiciona um estimador de recursos simples que estima os recursos necessários para executar uma
determinada instância de uma operação em Q# em um computador quântico. Saiba mais sobre o Estimador
de recursos.

Versão 0.3.1811.2802
Data de lançamento: 28 de novembro de 2018
Embora nossa extensão do VS Code não tenha sido usada, ela foi sinalizada e removida do marketplace durante
a limpeza de extensões relacionada ao pacote event-stream do NPM. Essa versão remove todas as
dependências de tempo de execução que podem fazer com que a extensão dispare qualquer sinalizador
vermelho.
Se já tiver instalado a extensão, você precisará instalá-la novamente visitando a extensão do Microsoft Quantum
Development Kit para Visual Studio Code no Visual Studio Marketplace e pressionando instalar. Lamentamos
pelo inconveniente.

Versão 0.3.1811.1511
Data de lançamento: 20 de novembro de 2018
Esta versão corrige um bug que impedia alguns usuários de carregar com êxito a extensão do Visual Studio.

Versão 0.3.1811.203
Data de lançamento: 2 de novembro de 2018
Esta versão inclui algumas correções de bugs, incluindo:
Invocar DumpMachine podia alterar o estado do simulador em determinadas situações.
Avisos de compilação removidos ao compilar projetos usando uma versão do .NET Core anterior ao 2.1.403.
Limpar a documentação, especialmente as dicas de ferramenta mostradas com o deslocamento do ponteiro
do mouse no VS Code ou no Visual Studio.

Versão 0.3.1810.2508
Data de lançamento: 29 de outubro de 2018
Esta versão inclui novos recursos de linguagem e uma experiência de desenvolvedor aprimorada:
Esta versão inclui um servidor de linguagem para Q#, bem como as integrações de cliente para o Visual
Studio e o Visual Studio Code. Isso habilita um novo conjunto de recursos do IntelliSense, juntamente com
feedback dinâmico no formato de sublinhados ondulados de erros e avisos.
Essa atualização melhora muito as mensagens de diagnóstico em geral, com navegação fácil e intervalos
precisos de diagnósticos, e detalhes adicionais nas informações exibidas em foco.
A linguagem Q# foi estendida de maneira a unificar as formas como os desenvolvedores podem realizar
operações comuns e com novos aprimoramentos nos recursos de linguagem para expressar de forma
eficiente a computação quântica. Há algumas alterações de falha na linguagem Q# com esta versão.
Esta versão também inclui uma nova biblioteca de química quântica:
A biblioteca de química contém novos recursos de simulação Hamiltoniana, incluindo:
integradores de Trotter-Suzuki de ordem uniforme arbitrária para melhorar a precisão da simulação.
Técnica de simulação de qubits com otimizações específicas da química para reduzir a complexidade
de $T$-Gate.
Um novo esquema de software livre, chamado de Esquema Broombridge (em referência a um marco
celebrado como o local de nascimento do Hamiltonianos) foi introduzido para importar representações de
moléculas e simulá-las.
São fornecidas várias representações químicas definidas usando o Esquema Broombridge. Esses modelos
foram gerados pelo NWChem, uma ferramenta de química computacional de alto desempenho de software
livre.
Tutoriais e amostras descrevem como usar a biblioteca de química e os modelos de dados de Broombridge
para:
Construir Hamiltonianos simples usando a biblioteca de química
Visualizar as energias em estado fundamental e excitado do Hidreto de lítio usando a estimativa de
fase.
Executar estimativas de recursos da simulação de química quântica.
Estimar os níveis de energia das moléculas representados pelo Esquema Broombridge.
A documentação descreve como usar o NWChem para gerar modelos químicos adicionais para simulação
quântica com Q#.
Saiba mais sobre a biblioteca de química do Quantum Development Kit.
Com a nova biblioteca de química, estamos separando as bibliotecas em um novo repositório do GitHub,
Microsoft/QuantumLibraries. Os exemplos permanecem no repositório Microsoft/Quantum. Contribuições são
bem-vindas em ambos!
Esta versão inclui correções de bugs e recursos para problemas relatados pela comunidade.
Contribuições da Comunidade
Obrigado! aos seguintes colaboradores com nossa base de código aberto em
http://github.com/Microsoft/Quantum. Essas contribuições complementam significativamente os exemplos
avançados de código Q#:
Rolf Huisman (@RolfHuisman): melhorou a experiência para desenvolvedores de QASM/Q# criando um
tradutor de QASM para Q#. PR #58.
Andrew Helwer (@ahelwer): contribuiu com um exemplo que implementa o jogo CHSH, um jogo
quântico relacionado à não localidade. PR #84.
Obrigado também a Rohit Gupta (@guptarohit,PR #90), Tanaka Takayoshi (@tanaka-takayoshi,PR #289) e Lee
James O'Riordan (@mlxd,PR #96) por seu trabalho para melhorar o conteúdo para todos nós por meio de
correções de documentação, ortografia e digitação!

Versão 0.2.1809.701
Data de lançamento: 10 de setembro de 2018
Esta versão inclui correções de bugs para problemas relatados pela Comunidade.

Versão 0.2.1806.3001
Data de lançamento: 30 de junho de 2018
Este lançamento é apenas uma correção rápida do problema #48 relatado no GitHub (a compilação de Q#
falhará se o nome de usuário contiver um espaço em branco). Siga as mesmas instruções de atualização que
0.2.1806.1503 com a nova versão correspondente ( 0.2.1806.3001-preview ).

Versão 0.2.1806.1503
Data de lançamento: 22 de junho de 2018
Esta versão inclui várias contribuições da Comunidade, bem como uma experiência de depuração aprimorada e
melhor desempenho. Especificamente:
melhorias de desempenho em simulações pequenas e grandes para o computador de destino
QuantumSimulator.
Aprimoramento da funcionalidade de depuração.
Contribuições da Comunidade com correções de bugs, novas funções auxiliares, operações e novos
exemplos.
Melhorias de desempenho
Essa atualização inclui melhorias significativas de desempenho para a simulação de números grandes e
pequenos de qubits para todos os computadores de destino. Essa melhoria é facilmente visível com a simulação
H2 que é um exemplo padrão no Quantum Development Kit.
Aprimoramento da funcionalidade de depuração
Esta atualização adiciona a nova funcionalidade de depuração:
Foram adicionadas duas novas operações, @"microsoft.quantum.extensions.diagnostics.dumpmachine" e
@"microsoft.quantum.extensions.diagnostics.dumpregister", que geram informações de função wave sobre o
computador quântico de destino em um momento determinado.
No Visual Studio, a probabilidade de medir um $\ket{1}$ em um único qubit agora é mostrada
automaticamente na janela de depuração do computador de destino de QuantumSimulator.
No Visual Studio, melhorou a exibição de propriedades variáveis nas janelas de depuração Automáticos e
Locais .
Saiba mais sobre como Testar e depurar.
Contribuições da Comunidade
A comunidade de codificação em Q# está crescendo e estamos muito felizes ao ver as primeiras bibliotecas e os
exemplos contribuídos pelo usuário que foram enviados à nossa base de código aberto em
http://github.com/Microsoft/quantum. Muito obrigado! aos seguintes colaboradores:
Mathias Soeken (@msoeken): contribuiu com um exemplo que define um método de síntese lógica baseado
em transformação que constrói redes Toffoli para implementar uma determinada permutação. O código é
totalmente escrito em funções e operações de Q#. PR #41.
RolfHuisman (@RolfHuisman): o Microsoft MVP Rolf Huisman contribuiu com um exemplo que gera código
de QASM simples do código Q# para uma classe restrita de programas que não têm fluxo de controle
clássico e operações quânticas restritas. PR #59
Sarah Kasier (@crazy4pi314): ajudou a melhorar nossa base de código enviando uma função de biblioteca
para operações controladas. PR #53
Jessica Lemieux (@Lemj3111): correção de @"microsoft.quantum.canon.quantumphaseestimation" e criação
de novos testes de unidade. PR #54
Tama McGlinn (@TamaMcGlinn): limpou a amostra de Teleportação, certificando-se de que a instância de
QuantumSimulator seja descartada. PR #20
Além disso, um grande Obrigado! a estes engenheiros de software da Microsoft da equipe de Serviços de
Engenharia Comercial, que fizeram alterações valiosas em nossa documentação durante seu Hackathon. Suas
alterações aumentaram imensamente a clareza e melhoraram a experiência de integração para todos nós:
Sascha Corti
Mihaela Curmei
John Donnelly
Kirill Logachev
Jan Pospisil
Anita Ramanan
Frances Tibble
Alessandro Vozza
Atualizar projetos existentes
Esta versão é totalmente compatível com versões anteriores. Basta atualizar o pacotes do NuGet em seus
projetos para a versão 0.2.1806.1503-preview e fazer uma recompilação completa para garantir que todos os
arquivos intermediários sejam regenerados.
No Visual Studio, siga as instruções normais sobre como atualizar um pacote.
Para atualizar modelos de projeto para a linha de comando, execute o seguinte comando:

dotnet new -i "Microsoft.Quantum.ProjectTemplates::0.2.1806.1503-preview"

Depois de executar esse comando, todos os novos projetos criados usando dotnet new <project-type> -lang Q#
usarão automaticamente esta versão do Quantum Development Kit.
Para atualizar um projeto existente para usar a versão mais recente, execute o seguinte comando de dentro do
diretório para cada projeto:

dotnet add package Microsoft.Quantum.Development.Kit -v "0.2.1806.1503-preview"


dotnet add package Microsoft.Quantum.Canon -v "0.2.1806.1503-preview"

Se um projeto existente também usar a integração de XUnit para testes de unidade, um comando semelhante
poderá ser usado para atualizar esse pacote também:

dotnet add package Microsoft.Quantum.Xunit -v "0.2.1806.1503-preview"

Dependendo da versão do XUnit que seu projeto de teste usa, talvez você também precise atualizar o XUnit para
2.3.1:

dotnet add package xunit -v "2.3.1"

Após a atualização, certifique-se de remover todos os arquivos temporários gerados pela versão anterior,
fazendo o seguinte:

dotnet clean

Problemas conhecidos
Não há problemas conhecidos adicionais a relatar.

Versão 0.2.1802.2202
Data de lançamento: 26 de fevereiro de 2018
Esta versão dá suporte para desenvolvimento em mais plataformas, interoperabilidade de linguagem e
aprimoramentos de desempenho. Especificamente:
Suporte para desenvolvimento baseado em macOS e Linux.
Compatibilidade com o .NET Core, incluindo suporte para Visual Studio Code entre plataformas.
Uma licença de software livre completa para as bibliotecas do Quantum Development Kit.
Melhoria do desempenho do simulador em projetos que exigem 20 ou mais qubits.
Interoperabilidade com a linguagem Python (versão prévia disponível no Windows).
Edições de .NET
A plataforma .NET está disponível por meio de duas edições diferentes, o .NET Framework fornecido com o
Windows e o .NET Core de software livre disponível no Windows, no macOS e no Linux. Com esta versão, a
maior parte do Quantum Development Kit é fornecida como bibliotecas para .NET Standard, o conjunto de
classes comuns tanto à estrutura quanto ao núcleo. Portanto, essas bibliotecas são compatíveis com as versões
recentes do .NET Framework ou do .NET Core.
Sendo assim, para ajudar a garantir que os projetos escritos usando o Quantum Development Kit sejam os mais
portáteis possíveis, recomendamos que os projetos de biblioteca escritos usando o Quantum Development Kit
tenham como destino o .NET Standard, enquanto os aplicativos de console tenham como destino o .NET Core.
Como versões anteriores do Quantum Development Kit davam suporte apenas ao .NET Framework, talvez seja
necessário migrar seus projetos existentes; consulte abaixo para obter detalhes de como fazer isso.
Migração do projeto
Projetos criados com versões anteriores do Quantum Development Kit ainda funcionarão, desde que você não
atualize os pacotes NuGet usados neles. Para migrar o código existente para a nova versão, execute as seguintes
etapas:
1. Crie um novo projeto .NET Core usando o tipo correto de modelo de projeto Q# (Aplicativo, Biblioteca ou
Projeto de Teste).
2. Copie os arquivos .qs e .cs/.fs existentes do projeto antigo para o novo projeto (usando Adicionar > Item
Existente). Não copie o arquivo AssemblyInfo.cs.
3. Compile e execute o novo projeto.
Observe que a operação RandomWalkPhaseEstimation do namespace Microsoft.Quantum.Canon foi movida
para o namespace Microsoft.Research.Quantum.RandomWalkPhaseEstimation no repositório
Microsoft/Quantum-NC.
Problemas conhecidos
A opção --filter para dotnet test não funciona corretamente para testes escritos em Q#. Como
resultado, testes de unidade individuais não podem ser executados no Visual Studio Code; é recomendável
usar dotnet test no prompt de comando para executar novamente todos os testes.

Versão 0.1.1801.1707
Data de lançamento: 18 de janeiro de 2018
Esta versão corrige alguns problemas relatados pela Comunidade. Especificamente:
O simulador agora funciona com CPUs antigas não habilitadas para AVX.
As configurações regionais de decimais não farão com que o analisador de Q# falhe.
A operação primitiva SignD agora retorna Int em vez de Double .

Versão 0.1.1712.901
Data de lançamento: 11 de dezembro de 2017
Problemas conhecidos
Requisitos de hardware e software
O simulador incluído no Quantum Development Kit requer uma instalação de 64 bits do Microsoft Windows
para ser executado.
O simulador quântico da Microsoft, instalado com o Quantum Development Kit, utiliza AVX (Advanced Vector
Extensions) e requer uma CPU habilitada para AVX. Os processadores Intel fornecidos no primeiro trimestre
de 2011 (Sandy Bridge) ou posteriores dão suporte a AVX. Estamos avaliando o suporte para CPUs
anteriores e poderemos anunciar detalhes em um momento posterior.
Criação do projeto
Ao criar uma solução (.sln) que usará Q#, a solução deve estar um diretório acima de cada projeto (.csproj)
na solução. Ao criar uma nova solução, isso pode ser feito certificando-se de que a caixa de seleção "Criar
diretório para a solução" esteja marcada na caixa de diálogo "Novo projeto". Se isso não for feito, os pacotes
NuGet do Quantum Development Kit NuGet precisarão ser instalados manualmente.
Q#
O IntelliSense não exibe erros adequados para o código em Q#. Verifique se você está exibindo erros de
build no Lista de Erros do Visual Studio para ver os erros corretos de Q#. Observe também que os erros de
Q# não aparecerão até que você tenha feito um build.
Usar uma matriz mutável em um aplicativo parcial pode levar a um comportamento inesperado.
Associar uma matriz imutável a uma matriz mutável (let a = b, em que b é uma matriz mutável) pode levar a
um comportamento inesperado.
A criação de perfil, a cobertura de código e outros plug-ins do VS nem sempre podem contar as linhas e os
blocos de Q# com precisão.
O compilador de Q# não valida cadeias de caracteres interpoladas. É possível criar erros de compilação de
C# digitando incorretamente nomes de variáveis ou usando expressões em cadeias de caracteres
interpoladas em Q#.
Simulação
O Simulador de Quantum usa OpenMP para paralelizar a álgebra linear necessária. Por padrão, o OpenMP
usa todos os threads de hardware disponíveis, o que significa que programas com pequenos números de
qubits geralmente serão executados lentamente, pois a coordenação necessária reduzirá o trabalho real. Isso
pode ser corrigido definindo a variável de ambiente OMP_NUM_THREADS como um número pequeno.
Como uma regra prática muito ampla, 1 thread é adequado para até cerca de 4 qubits e, em seguida, 1
thread adicional por qubit é adequado, embora isso seja altamente dependente de seu algoritmo.
Depuração
F11 (step in) não funciona no código Q#.
O realce de código em Q# em um ponto de interrupção ou pausa em uma única etapa é, às vezes, impreciso.
A linha correta será realçada, mas às vezes o realce será iniciado e terminará em colunas incorretas na linha.
Testando
Os testes devem ser executados no modo de 64 bits. Se os testes estiverem falhando com uma
BadImageFormatException, vá para o menu de teste e selecione Configurações do Teste > Arquitetura do
Processador Padrão > x64.
Alguns testes levam muito tempo (possivelmente até 5 minutos, dependendo do seu computador) para
serem executados. Isso é normal, pois alguns usam mais de 20 qubits; nosso maior teste é executado
atualmente em 23 qubits.
Exemplos
Em alguns computadores, algumas amostras pequenas podem ser executadas lentamente, a menos que a
variável de ambiente OMP_NUM_THREADS esteja definida como "1". Consulte também a nota de versão em
"Simulação".
Bibliotecas
Há uma suposição implícita de que qubits passados para uma operação em argumentos diferentes são todos
distintos. Por exemplo, todas as operações de biblioteca (e todos os simuladores) pressupõem que os dois
qubits passados para um NOT controlado sejam qubits diferentes. Violar essa pressuposição pode levar a
resultados imprevisíveis. É possível testar isso usando o simulador de rastreamento de computação quântica.
A função Microsoft.Quantum.Bind pode não funcionar conforme o esperado em todos os casos.
No namespace Microsoft.Quantum.Extensions.Math, a função SignD retorna um Double em vez de um Int,
embora a função System.Math.Sign subjacente sempre retorne um inteiro. É seguro comparar o resultado
com relação a 1,0,-1,0 e 0,0, pois todos esses doubles têm representações binárias exatas.
Recursos de aprendizagem
19/05/2021 • 5 minutes to read

Recursos de aprendizagem de otimização


As seguintes publicações podem ser bons pontos de partida para entender os princípios matemáticos por trás
dos resolvedores:
Glover F., Kochenberger G., Du Y. (2019). Um tutorial sobre a formulação e o uso de modelos QUBO.
Lucas, Andrew. (2014) Formulações Ising de muitos problemas de NP. Fronteiras na física

Recursos de aprendizagem da computação quântica


Embora as ferramentas discutidas na seção Conceitos de computação quântica sejam fundamentais para
qualquer desenvolvedor de software quantum, elas não abrangem a profundidade nem a amplitude do que é
conhecido sobre a programação de computadores e o design de algoritmos quantum. Como a computação
quântica permanece um campo em rápido desenvolvimento, não há um recurso que tenha todas as
informações necessárias para aprender a usar melhor essa ferramenta para resolver problemas. Por esse
motivo, compilamos uma lista de referência que pode ser de interesse do leitor que deseja saber mais sobre a
arte e a beleza da programação de computação quântica. Esta seção contém referências selecionadas para
cobertura profunda de tópicos de computação quântica.
Basic quantum computing
Nielsen, M. A. & Chuang, I. L. (2010). Computação quântica e informações do Quantum. Quantum
Computation and Quantum Information, UK: Cambridge University Press, 2010.
Kaye, P., Laßamme, R., & Mosca, M. (2007). An introduction to quantum computing. Oxford University Press.
Rieffel, E. G., & Polak, W. H. (2011). Quantum computing: A gentle introduction. MIT Press.
Sarah C. Kaiser and Christopher E. Granade (o programa Manning Early Access iniciou em abril de 2019 e a
publicação foi realizada em no outono do hemisfério norte de 2020). Aprenda a computação quântica com
Python e Q# – uma abordagem prática.
Conteúdo da comunidade
Awesome qsharp: uma lista de software livre de código e recursos Q#.
Quantum Computing StackExchange: uma comunidade online para que os desenvolvedores de quantum
aprendam e compartilhem seus conhecimentos.
Subreddit para a linguagem de programação Q#: uma comunidade online no Reddit para discutir as notícias
e os desenvolvimentos mais recentes do Q#.
Subreddit para a computação quântica: uma comunidade online no Reddit para discutir as notícias e os
desenvolvimentos mais recentes da computação quântica.
Comunidade de Q#: um grupo da comunidade que coleta e mantém projetos relacionados à linguagem de
programação Q# por meio de uma comunidade de pessoas empolgadas com a programação quantum.
Cursos online
Módulos de computação quântica do MS Learn. Um tutorial passo a passo de conceitos e práticas de
computação quântica.
Computação quântica – curso incrível. Aprenda a criar algoritmos quantum desde o princípio com um
computador quantum simulado em seu navegador.
Introdução à computação quântica – LinkedIn Learning. Introdução ao vídeo de 1h25m.
Computação quântica em quadrinhos. Aula prática da comunidade semanal aos domingos.
Tópicos avançados
Elementary techniques for building controlled gates
Barenco, A., Bennett, C. H., Cleve, R., DiVincenzo, D. P., Margolus, N., Shor, P., ... & Weinfurter, H. (1995).
Elementary gates for quantum computation. Physical Review A, 52(5), 3457.
Jones, C. (2013). Low-overhead constructions for the fault-tolerant Toffoli gate. Physical Review A, 87(2),
022328
Techniques for preparing quantum states
Shende, V. V., Markov, I. L., & Bullock, S. S. (2004). Minimal universal two-qubit controlled-NOT-based circuits.
Physical Review A, 69(6), 062321.
Ozols, M., Roetteler, M., & Roland, J. (2013). Quantum rejection sampling. ACM Transactions on Computation
Theory (TOCT), 5(3), 11.
Grover, L., & Rudolph, T. (2002). Creating superpositions that correspond to efficiently integrable probability
distributions. arXiv preprint quant-ph/0208112.
Farhi, E., Goldstone, J., Gutmann, S., & Sipser, M. (2000). Quantum computation by adiabatic evolution. arXiv
preprint quant-ph/0001106.
Approaches for synthesizing circuits out of H, T and CNOT gates
Kliuchnikov, V., Maslov, D., & Mosca, M. (2013). Asymptotically optimal approximation of single qubit
unitaries by Clifford and T circuits using a constant number of ancillary qubits. Physical Review Letters,
110(19), 190502.
Ross, N. J., & Selinger, P. (2014). Optimal ancilla-free Clifford+ T approximation of z-rotations. arXiv preprint
arXiv:1403.2975.
Kliuchnikov, V. (2013). Synthesis of unitaries with Clifford+ T circuits. arXiv preprint arXiv:1306.3200.
Jones, N. C., WhitÞeld, J. D., McMahon, P. L., Yung, M. H., Van Meter, R., Aspuru-Guzik, A. e Yamamoto, Y. (2012).
Faster quantum chemistry simulation on fault-tolerant quantum computers. New Journal of Physics, 14(11),
115023.
Approaches for quantum arithmetic
Takahashi, Y., & Kunihiro, N. (2005). A linear-size quantum circuit for addition with no ancillary qubits.
Quantum Information & Computation, 5(6), 440-448.
Draper, T. G. (2000). Addition on a quantum computer. arXiv preprint quant-ph/0008033.
Soeken, M., Roetteler, M., Wiebe, N., & De Micheli, G. (março de 2017). Design automation and design space
exploration for quantum computers. In 2017 Design, Automation & Test in Europe Conference & Exhibition
(DATE) (pp. 470-475). IEEE.
Methods for fast quantum sampling (amplitude amplification ) and probability estimation
Brassard, G., Hoyer, P., Mosca, M., & Tapp, A. (2002). Quantum amplitude ampliÞcation and estimation.
Contemporary Mathematics, 305, 53-74.
Grover, L. K. (2005). Fixed-point quantum search. Physical Review Letters, 95(15), 150501.
Berry, D. W., Childs, A. M., & Kothari, R. (outubro de 2015). Hamiltonian simulation with nearly optimal
dependence on all parameters. In Foundations of Computer Science (FOCS), 2015 IEEE 56th Annual
Symposium on (pp. 792-809). IEEE.
Algorithms for quantum simulation
Lloyd, S. (1996). Universal Quantum Simulators. Science (New York, NY), 273(5278), 1073.
Berry, D. W., Childs, A. M., Cleve, R., Kothari, R., & Somma, R. D. (2015). Como simular a dinâmica
hamiltoniana com uma série de Taylor truncada. Physical Review Letters, 114(9), 090502.
Low, G. H., & Chuang, I. L. (2017). Optimal Hamiltonian simulation by quantum signal processing. Physical
Review Letters, 118(1), 010501.
Low, G. H., & Chuang, I. L. (2016). Hamiltonian simulation by qubitization. arXiv preprint:1610.06546.
Reiher, M., Wiebe, N., Svore, K. M., Wecker, D., & Troyer, M. (2017). Elucidating reaction mechanisms on
quantum computers. Proceedings of the National Academy of Sciences, 201619152.
Wiebe, N., Berry, D. W., H¯yer, P., & Sanders, B. C. (2011). Simulating quantum dynamics on a quantum
computer. Journal of Physics A: Mathematical and Theoretical, 44(44), 445308.
Peruzzo, A., McClean, J., Shadbolt, P., Yung, M. H., Zhou, X. Q., Love, P. J., ... & Obrien, J. L. (2014). A variational
eigenvalue solver on a photonic quantum processor. Nature communications, 5.
Procedures for quantum optimization
Durr, C., & H¯yer, P. (1996). A quantum algorithm for Þnding the minimum. arXiv preprint quantph/9607014.
Farhi, E., Goldstone, J., & Gutmann, S. (2014). A quantum approximate optimization algorithm. arXiv preprint
arXiv:1411.4028.
Brandao, F. G., Svore, K. M. (2017). Quantum Speed-ups for SemideÞnite Programming. In Annual
Symposium on Foundations of Computer Science (FOCS 2017).
Guia do usuário do Q#
23/04/2021 • 2 minutes to read

Bem-vindo(a) ao guia do usuário do Q#!


Nos diferentes tópicos deste guia, apresentamos algumas das noções básicas para o desenvolvimento de
programas quantum usando o Q#.
Nós nos referimos ao guia da linguagem Q# para obter uma especificação e documentação completas da
linguagem de programação quantum Q#.

Conteúdo do guia do usuário


Programas Q#: uma rápida introdução aos programas quânticos no Q#.
Maneiras de executar um programa Q#: descreve como um programa Q# é executado e mostra uma
visão geral das diversas formas de chamar o programa: da linha de comando, de Jupyter Notebooks Q#
ou de um programa de host clássico escrito em uma linguagem Python ou .NET.
Testando e depurando: Detalha algumas técnicas para verificar se o código está fazendo o que deveria
fazer. Devido à opacidade geral das informações quânticas, a depuração de um programa quântico pode
exigir técnicas especializadas. Felizmente, o Q# é compatível com muitas das técnicas de depuração
clássicas que os programadores já conhecem, bem como aquelas específicas para computação quântica.
Isso inclui a criação e execução de testes de unidade em Q#, a inserção de declarações em valores e
probabilidades no código e as funções Dump , que produzem os estados dos computadores de destino.
Essas últimas podem ser usadas junto com nosso simulador de estado completo para depurar
determinadas partes de computações contornando algumas limitações da computação quântica (por
exemplo, o teorema da não clonagem).
Simuladores Quantum e avaliadores de recursos
Simuladores e aplicativos host quânticos: Uma visão geral dos diferentes simuladores disponíveis e de
como os programas host e os computadores de destino funcionam juntos para executar programas Q#.
Simulador de estado completo: O computador de destino que simula o estado quântico completo. Útil
para execução ou depuração completa de programas de menor escala (menos que algumas dúzias de
qubits)
Estimador de recursos: Estima os recursos necessários para executar uma determinada instância de uma
operação em Q# em um computador quântico.
Simulador de rastreamento: Executa um programa quântico da Microsoft sem realmente simular o
estado de um computador quântico e, portanto, pode executar programas quânticos que usam milhares
de qubits. Útil para depurar um código clássico dentro de um programa quantum, bem como estimar os
recursos necessários.
Simulador do Toffoli: Um simulador quantum de uso especial que pode ser usado com milhões de qubits,
mas somente para programas com um conjunto restrito de operações quantum (X, CNOT e X
multicontrolado).
Páginas de referência rápida
Comandos magic do IQ#: Página de referência rápida para comandos magic do IQ# do Jupyter Notebooks
no Q#.
Linguagem de programação quântica Q#
28/04/2021 • 2 minutes to read

Tudo o que você precisa saber sobre a linguagem de programação Q# é detalhado no guia de linguagem Q#.
Como qualquer outro, nosso processo de design de linguagem é um software livre e as contribuições são bem-
vindas. Para obter mais informações sobre as bases e a motivação por trás do Q#, consulte Por que precisamos
do Q#?.
O Q# é fornecido como parte do Quantum Development Kit (QDK). Para uma visão geral rápida, confira O que é
o QDK?.

O que é um programa quântico?


Um programa quântico pode ser visto como um conjunto específico de sub-rotinas clássicas que, quando
chamadas, executam uma computação interagindo com um sistema quântico; um programa escrito em Q# não
modela diretamente o estado quântico, mas descreve como um computador de controle clássico interage com
qubits. Isso nos permite ser totalmente independentes a respeito do que um estado quântico é em cada
computador de destino, o que pode ter interpretações diferentes dependendo do computador.
Uma consequência importante disso é que o Q# não tem a capacidade de introspecção sobre oo estado de um
qubit ou outras propriedades da mecânica quântica diretamente, o que garante que um programa em Q# possa
ser fisicamente executado em um computador quântico. Em vez disso, um programa pode chamar operações
como Measure para extrair informações clássicas de um qubit.
Uma vez alocado, um qubit pode ser passado para operações e funções, também conhecido como chamadores.
De certa forma, isso é tudo o que um programa em Q# pode fazer com um qubit. Todas as ações diretas no
estado de um qubit são definidas por chamadores intrínsecos, como X e H , ou seja, chamadores cujas
implementações não são definidas em Q#, mas, em vez disso, são definidas pelo computador de destino. O que
essas operações realmente fazem é apenas concretizado pelo computador de destino que usamos para executar
o programa em Q# específico.
Por exemplo, se estiver executando o programa em nosso simulador de estado completo, o simulador executará
as operações matemáticas correspondentes para o sistema quântico simulado. Mas olhando para o futuro,
quando o computador de destino for um computador quântico real, chamar tais operações em Q# direcionará o
computador quântico para executar as operações reais correspondentes no sistema quântico real (por exemplo,
pulsos de laser com precisão exata).
Um programa em Q# recombina essas operações conforme definido por um computador de destino para criar
operações novas e de nível mais alto para expressar a computação quântica. Dessa forma, o Q# facilita a
expressão dos algoritmos quânticos subjacentes e híbridos quânticos-clássicos, além de ser geral em relação à
estrutura de um computador de destino ou simulador.
Um exemplo simples é o seguinte programa, que aloca um qubit no estado $\ket{0}$, depois aplica uma
operação de Hadamard H a ele e mede o resultado na base PauliZ .
@EntryPoint()
operation MeasureOneQubit() : Result {
// The following using block creates a fresh qubit and initializes it
// in the |0 state.
use qubit = Qubit();
// We apply a Hadamard operation H to the state, thereby preparing the
// state 1 / sqrt(2) (|0 + |1 ).
H(qubit);
// Now we measure the qubit in Z-basis.
let result = M(qubit);
// As the qubit is now in an eigenstate of the measurement operator,
// we reset the qubit before releasing it.
if result == One { X(qubit); }
// Finally, we return the result of the measurement.
return result;

Nosso Quantum Katas oferece uma boa introdução sobre Conceitos de computação quântica, como operações
quânticas comuns e como manipular qubits. Mais exemplos também podem ser encontrados em Operações e
funções intrínsecas.
Maneiras de executar um programa em Q#
01/05/2021 • 21 minutes to read

Um dos maiores pontos fortes do kit de desenvolvimento Quantum é sua flexibilidade entre plataformas e
ambientes de desenvolvimento. No entanto, isso também significa que novos usuários Q# podem ficar confusos
ou sobrecarregados pelas inúmeras opções encontradas no Guia de instalação. Nesta página, explicamos o que
acontece quando um programa Q# é executado e comparamos as diferentes maneiras em que os usuários
podem fazer isso.
Uma distinção principal é que Q# pode ser executado:
como um aplicativo autônomo, onde Q# é o único idioma envolvido e o programa é invocado diretamente.
Dois métodos se enquadram nesta categoria:
a interface de linha de comando dos
Q# notebooks Jupyter
com um programa de host adicional, escrito em Python ou em uma linguagem .net (por exemplo, C# ou F#),
que então invoca o programa e pode processar posteriormente os resultados retornados.
Para entender melhor esses processos e suas diferenças, consideramos um simples programa Q# e
comparamos as maneiras como ele pode ser executado.

Programa Q# básico
Um programa do Quantum básico consiste em preparar um qubit em uma superposição de status equivalente
$\ket {0} $ e $\ket {1} $, medindo esse qubit e informando o resultado, que será aleatoriamente um desses dois
estados com probabilidade igual. Esse processo está no núcleo do início rápido do gerador de números
aleatórios Quantum.
No Q#, isso seria executado pelo seguinte código:

use q = Qubit(); // allocates qubit for use (automatically in |0>)


H(q); // puts qubit in superposition of |0> and |1>
return MResetZ(q); // measures qubit, returns result (and resets it to |0> before deallocation)

No entanto, esse código sozinho não pode ser executado pelo Q#. Para isso, ele precisa incorporar uma
operação, que, em seguida, será executada quando chamada diretamente ou por outra operação. Portanto, é
possível gravar uma operação da seguinte maneira:

operation MeasureSuperposition() : Result {


use q = Qubit();
H(q);
return MResetZ(q);

Você definiu uma operação, MeasureSuperposition , que não recebe entradas e retorna um valor do tipo Result.
Além das operações, o Q# também permite encapsular computações determinísticas em funções. Além da
garantia determinista que implica que as computações que agem em qubits precisam ser encapsuladas em
operações em vez de funções, há pouca diferença entre operações e funções. Nos referimos a eles
coletivamente como chamadores.
O chamador é definido em um arquivo Q#
O chamador é, precisamente, o que é chamado e executado pelo Q#. No entanto, ele requer mais alguns
complementos para compor um *.qs Q# arquivo completo.
Todos os tipos Q# e os chamadores (aqueles que você define e os que são intrínsecos ao idioma) são definidos
em namespaces, que fornecem a cada um deles um nome completo que pode ser referenciado.
Por exemplo, as operações H e MResetZ são encontradas nos namespaces Microsoft.Quantum.Instrinsic e
Microsoft.Quantum.Measurement (parte das Q#, nas bibliotecas padrão). Assim, eles podem ser chamados pelos
seus nomes completos, Microsoft.Quantum.Intrinsic.H(<qubit>)
Microsoft.Quantum.Measurement.MResetZ(<qubit>) , mas fazer isso sempre levaria a um código muito confuso.
Em vez disso, as instruções open permitem que os chamadores sejam referenciados com uma abreviação mais
concisa, como fizemos no corpo da operação acima. Portanto, o arquivo Q# completo que contém nossa
operação consistiria em definir nosso namespace, abrir os namespaces para os chamadores que a operação usa
e, em seguida, nossa operação:

namespace NamespaceName {
open Microsoft.Quantum.Intrinsic; // for the H operation
open Microsoft.Quantum.Measurement; // for MResetZ

operation MeasureSuperposition() : Result {


use q = Qubit();
H(q);
return MResetZ(q);

}
}

NOTE
Os namespaces também podem ser com alias quando abertos, o que é útil se os nomes ou tipos de chamadores em dois
namespaces entrarem em conflito. Por exemplo, poderíamos usar
open Microsoft.Quantum.Instrinsic as NamespaceWithH; acima e, em seguida, chamar H via
NamespaceWithH.H(<qubit>) .

NOTE
Uma exceção a tudo isso é o namespace Microsoft.Quantum.Core , que sempre abre automaticamente. Portanto, os
chamadores como o Length sempre podem ser usados diretamente.

Execução no computador de destino


Agora o modelo de execução geral de um programa Q# fica claro.
Em primeiro lugar, o chamador específico a ser executado tem acesso a quaisquer outros chamadores e tipos
definidos no mesmo namespace. Ele também acessa aqueles de qualquer uma das Q# bibliotecas, mas eles
devem ser referenciados por meio de seu nome completo ou por meio do uso de open instruções descritas
acima.
O próprio chamador é executado em um computador de destino . Esses computadores de destino podem ser
hardwares de Quantum reais ou vários simuladores disponíveis como parte do QDK. Para nossos objetivos, o
computador de destino mais útil é uma instância do simulador de status completo, QuantumSimulator , que
calcula o comportamento do programa como se ele estivesse sendo executado em um computador Quantum
sem ruído.
Até agora, descrevemos o que acontece quando um chamador específicoQ# está sendo executado.
Independentemente de Q# ser usado em um aplicativo autônomo ou com um programa host, esse processo
geral é mais ou menos o mesmo, ou seja, essa é a flexibilidade do QDK. As diferenças entre as maneiras de
chamar o kit de desenvolvimento Quantum, portanto, se revelam sobre comoQ#esse chamador é invocado para
ser executado e de que maneira todos os resultados são retornados. Mais especificamente, as diferenças giram
em torno de:
Indicando qual chamador Q# deve ser executado
Como possíveis argumentos do chamador são fornecidos
Especificando o computador de destino no qual executar o chamador
Como quaisquer resultados são retornados
Primeiro, discutimos como isso é feito com o Q#aplicativo autônomo do prompt de comando e, em seguida,
prosseguimos usando programas de host Python e C#. Deixamos o aplicativo autônomo de Q# notebooks
Jupyter por último, porque ao contrário dos três primeiros, a funcionalidade principal não é centralizada em um
arquivo local Q#.

NOTE
Embora não o ilustre nesses exemplos, uma semelhança entre os métodos de execução é que todas as mensagens
impressas dentro do programa Q# (por meio de Message ou DumpMachine , por exemplo) normalmente serão sempre
impressas no respectivo console.

Q# no prompt de comando
Uma das maneiras mais fáceis de começar a gravação dos programasQ# é evitar completamente a preocupação
com arquivos separados e uma segunda linguagem. O uso do Visual Studio Code ou do Visual Studio com a
extensão QDK permite um fluxo de trabalho contínuo no qual executamos oQ#chamador de apenas um único
arquivoQ#.
Para isso, vamos, em última análise, executar o programa entrando
dotnet run

no prompt de comando. O fluxo de trabalho mais simples acontece quando o local do diretório do terminal é o
mesmo que o arquivoQ#, que pode ser facilmente manipulado junto com Q# a edição de arquivos usando o
terminal integrado no vs Code, por exemplo. No entanto, o comando dotnet run aceita inúmeras opções e o
programa também pode ser executado de um local diferente, simplesmente fornecendo --project <PATH> com
o local do arquivo Q#.
Adicionar ponto de entrada ao arquivoQ#
A maioria dos arquivos Q# conterá mais de um chamador, por isso, precisamos permitir que o compilador saiba
qual pode ser executado quando fornecemos o comando dotnet run . Isso é feito com uma simples alteração no
Q# do próprio arquivo: - Adicione uma linha com @EntryPoint() diretamente antes do chamador.
Nosso arquivo acima se tornaria

namespace NamespaceName {
open Microsoft.Quantum.Intrinsic; // for the H operation
open Microsoft.Quantum.Measurement; // for MResetZ

@EntryPoint()
operation MeasureSuperposition() : Result {
use q = Qubit();
H(q);
return MResetZ(q);

}
}

Agora, uma chamada de dotnet run do prompt de comando executa MeasureSuperposition e o valor retornado
é impresso diretamente no terminal. Portanto, você verá One e/ou Zero impresso.
Observe que não importa se você tiver mais chamadores definidos abaixo, somente MeasureSuperposition será
executado. Além disso, não tem problema se o chamador incluir comentários de documentação antes de sua
declaração, o atributo @EntryPoint() pode ser simplesmente colocado acima deles.
Argumentos para o chamador
Até agora, consideramos apenas uma operação que não usa entradas. Suponha que você queira executar uma
operação semelhante, mas em vários qubits. O número de qubits é fornecido como um argumento. Essa
operação poderia ser gravada de maneira que

operation MeasureSuperpositionArray(n : Int) : Result[] {


use qubits = Qubit[n]; // allocate a register of n qubits
ApplyToEach(H, qubits); // apply H to each qubit in the register
return ForEach(MResetZ, qubits); // perform MResetZ on each qubit, returns the resulting array
}
}

o valor retornado seja uma matriz dos resultados da medição. Observe que ApplyToEach e ForEach estão nos
Microsoft.Quantum.Canon Microsoft.Quantum.Arrays namespaces e, exigindo instruções adicionais open para
cada um.
Se movermos o atributo @EntryPoint() para preceder essa nova operação (observe que só pode haver uma
linha em um arquivo), tentar executá-la com simplesmente dotnet run resulta em uma mensagem de erro que
indica quais opções de linha de comando adicionais são necessárias e como expressá-las.
O formato geral para a linha de comando é realmente dotnet run [options] , e os argumentos que podem ser
chamador são fornecidos lá. Nesse caso, o argumento n está ausente e mostra que precisamos fornecer a
opção -n <n> . Para executar o MeasureSuperpositionArray para n=4 qubits, portanto, usamos

dotnet run -n 4

produzindo uma saída semelhante a

[Zero,One,One,One]

É claro que isso se estende a vários argumentos.

NOTE
Os nomes de argumentos definidos em camelCase são ligeiramente alterados pelo compilador para serem aceitos como
entradasQ#. Por exemplo, se em vez de n , usamos o nome numQubits acima, essa entrada seria fornecida na linha de
comando via --num-qubits 4 em vez de -n 4 .

A mensagem de erro também fornece outras opções que podem ser usadas, incluindo como alterar o
computador de destino.
Computadores de destino diferentes
Como as saídas de nossas operações até agora têm sido os resultados esperados de sua ação em qubits reais,
fica claro que o computador de destino padrão da linha de comando é o simulador de Quantum de estado
completo, QuantumSimulator . No entanto, podemos instruir os chamadores a serem executados em um
computador de destino específico com a opção --simulator (ou a abreviação -s ).
Por exemplo, poderíamos executá-lo em ResourcesEstimator :

dotnet run -n 4 -s ResourcesEstimator

A saída impressa é, então

Metric Sum
CNOT 0
QubitClifford 4
R 0
Measure 4
T 0
Depth 0
Width 4
BorrowedWidth 0

Para obter detalhes sobre o que essas métricas indicam, consulte Avaliador de recursos: métricas relatadas.
Resumo de execução de linha de comando
Opções relacionadas aos Q# dotnet run

Como mencionamos brevemente acima com a opção --project , o dotnet run comando também aceita
opções não relacionadas aos argumentos que podem ser chamador Q#. Se fornecer ambos os tipos de opções,
as opções específicas dotnet deverão ser fornecidas primeiro, seguidas por um delimitador -- e, em seguida,
as opções específicasQ#. Por exemplo, a especificação de um caminho junto com um número qubits para a
operação acima seria executada via dotnet run --project <PATH> -- -n <n> .

Q# com programas de host


Com o arquivoQ# em mãos, uma alternativa para chamar uma operação ou função diretamente do prompt de
comando é usar um programa de host em outra linguagem clássica. Especificamente, isso pode ser feito com
Python ou uma linguagem .NET, como C# ou F# (para fins de brevidade, só detalharemos C# aqui). Um pouco
mais de configuração é necessário para habilitar a interoperabilidade, mas esses detalhes podem ser
encontrados nos Guias de instalação.
Resumindo, a situação agora inclui um arquivo de programa host (por exemplo, *.py ou *.cs ) no mesmo
local que o nosso arquivoQ#. Agora é o programa host que é executado e, enquanto está em execução, ele pode
chamar Q# operações e funções Q#específicas do arquivo. O núcleo da interoperabilidade se baseia no
compilador Q#, tornando o conteúdo do Q# arquivo acessível ao programa host para que ele possa ser
chamado.
Um dos principais benefícios do uso de um programa de host é que os dados clássicos retornados pelo
programa Q# podem ser processados ainda mais no idioma do host. Isso pode consistir em um processamento
de dados avançado (por exemplo, algo que não pode ser executado internamente no Q#) e, em seguida, chamar
Q# ações adicionais com base nesses resultados ou algo tão simples quanto plotar os resultadosQ#.
O esquema geral é mostrado aqui e discutimos as implementações específicas para Python e C# abaixo. Um
exemplo que usa um programa de host F# pode ser encontrado em exemplos de interoperabilidade .net.
NOTE
O @EntryPoint() atributo usado para Q# aplicativos não pode ser usado com programas de host. Um erro será gerado
se estiver presente no arquivo Q# que está sendo chamado por um host.

Para trabalhar com diferentes programas de host, não há nenhuma alteração necessária para um arquivo *.qs
Q#. As implementações do programa host a seguir funcionam com o mesmo arquivoQ#:

namespace NamespaceName {
open Microsoft.Quantum.Intrinsic; // contains H
open Microsoft.Quantum.Measurement; // MResetZ
open Microsoft.Quantum.Canon; // ApplyToEach
open Microsoft.Quantum.Arrays; // ForEach

operation MeasureSuperposition() : Result {


use q = Qubit();
H(q);
return MResetZ(q);

operation MeasureSuperpositionArray(n : Int) : Result[] {


use qubits = Qubit[n];
ApplyToEach(H, qubits);
return ForEach(MResetZ, qubits);

}
}

Selecione a guia correspondente ao seu idioma de host de interesse.

Python
C#

Um programa de host do Python é construído da seguinte maneira:


1. Importe o módulo qsharp , que registra o carregador de módulo para Q# interoperabilidade. Isso
permite que Q# os namespaces apareçam como módulos Python, dos quais podemos "importar" Q#
chamadores. Observe que, tecnicamente, não são os Q# próprios chamadores que são importados, mas,
em vez disso, os stubs do Python que permitem chamá-los. Eles se comportam como objetos de classes
do Python. Usamos métodos nesses objetos para especificar os computadores de destino para os quais
enviamos a operação ao executar o programa.
2. Importe os chamadores Q# que invocaremos diretamente---nesse caso, MeasureSuperposition e
MeasureSuperpositionArray .

import qsharp
from NamespaceName import MeasureSuperposition, MeasureSuperpositionArray

Com o módulo qsharp importado, você também pode importar os chamadores diretamente dos Q#
namespaces da biblioteca.
3. Entre qualquer outro código Python, agora é possível chamar aqueles que podem ser chamadores em
computadores de destino específicos e atribuir seus retornos a variáveis (se eles retornarem um valor)
para uso posterior.
Especificando computadores de destino
Chamar uma operação a ser executada em um computador de destino específico é feito por meio de métodos
de Python diferentes no objeto importado. Por exemplo, .simulate(<args>) , usa o QuantumSimulator para
executar a operação, enquanto .estimate_resources(<args>) faz isso no ResourcesEstimator .
Passando entradas para Q#
Os argumentos para o chamador Q# devem ser fornecidos na forma de um argumento de palavra-chave, em
que a palavra-chave é o nome do argumento na definição que pode ser chamador Q#. Ou seja,
MeasureSuperpositionArray.simulate(n=4) é válido, enquanto MeasureSuperpositionArray.simulate(4) geraria um
erro.
Portanto, o programa de host do Python

import qsharp
from NamespaceName import MeasureSuperposition, MeasureSuperpositionArray

single_qubit_result = MeasureSuperposition.simulate()
single_qubit_resources = MeasureSuperposition.estimate_resources()

multi_qubit_result = MeasureSuperpositionArray.simulate(n=4)
multi_qubit_resources = MeasureSuperpositionArray.estimate_resources(n=4)

print('Single qubit:\n' + str(single_qubit_result))


print(single_qubit_resources)

print('\nMultiple qubits:\n' + str(multi_qubit_result))


print(multi_qubit_resources)

resulta em uma saída como algo semelhante ao seguinte:

Single qubit:
1
{'CNOT': 0, 'QubitClifford': 1, 'R': 0, 'Measure': 1, 'T': 0, 'Depth': 0, 'Width': 1, 'BorrowedWidth': 0}

Multiple qubits:
[0, 1, 1, 1]
{'CNOT': 0, 'QubitClifford': 4, 'R': 0, 'Measure': 4, 'T': 0, 'Depth': 0, 'Width': 4, 'BorrowedWidth': 0}

Usando código Q# de outros projetos ou pacotes


Por padrão, o comando import qsharp carrega todos os arquivos .qs na pasta atual e torna suas operações e
funções Q# disponíveis para uso de dentro do script Python.
Para carregar o código Q# de outra pasta, a qsharp.projects API pode ser usada para adicionar uma referência
a um arquivo .csproj para um projeto Q# (ou seja, um projeto que faz referência a Microsoft.Quantum.Sdk ).
Esse comando compilará todos os arquivos .qs na pasta que contém o .csproj e suas subpastas. Ele também
carregará recursivamente todos os pacotes referenciados por meio PackageReference Q# do ou projetos
referenciados por meio ProjectReference desse arquivo .csproj .
Por exemplo, o seguinte código Python importa um projeto externo, referenciando seu caminho relativo à pasta
atual e invoca uma de suas operaçõesQ#:

import qsharp
qsharp.projects.add("../qrng/Qrng.csproj")
from Qrng import SampleQuantumRandomNumberGenerator
print(f"Qrng result: {SampleQuantumRandomNumberGenerator.simulate()}")

Isso resulta em uma saída como algo semelhante ao seguinte:


Adding reference to project: ../qrng/Qrng.csproj
Qrng result: 0

Para carregar pacotes externos que contenham código Q#, use a qsharp.packages API.
Se o código Q# na pasta atual depender de projetos ou pacotes externos, você poderá ver erros durante a
execução import qsharp , já que as dependências ainda não foram carregadas. Para carregar os pacotes ou
projetos externos necessários Q# durante o comando import qsharp , verifique se a pasta com o script Python
contém um arquivo .csproj que faz referência a Microsoft.Quantum.Sdk . Nesse caso .csproj , adicione a
propriedade <IQSharpLoadAutomatically>true</IQSharpLoadAutomatically> ao <PropertyGroup> . Isso instruirá IQ#
a carregar recursivamente qualquer ProjectReference item ou PackageReference encontrado no .csproj
import qsharp comando.

Por exemplo, aqui está um arquivo .csproj simples que faz com que eu Q# carregue o pacote
Microsoft.Quantum.Chemistry automaticamente:

<Project Sdk="Microsoft.Quantum.Sdk/0.12.20072031">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
<IQSharpLoadAutomatically>true</IQSharpLoadAutomatically>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Quantum.Chemistry" Version="0.12.20072031" />
</ItemGroup>
</Project>

NOTE
Atualmente, essa propriedade <IQSharpLoadAutomatically> personalizada é exigida por hosts Python, mas no futuro,
isso pode se tornar o comportamento padrão para um arquivo .csproj localizado na mesma pasta que o script Python.

NOTE
Atualmente, a <QsharpCompile> configuração no .csproj é ignorada por hosts do Python e todos os .qs arquivos
na pasta do .csproj (incluindo subpastas) são carregados e compilados. O suporte para configurações .csproj será
melhorado no futuro (consulte iqsharp # 277 para obter mais detalhes).

Q# notebooks Jupyter
Q# Os notebooks Jupyter usam o kernel I Q#, que permite que você defina, compile e execute os chamadores
Q# em um único notebook, tudo junto com instruções, observações e outros conteúdos. Isso significa que,
embora seja possível importar e usar o conteúdo dos arquivos *.qs Q#, eles não são necessários no modelo
de execução.
Aqui, detalharemos como executar as operações Q# definidas acima, mas uma introdução mais ampla ao uso de
Q# notebooks Jupyter é fornecida em introdução aos Q# notebooks Jupyter.
Definindo operações
Em um Q# Jupyter Notebook, você insere o código Q# da mesma forma que faria dentro do namespace de um
arquivoQ#.
Portanto, podemos habilitar o acesso a chamadores a partir das Q# bibliotecas padrão com instruções open
para seus respectivos namespaces. Após a execução de uma célula com tal instrução, as definições desses
namespaces estão disponíveis em todo o espaço de trabalho.

NOTE
Os chamadores de Microsoft. Quantum. intrínseca e Microsoft. Quantum. Canon (por exemplo, H e ApplyToEach )
ficam automaticamente disponíveis para operações definidas nas células de Q# notebooks Jupyter. No entanto, isso não é
verdadeiro para o código originado de arquivos Q# externos (um processo mostrado em introdução a Q#e notebooks
Jupyter ).

Da mesma forma, a definição de operações requer apenas a gravação do códigoQ# e a execução da célula.

Em seguida, a saída lista essas operações, que podem ser chamadas a partir de células futuras.
Computadores de destino
A funcionalidade para executar operações em computadores de destino específicos é fornecida por meio de IQ#
comandos mágicos. Por exemplo, utiliza %simulate o QuantumSimulator e %estimate usa o ResourcesEstimator :

Passando entradas para funções e operações


Para informar as entradas para as operações Q#, os argumentos podem ser passados como pares key=value
para executar o comando mágico. Portanto, para executar MeasureSuperpositionArray com quatro qubits,
podemos executar %simulate MeasureSuperpositionArray n=4 :

Esse padrão pode ser usado de forma semelhante com %estimate e outros comandos executáveis.
Como usar o código Q# de outros projetos ou pacotes
Por padrão, um Jupyter Notebook Q# carrega todos os arquivos .qs na pasta atual e torna suas operações e
funções Q# disponíveis para uso de dentro do notebook. O %who comando mágico lista todas as operações e
funções Q# disponíveis no momento.
Para carregar o código Q# de outra pasta, o %project comando mágico pode ser usado para adicionar uma
referência a um arquivo .csproj para um projeto Q# (ou seja, um projeto que faz referência a
Microsoft.Quantum.Sdk ). Esse comando compilará todos os arquivos .qs na pasta que contém o .csproj ( e
subpastas). Ele também carregará recursivamente todos os pacotes referenciados por meio PackageReference
Q# do ou projetos referenciados por meio ProjectReference desse arquivo .csproj .
Por exemplo, as células a seguir simulam uma operação Q# de um projeto externo, onde o caminho do projeto é
referenciado em relação à pasta atual:

Para carregar pacotes externos que contenham código Q#, use o %package comando mágico. Carregar um
pacote também disponibilizará qualquer comando mágico personalizado ou exibirá codificadores que estejam
presentes em quaisquer conjuntos que fazem parte do pacote.
Para carregar pacotes externos ou projetosQ# no período de inicialização do notebook, verifique se a pasta do
notebook contém um arquivo .csproj que faz referência a Microsoft.Quantum.Sdk . No caso .csproj , adicione
a propriedade <IQSharpLoadAutomatically>true</IQSharpLoadAutomatically> ao <PropertyGroup> . Isso instruiráQ#
I a carregar recursivamente qualquer itens ProjectReference ou PackageReference encontrados no .csproj
período de carregamento do notebook.
Por exemplo, aqui está um arquivo .csproj simples que faz com que IQ# carregue o pacote
Microsoft.Quantum.Chemistry automaticamente:
<Project Sdk="Microsoft.Quantum.Sdk/0.12.20072031">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
<IQSharpLoadAutomatically>true</IQSharpLoadAutomatically>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Quantum.Chemistry" Version="0.12.20072031" />
</ItemGroup>
</Project>

NOTE
Atualmente, essa propriedade <IQSharpLoadAutomatically> personalizada é exigida por hosts Q# do Jupyter
Notebook, mas no futuro, isso pode se tornar o comportamento padrão para um arquivo .csproj localizado na mesma
pasta que o arquivo do notebook.

NOTE
Atualmente, a <QsharpCompile> configuração no .csproj é ignorada por hosts Q# do Jupyter Notebook e todos os
arquivos .qs na pasta do .csproj (incluindo subpastas) são carregados e compilados. O suporte para .csproj
configurações será melhorado no futuro (consulte iqsharp # 277 para obter mais detalhes).
Testando e depurando
19/05/2021 • 11 minutes to read

Assim como acontece com a programação clássica, é essencial poder verificar se os programas quantum atuam
como o pretendido e podem diagnosticar o comportamento incorreto. Nesta seção, abordaremos as
ferramentas oferecidas pelo Q# para testar e depurar programas quantum.

Testes de Unidade
Uma abordagem comum para testar programas clássicos é escrever programas pequenos chamados de testes
de unidade, que executam o código em uma biblioteca e comparam sua saída com alguma saída esperada. Por
exemplo, você pode garantir que Square(2) retorne 4 , desde que você saiba a priori que $2^2 = $4.
Q# dá suporte à criação de testes de unidade para programas quantum e que podem ser executados como
testes dentro da estrutura de teste de unidade xUnit.
Criar um projeto de teste
Visual Studio 2019
Linha de comando/Visual Studio Code

Abra o Visual Studio 2019. Vá para o menu Arquivo e selecione Novo > Projeto... . No canto superior direito,
pesquise Q# e selecione o modelo Projeto de Teste Q# .
Seu novo projeto tem um só arquivo Tests.qs , que fornece um local conveniente para definir novos testes de
unidade Q#. Inicialmente, esse arquivo contém um teste de unidade de exemplo AllocateQubit que verifica se
um qubit alocado recentemente está no estado $\ket{0}$ e imprime uma mensagem:

@Test("QuantumSimulator")
operation AllocateQubit () : Unit {

use qubit = Qubit();


AssertMeasurement([PauliZ], [qubit], Zero, "Newly allocated qubit must be in the |0 state.");

Message("Test passed");
}

Qualquer operação Q# ou função que usa um argumento de tipo Unit e retorna Unit pode ser marcada como
um teste de unidade por meio do atributo @Test("...") . No exemplo anterior, o argumento para esse atributo,
"QuantumSimulator" , especifica o destino no qual o teste é executado. É possível executar um só teste em vários
destinos. Por exemplo, adicione um atributo @Test("ResourcesEstimator") antes de AllocateQubit .

@Test("QuantumSimulator")
@Test("ResourcesEstimator")
operation AllocateQubit () : Unit {
...

Salve o arquivo e execute todos os testes. Agora deve haver dois testes de unidade, um em que AllocateQubit é
executado no QuantumSimulator e outro em que ele é executado no ResourcesEstimator .
O compilador Q# reconhece os destinos internos "QuantumSimulator" , "ToffoliSimulator" e
"ResourcesEstimator"como destinos de execução válidos para testes de unidade. Também é possível especificar
qualquer nome totalmente qualificado para definir um destino de execução personalizado.
Como executar testes de unidade Q#
Visual Studio 2019
Linha de comando/Visual Studio Code

Como uma configuração por solução realizada uma só vez, vá para o menu Teste e selecione Configurações
do Teste > Arquitetura do Processador Padrão > X64 .

TIP
A configuração de arquitetura de processador padrão para o Visual Studio é armazenada no arquivo ( .suo ) de opções
de solução para cada solução. Se você excluir esse arquivo, precisará selecionar X64 como a arquitetura do processador
novamente.

Compile o projeto, abra o menu testar e selecione Windows > Gerenciador de Testes . AllocateQubit é
exibido na lista de testes no grupo de Testes Não Executados . Selecione Executar ETudo ou executar este
teste individual.
A função intrínseca Message function tem o tipo (String -> Unit) e habilita a criação de mensagens de
diagnóstico.

Visual Studio 2019


Linha de comando/Visual Studio Code

Depois de executar um teste no Test Explorer e clicar no nome do teste, um painel é exibido com informações
sobre a execução de teste: status de aprovação/falha, tempo decorrido e um link para a saída. Clique em Saída
para abrir a saída de teste em uma nova janela.

Fatos e declarações
Como as funções no Q# não têm efeitos colaterais lógicos, você nunca pode observar, de dentro de um
programa Q#, nenhum outro tipo de efeito da execução de uma função cujo tipo de saída seja a tupla vazia () .
Ou seja, um computador de destino pode optar por não executar nenhuma função que retorna () com a
garantia de que essa omissão não modificará o comportamento de nenhum código a seguir Q#. Esse
comportamento faz com que as funções retornem () (como Unit ) uma ferramenta útil para inserir asserções
e depurar a lógica em programas Q#.
Vamos considerar um exemplo simples:
function PositivityFact(value : Double) : Unit
{
if value <= 0
{
fail "Expected a positive number.";
}
}

Aqui, a palavra-chave fail indica que a computação não deve continuar e gera uma exceção no computador
de destino que executa o programa Q#. Por definição, uma falha desse tipo não pode ser observada de dentro
do Q#, pois o computador de destino não executa mais o código Q# depois de atingir uma instrução fail .
Portanto, se continuarmos após uma chamada para PositivityFact , poderemos ter certeza de que sua entrada
foi positiva.
Observe que podemos implementar o mesmo comportamento que PositivityFact usando a função Fact do
namespace Microsoft.Quantum.Diagnostics namespace:

Fact(value > 0, "Expected a positive number.");

As asserções, por outro lado, são usadas de maneira semelhante aos fatos, mas podem depender do estado do
computador de destino. De modo correspondente, elas são definidas como operações, enquanto os fatos são
definidos como funções (como no exemplo anterior). Para entender a distinção, considere o seguinte uso de um
fato em uma asserção:

operation AssertQubitsAreAvailable() : Unit


{
Fact(GetQubitsAvailableToUse() > 0, "No qubits were actually available");
}

Aqui, estamos usando a operação GetQubitsAvailableToUse operation para retornar o número de qubits
disponíveis para uso. Como isso depende do estado global do programa e de seu ambiente de execução, nossa
definição de AssertQubitsAreAvailable deve ser uma operação também. No entanto, podemos usar esse estado
global para gerar um valor simples Bool como entrada para a função Fact .
O prelúdio, desenvolvendo com base nessas ideias, oferece duas declarações especialmente úteis,
AssertMeasurement operation e AssertMeasurementProbability operation, ambas modeladas como operações
em () . Essas asserções adotam um operador Pauli que descreve uma medição específica de interesse, um
registro quantum no qual uma medida é executada e um resultado hipotético. Os computadores de destino que
trabalham por simulação não são vinculados pelo teorema sem clonagem e podem executar essas medições
sem perturbar o registro que passa para tais asserções. Um simulador pode, assim, de modo semelhante à
função PositivityFact anterior, interromper a computação se o resultado hipotético não for observado na
prática:

use register = Qubit();

H(register);
AssertMeasurement([PauliX], [register], Zero);
// Even though we do not have access to states in Q#,
// we know by the anthropic principle that the state
// of register at this point is |+ .

Em hardware quantum físico, em que o teorema sem clonagem impede o exame de um estado quantum, as
operações AssertMeasurement e AssertMeasurementProbability e simplesmente retornam () sem nenhum
outro efeito.
O namespace Microsoft.Quantum.Diagnostics namespace fornece várias outras funções da família Assert , com
as quais você pode verificar condições mais avançadas.

Funções de despejo
Para ajudar a solucionar problemas de programas quantum, o Microsoft.Quantum.Diagnostics namespace
namespace oferece duas funções que podem ser despejadas em um arquivo o status atual do computador de
destino: DumpMachine function e DumpRegister function. A saída gerada de cada uma depende do computador
de destino.
DumpMachine
O simulador quantum completo distribuído como parte do Quantum development kit grava no arquivo a
função de ciclo de todo o sistema quantum, como uma matriz unidimensional de números complexos, na qual
cada elemento representa a amplitude da probabilidade de medir o estado de base computacional $\ket{n}$, em
que $\ket{n} = \ket{b_{n-1}...b_1b_0}$ para bits ${b_i}$. Por exemplo, em um computador com apenas dois
qubits alocados e no estado quantum $$ \begin{align} \ket{\psi} = \frac{1}{\sqrt{2}} \ket{00} - \frac{(1 + i)}{2}
\ket{10}, \end{align} $$, chamar DumpMachine function gera esta saída:

# wave function for qubits with ids (least to most significant): 0;1
∣0 : 0.707107 + 0.000000 i == ********** [ 0.500000 ] --- [ 0.00000 rad ]
∣1 : 0.000000 + 0.000000 i == [ 0.000000 ]
∣2 : -0.500000 + -0.500000 i == ********** [ 0.500000 ] / [ -2.35619 rad ]
∣3 : 0.000000 + 0.000000 i == [ 0.000000 ]

A primeira linha fornece um comentário com as IDs dos qubits correspondente em sua ordem significativa. O
restante das linhas descreve a amplitude de probabilidade de medir o vetor de estado base $\ket{n}$ nos
formatos cartesiano e polar. Em detalhes para a primeira linha:
∣0 : esta linha corresponde ao estado de base computacional 0
0.707107 + 0.000000 i : a amplitude de probabilidade no formato cartesiano.
== : o sinal equal separa as representações equivalentes.
********** : uma representação gráfica da magnitude, o número de * é proporcional à probabilidade de
medir esse vetor de estado.
[ 0.500000 ] : o valor numérico da magnitude
--- : uma representação gráfica da fase da amplitude (confira a saída a seguir).
[ 0.0000 rad ] : o valor numérico da fase (em radianos).

Tanto a magnitude quanto a fase são exibidas com uma representação gráfica. A representação de magnitude é
direta: ela mostra uma barra de * , quanto maior a probabilidade, maior será o tamanho da barra. Para a fase,
mostramos os seguintes símbolos para representar o ângulo com base em intervalos:
[ -π/16, π/16) ---
[ π/16, 3π/16) /-
[ 3π/16, 5π/16) /
[ 5π/16, 7π/16) +/
[ 7π/16, 9π/16) ↑
[ 8π/16, 11π/16) \-
[ 7π/16, 13π/16) \
[ 7π/16, 15π/16) +\
[15π/16, 19π/16) ---
[17π/16, 19π/16) -/
[19π/16, 21π/16) /
[21π/16, 23π/16) /+
[23π/16, 25π/16) ↓
[25π/16, 27π/16) -\
[27π/16, 29π/16) \
[29π/16, 31π/16) \+
[31π/16, π/16) ---

Os seguintes exemplos mostram DumpMachine para alguns estados comuns:


∣0

# wave function for qubits with ids (least to most significant): 0


∣0 : 1.000000 + 0.000000 i == ******************** [ 1.000000 ] --- [ 0.00000 rad ]
∣1 : 0.000000 + 0.000000 i == [ 0.000000 ]

∣1

# wave function for qubits with ids (least to most significant): 0


∣0 : 0.000000 + 0.000000 i == [ 0.000000 ]
∣1 : 1.000000 + 0.000000 i == ******************** [ 1.000000 ] --- [ 0.00000 rad ]

∣+

# wave function for qubits with ids (least to most significant): 0


∣0 : 0.707107 + 0.000000 i == ********** [ 0.500000 ] --- [ 0.00000 rad ]
∣1 : 0.707107 + 0.000000 i == ********** [ 0.500000 ] --- [ 0.00000 rad ]

∣-

# wave function for qubits with ids (least to most significant): 0


∣0 : 0.707107 + 0.000000 i == ********** [ 0.500000 ] --- [ 0.00000 rad ]
∣1 : -0.707107 + 0.000000 i == ********** [ 0.500000 ] --- [ 3.14159 rad ]

NOTE
A ID de um qubit é atribuída no runtime e não é necessariamente alinhada à ordem na qual o qubit foi alocado ou sua
posição em um registro qubit.

Visual Studio 2019


Linha de comando/Visual Studio Code
TIP
Você pode localizar uma ID do qubit no Visual Studio colocando um ponto de interrupção em seu código e inspecionando
o valor de uma variável qubit, por exemplo:

o qubit com o índice 0 em register2 tem id= 3 , o qubit com índice 1 tem id= 2 .

Como DumpMachine function faz parte do namespace Microsoft.Quantum.Diagnostics namespace, você deve
adicionar uma instrução open para acessá-lo:

namespace Samples {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;

operation Operation () : Unit {


use qubits = Qubit[2];
H(qubits[1]);
DumpMachine("dump.txt");

}
}

DumpRegister
O DumpRegister function funciona como DumpMachine function, exceto pelo fato de que ele também usa uma
matriz de qubits para limitar a quantidade de informações apenas às relevantes para o qubits correspondente.
Assim como acontece com DumpMachine function, as informações geradas por DumpRegister function
dependem do computador de destino. Para o simulador de quantum de estado completo, ele grava no arquivo a
função de ciclo até uma fase global do subsistema quantum gerada pelo qubits fornecido no mesmo formato
que DumpMachine function. Por exemplo, pegar novamente um computador com apenas dois qubits alocados e
no estado Quantum $$ \begin{align} \ket{\psi} = \frac{1}{\sqrt{2}} \ket{00} -\frac{(1 + i)}{2} \ket{10} =-e^{-
i\pi/4} ((\frac{1}{\sqrt{2}} \ket{0} -\frac{(1 + i)}{2} \ket{1}) \otimes \frac{-(1 + i)}{\sqrt{2}} \ket{0}), \end{align} $$
chamando DumpRegister function para qubit[0] gera esta saída:
# wave function for qubits with ids (least to most significant): 0
∣0 : -0.707107 + -0.707107 i == ******************** [ 1.000000 ] / [ -2.35619 rad ]
∣1 : 0.000000 + 0.000000 i == [ 0.000000 ]

e chamar DumpRegister function para qubit[1] gera esta saída:

# wave function for qubits with ids (least to most significant): 1


∣0 : 0.707107 + 0.000000 i == *********** [ 0.500000 ] --- [ 0.00000 rad ]
∣1 : -0.500000 + -0.500000 i == *********** [ 0.500000 ] / [ -2.35619 rad ]

Em geral, o estado de um registro envolvido com outro registro é um estado misto, em vez de um estado puro.
Nesse caso, o DumpRegister function gera a seguinte mensagem:

Qubits provided (0;) are entangled with some other qubit.

O seguinte exemplo mostra como você pode usar o DumpRegister function e o DumpMachine function em seu
código Q#:

namespace app
{
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;

operation Operation () : Unit {

use qubits = Qubit[2];


X(qubits[1]);
H(qubits[1]);
R1Frac(1, 2, qubits[1]);

DumpMachine("dump.txt");
DumpRegister("q0.txt", qubits[0..0]);
DumpRegister("q1.txt", qubits[1..1]);

ResetAll(qubits);

}
}

Depuração
Além de funções e operações Assert e Dump , o Q# dá suporte a um subconjunto de funcionalidades de
depuração padrão do Visual Studio: configurar pontos de interrupção de linha, percorrer código usando F10 e
inspecionar valores de variáveis clássicas são possíveis ao executar seu código no simulador.
A depuração no Visual Studio Code aproveita os recursos de depuração fornecidos pela extensão C# para Visual
Studio Code do OmniSharp e requer a instalação da versão mais recente.
Q# Language
15/04/2021 • 2 minutes to read

Q# is part of Microsoft's Quantum Development Kit and provides rich IDE support and tools for program
visualization and analysis. Our goal is to support the development of future large-scale applications while
supporting user's first efforts in that direction on current quantum hardware.
The type system permits Q# programs to safely interleave and naturally represent the composition of classical
and quantum computations. A Q# program may express arbitrary classical computations based on quantum
measurements that execute while qubits remain live, meaning they are not released and maintain their state.
Even though the full complexity of such computations requires further hardware development, Q# programs
can be targeted to run on various quantum hardware backends in Azure Quantum.
Q# is a stand-alone language offering a high level of abstraction. There is no notion of a quantum state or a
circuit; instead, Q# implements programs in terms of statements and expressions, much like classical
programming languages. Distinct quantum capabilities (such as support for functors and control-flow
constructs) facilitate expressing, for example, phase estimation and quantum chemistry algorithms.
Program execution
15/04/2021 • 2 minutes to read

The following program gives a first glimpse at how a Q# command-line application is implemented:

namespace Microsoft.Quantum.Samples {

open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Arrays as Array;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Diagnostics as Diagnostics;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Preparation;

operation ApplyQFT (reg : LittleEndian) : Unit


is Adj + Ctl {

let qs = reg!;
SwapReverseRegister(qs);

for (i in Array.IndexRange(qs)) {
for (j in 0 .. i-1) {
Controlled R1Frac([qs[i]], (1, i - j, qs[j]));
}
H(qs[i]);
}
}

@EntryPoint()
operation RunProgram(vector : Double[]) : Unit {

let n = Floor(Log(IntAsDouble(Length(vector))) / LogOf2());


if (1 <<< n != Length(vector)) {
fail "Length(vector) needs to be a power of two.";
}

let amps = Array.Mapped(ComplexPolar(_,0.), vector);


using (qs = Qubit[n]) {
let reg = LittleEndian(qs);

PrepareArbitraryState(amps, reg);
Message("Before QFT:");
Diagnostics.DumpRegister((), qs);

ApplyQFT(reg);
Message("After QFT:");
Diagnostics.DumpRegister((), qs);

ResetAll(qs);
}
}
}

The operation PrepareArbitraryState initializes a quantum state where the amplitudes for each basis state
correspond to the normalized entries of the specified vector. A quantum Fourier transformation (QFT) is then
applied to that state.
The corresponding project file to build the application is the following:
<Project Sdk="Microsoft.Quantum.Sdk/0.12.20070124">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

</Project>

The first line specifies the version number of the software development kit used to build the application, and line
4 indicates that the project is executable opposed to e.g. a library that cannot be invoked from the command
line.
To run the application, you will need to install .NET Core. Then put both files in the same folder and run
dotnet build <projectFile> , where <projectFile> is to be replaced with the path to the project file.

To run the program after having built it, run the command

dotnet run --no-build --vector 1. 0. 0. 0.

The output from this invocation shows that the amplitudes of the quantum state after application of the QFT are
evenly distributed and real. Note that the reason that we can so readily output the amplitudes of the state vector
is that the previous program is, by default, run on a full state simulator, which supports outputting the tracked
quantum state via DumpRegister for debugging purposes. The same would not be possible if we were to run it
on quantum hardware instead, in which case the two calls to DumpRegister wouldn't do anything. You can see
this by targeting the application to a particular hardware platform by adding the project property
<ExecutionTarget>honeywell.qpu</ExecutionTarget> after <PropertyGroup> .
Namespaces
01/05/2021 • 2 minutes to read

At its top-level, a Q# program consists of a set of namespaces. Aside from comments, namespaces are the only
top-level elements in a Q# program, and any other elements must reside within a namespace. Each file may
contain zero or more namespaces, and each namespace may span multiple files. Q# does not support nested
namespaces.
A namespace block consists of the keyword namespace , followed by the namespace name, and the content of
the block inside braces { } . Namespace names consist of a sequence of one or more legal symbols separated
by a dot ( . ). Double underscores ( __ ), double dots ( .. ), or an underscore followed by a dot ( _. ) are not
permitted since these character sequences are reserved. More precisely, a fully qualified name may not contain
such a sequence, and namespace names correspondingly cannot end with an underscore. While namespace
names may contain dots for better readability, Q# does not support relative references to namespaces. For
example, two namespaces Foo and Foo.Bar are unrelated, and there is no notion of a hierarchy. In particular,
for a function Baz defined in Foo.Bar , it is not possible to open Foo and then access that function via Bar.Baz
.
Within a namespace block, open directives precede any other namespace elements. Aside from open directives,
namespace blocks may contain operation, function, and type declarations. These may occur in any order and are
recursive by default, meaning they can be declared and used in any order and can call themselves; there is no
need for the declaration of a type or callable to precede its use.

Open Directives
By default, everything declared within the same namespace can be accessed without further qualification.
However, declarations in a different namespace can only be used by qualifying their name with the name of the
namespace they belong to or by opening that namespace before it is used, as shown in the following example.

namespace Microsoft.Quantum.Samples {

open Microsoft.Quantum.Arithmetic;
open Microsoft.Quantum.Arrays as Array;

// ...
}

The example uses an open directive to import all types and callables declared in the
Microsoft.Quantum.Artithmetic namespace. They can then be referred to by their unqualified name unless that
name conflicts with a declaration in the namespace block or another opened namespace.
To avoid typing out the full name while still distinguishing where certain elements come from, you can define an
alternative name, or alias, which is usually shorter, for a particular namespace. In this case, all types and callables
declared in that namespace can be qualified by the defined short name instead. In the previous example, this is
the case for the Microsoft.Quantum.Arrays namespace. A function IndexRange declared in
Microsoft.Quantum.Arrays , for example, can then be used via Array.IndexRange within that namespace block.

Defining namespace aliases is particularly helpful when combined with the code completion functionality
provided by the Q# extensions available for Visual Studio Code and Visual Studio. With the extension installed,
typing the namespace alias followed by a dot will show a list of all the available elements in that namespace that
are valid at the current location.
Whether you are opening a namespace or defining an alias, open directives need to precede any other
namespace elements and are valid throughout the namespace piece in that file only.
Type Declarations
01/05/2021 • 2 minutes to read

Q# supports user-defined types. User-defined types are similar to record types in F#; they are immutable but
support a copy-and-update construct.

User-defined types
User-defined types may contain both named and anonymous items. The following declaration within a
namespace, for example, defines a type Complex which has two named items Real and Imaginary , both of
type Double :

newtype Complex = (Real: Double, Imaginary : Double);

Any combination of named and unnamed items is supported, and inner items may also be named. For example,
the type Nested , defined as

newtype Nested = (Double, (ItemName : Int, String));

contains two anonymous items of type Double and String respectively, and a named item ItemName of type
Int .

You can access the contained items via their name or by deconstruction (for more information, see item access).
You can also access a tuple of all items where the shape matches the one defined in the declaration via the
unwrap operator.
User-defined types are useful for two reasons. First, as long as the libraries and programs that use the defined
types access items via their name rather than by deconstruction, the type can be extended to contain additional
items later on without breaking any library code. Because of this, accessing items via deconstruction is generally
discouraged.
Second, Q# allows you to convey the intent and expectations for a specific data type since there is no automatic
conversion between values of two user-defined types, even if their item types are identical.
For example, the arithmetic library includes quantum arithmetic operations for both big-endian and little-endian
quantum integers. It hence defines two types, BigEndian and LittleEndian , both of which contain a single
anonymous item of type Qubit[] :

newtype BigEndian = Qubit[];


newtype LittleEndian = Qubit[];

These types allow operations to specify whether they are written for big-endian or little-endian representations
and leverages the type system to ensure at compile-time that mismatched operands aren't allowed.
Type names must be unique within a namespace and may not conflict with operation and function names. Types
may not have circular dependencies in Q#; that is, defining something like a directly or indirectly recursive type
is not allowed. For example, the following construct will give a compilation error:
newtype Foo = (Foo, Int); // gives an error
newtype Bar = Baz; // gives an error
newtype Baz = Bar; // gives an error

User-defined constructors
Constructors for user-defined types are automatically generated by the compiler. Currently, it is not possible
to define a custom constructor, though this may be an addition to the language in the future.
Callable declarations
19/05/2021 • 3 minutes to read

Callable declarations, or callables, are declared at a global scope and publicly visible by default; that is, they can
be used anywhere in the same project and in a project that references the assembly in which they are declared.
Access modifiers allow you to restrict their visibility to the current assembly only, such that implementation
details can be changed later on without breaking code that relies on a specific library.
Q# supports two kinds of callables: operations and functions. The topic Operations and Functions elaborates on
the distinction between the two. Q# also supports defining templates; for example, type-parameterized
implementations for a certain callable. For more information, see Type parameterizations.

NOTE
Such type-parametrized implementations may not use any language constructs that rely on particular properties of the
type arguments; there is currently no way to express type constraints in Q#, or to define specialized implementations for
particular type arguments. However, it is conceivable to introduce a suitable mechanism, similar to type classes in Haskell,
for example, to allow for more expressiveness in the future.

Callables and functors


Q# allows specialized implementations for specific purposes; for example, operations in Q# can implicitly or
explicitly define support for certain functors, and along with it the specialized implementations to invoke when a
specific functor is applied to that callable.
A functor, in a sense, is a factory that defines a new callable implementation that has a specific relation to the
callable it was applied to. Functors are more than traditional higher-level functions in that they require access to
the implementation details of the callable they have been applied to. In that sense, they are similar to other
factories, such as templates. Correspondingly, they can be applied not just to callables, but templates as well.
The example program shown in Program implementation, for example, defines the operation ApplyQFT and the
operation RunProgram , which is used as an entry point. ApplyQFT takes a tuple-valued argument containing an
integer and a value of type LittleEndian and returns a value of type Unit . The annotation is Adj + Ctl in the
declaration of ApplyQFT indicates that the operation supports both the Adjoint and the Controlled functor.
(For more information, see Operation characteristics). If Unitary is an operation that has an adjoint and a
controlled specialization, the expression Adjoint Unitary accesses the specialization that implements the
adjoint of Unitary , and Controlled Unitary accesses the specialization that implements the controlled version
of Unitary . In addition to the original operation’s argument, the controlled version of an operation takes an
array of control qubits and then applies the original operation conditional on all of these control qubits being in
a |1 state.
In theory, an operation for which an adjoint version can be defined should also have a controlled version and
vice versa. In practice, however, it may be hard to develop an implementation for one or the other, especially for
probabilistic implementations following a repeat-until-success pattern. For that reason, Q# allows you to declare
support for each functor individually. However, since the two functors commute, an operation that defines
support for both also has to have an implementation (usually implicitly defined, meaning compiler-generated)
for when both functors are applied to the operation.
There are no functors that can be applied to functions, such that functions currently have exactly one body
implementation and no further specializations. For example, the declaration
function Hello (name : String) : String {
return $"Hello, {name}!";
}

is equivalent to

function Hello (name : String) : String {


body (...) {
return $"Hello, {name}!";
}
}

Here, body specifies that the given implementation applies to the default body of the function Hello , meaning
the implementation is invoked when no functors or other factory mechanisms have been applied prior to
invocation. The three dots in body (...) correspond to a compiler directive indicating that the argument items
in the function declaration should be copy and pasted into this spot.
The reasons behind explicitly indicating where the arguments of the parent callable declaration are to be copied
and pasted are twofold: one, it is unnecessary to repeat the argument declaration, and two, and more
importantly, it ensures that functors that require additional arguments, like the Controlled functor, can be
introduced in a consistent manner.
The same applies to operations; when there is exactly one specialization defining the implementation of the
default body, the additional wrapping of the form body (...){ <implementation> } may be omitted.

Recursion
Q# callables can be directly or indirectly recursive and can be declared in any order; an operation or function
may call itself, or it may call another callable that directly or indirectly calls the caller.
When running on quantum hardware, stack space may be limited, and recursions that exceed that stack space
limit result in a runtime error.
Specialization declarations
01/05/2021 • 4 minutes to read

As explained in the section about callable declarations, there is currently no reason to explicitly declare
specializations for functions. This topic applies to operations and elaborates on how to declare the necessary
specializations to support certain functors.
It is quite a common problem in quantum computing to require the adjoint of a given transformation. Many
quantum algorithms require both an operation and its adjoint to perform a computation. Q# employs symbolic
computation that can automatically generate the corresponding adjoint implementation for a particular body
implementation. This generation is possible even for implementations that freely mix classical and quantum
computations. There are, however, some restrictions that apply in this case. For example, auto-generation is not
supported for performance reasons if the implementation makes use of mutable variables. Moreover, each
operation called within the body generates the corresponding adjoint needs to support the Adjoint functor
itself.
Even though one cannot easily undo measurements in the multi-qubit case, it is possible to combine
measurements so that the applied transformation is unitary. In this case, it means that, even though the body
implementation contains measurements that on their own don't support the Adjoint functor, the body in its
entirety is adjointable. Nonetheless, auto-generating the adjoint implementation will fail in this case. For this
reason, it is possible to manually specify the implementation. The compiler automatically generates optimized
implementations for common patterns such as conjugations. Nonetheless, an explicit specialization may be
desirable to define a more optimized implementation by hand. It is possible to specify any one implementation
and any number of implementations explicitly.

NOTE
The correctness of such a manually specified implementation is not verified by the compiler.

In the following example, the declaration for an operation SWAP , which exchanges the state of two qubits q1
and q2 , declares an explicit specialization for its adjoint version and its controlled version. While the
implementations for Adjoint SWAP and Controlled SWAP are thus user-defined, the compiler still needs to
generate the implementation for the combination of both functors ( Controlled Adjoint SWAP , which is the same
as Adjoint Controlled SWAP ).
operation SWAP (q1 : Qubit, q2 : Qubit) : Unit
is Adj + Ctl {

body (...) {
CNOT(q1, q2);
CNOT(q2, q1);
CNOT(q1, q2);
}

adjoint (...) {
SWAP(q1, q2);
}

controlled (cs, ...) {


CNOT(q1, q2);
Controlled CNOT(cs, (q2, q1));
CNOT(q1, q2);
}
}

Auto-generation directives
When determining how to generate a particular specialization, the compiler prioritizes user-defined
implementations. This means that if an adjoint specialization is user-defined and a controlled specialization is
auto-generated, then the controlled adjoint specialization is generated based on the user-defined adjoint and
vice versa. In this case, both specializations are user-defined. As the auto-generation of an adjoint
implementation is subject to more limitation, the controlled adjoint specialization defaults to generating the
controlled specialization of the explicitly defined implementation of the adjoint specialization.
In the case of the SWAP implementation, the better option is to adjoint the controlled specialization to avoid
unnecessarily conditioning the execution of the first and the last CNOT on the state of the control qubits. Adding
an explicit declaration for the controlled adjoint version that specifies a suitable generation directive forces the
compiler to generate the controlled adjoint specialization based on the manually specified implementation of
the controlled version instead. Such an explicit declaration of a specialization that is to be generated by the
compiler takes the form

controlled adjoint invert;

and is inserted inside the declaration of SWAP . On the other hand, inserting the line

controlled adjoint distribute;

forces the compiler to generate the specialization based on the defined (or generated) adjoint specialization. See
this partial specialization inference proposal for more details.
For the operation SWAP , there is a better option. SWAP is self-adjoint, that is, it is its own inverse; the -defined
implementation of the adjoint merely calls the body of SWAP . You express this with the directive

adjoint self;

Declaring the adjoint specialization in this manner ensures that the controlled adjoint specialization that is
automatically inserted by the compiler merely invokes the controlled specialization.
The following generation directives exist and are valid:
SP EC IA L IZ AT IO N DIREC T IVE( S)

body specialization: -

adjoint specialization: self , invert

controlled specialization: distribute

controlled adjoint specialization: self , invert , distribute

That all generation directives are valid for a controlled adjoint specialization is not a coincidence; as long as
functors commute, the set of valid generation directives for implementing the specialization for a combination
of functors is always the union of the set of valid generators for each one.
In addition to the previously listed directives, the directive auto is always valid; it indicates that the compiler
should automatically pick a suitable generation directive. The declaration

operation DoNothing() : Unit {


body (...) { }
adjoint auto;
controlled auto;
controlled adjoint auto;
}

is equivalent to

operation DoNothing() : Unit


is Adj + Ctl { }

The annotation is Adj + Ctl in this example specifies the operation characteristics, which contain the
information about what functors a particular operation supports.
While for readability's sake, it is recommended that you annotate each operation with a complete description of
its characteristics, the compiler automatically inserts or completes the annotation based on explicitly declared
specializations. Conversely, the compiler also generates specializations that haven't been declared explicitly but
need to exist based on the annotated characteristics. We say the given annotation has implicitly declared these
specializations. The compiler automatically generates the necessary specializations if it can, picking a suitable
directive. Q# thus supports inference of both operation characteristics and existing specializations based on
(partial) annotations and explicitly defined specializations.
In a sense, specializations are similar to individual overloads for the same callable, with the caveat that certain
restrictions apply to which overloads you can declare.
Comments
01/05/2021 • 2 minutes to read

Comments begin with two forward slashes ( // ) and continue until the end of line. Such end-of-line comments
may appear anywhere in the source code. Q# does not currently support block comments.

Documentation Comments
Comments that begin with three forward slashes, /// , are treated specially by the compiler when they appear
before a type or callable declaration. In that case, their contents are taken as documentation for the defined type
or callable, as for other .NET languages.
Within /// comments, text to appear as a part of API documentation is formatted as Markdown, with different
parts of the documentation indicated by specially-named headers. As an extension to Markdown, cross-
references to operations, functions, and user-defined types in Q# can be included using @"<ref target>," where
<ref target> is replaced by the fully qualified name of the code object being referenced. Optionally, a
documentation engine may also support additional Markdown extensions.
For example:

/// # Summary
/// Given an operation and a target for that operation,
/// applies the given operation twice.
///
/// # Input
/// ## op
/// The operation to be applied.
/// ## target
/// The target to which the operation is to be applied.
///
/// # Type Parameters
/// ## 'T
/// The type expected by the given operation as its input.
///
/// # Example
/// ```Q#
/// // Should be equivalent to the identity.
/// ApplyTwice(H, qubit);
/// ```
///
/// # See Also
/// - Microsoft.Quantum.Intrinsic.H
operation ApplyTwice<'T>(op : ('T => Unit), target : 'T) : Unit {
op(target);
op(target);
}

Q# recognizes the following names as documentation comment headers.


Summar y : A short summary of a function or operation's behavior or the purpose of a type. The first
paragraph of the summary is used for hover information. It should be plain text.
Description : A description of a function or operation's behavior or the purpose of a type. The summary and
description are concatenated to form the generated documentation file for the function, operation, or type.
The description may contain in-line LaTeX-formatted symbols and equations.
Input : A description of the input tuple for an operation or function. May contain additional Markdown
subsections indicating each element of the input tuple.
Output : A description of the tuple returned by an operation or function.
Type Parameters : An empty section that contains one additional subsection for each generic type
parameter.
Named Items : A description of the named items in a user-defined type. May contain additional Markdown
subsections with the description for each named item.
Example : A short example of the operation, function, or type in use.
Remarks : Miscellaneous prose describing some aspect of the operation, function, or type.
See Also : A list of fully qualified names indicating related functions, operations, or user-defined types.
References : A list of references and citations for the documented item.
Statements
15/04/2021 • 2 minutes to read

Q# distinguishes between statements and expressions. Q# programs consist of a mixture of classical and
quantum computations and the implementation looks much like in any other classical programming language.
Some statements, such as the let and mutable bindings, are well-known from classical languages, while
others such as conjugations or qubit allocations are unique to the quantum domain.
The following statements are currently available in Q#:
Call Statement
A call statement consists of an operation or function call returning Unit . The invoked callable needs to
satisfy the requirements imposed by the current context. See this section for more details.
Return Statement
A return statement terminates the execution within the current callable context and returns control to the
caller. Any finalizing tasks are executed after the return value is evaluated but before control is returned.
See this section for more details.
Fail Statement
A fail statement aborts the execution of the entire program, collecting information about the current
program state before terminating in an error. It aggregates the collected information and presents it to
the user along with the message specified as part of the statement. See this section for more details.
Variable Declaration
Defines one or more local variables that will be valid for the remainder of the current scope, and binds
them to the specified values. Variables can be permanently bound or declared to be reassignable later on.
See this section for more details.
Variable Reassignment
Variables that have been declared as being reassignable can be rebound to contain different values. See
this section for more details.
Iteration
An iteration is a loop-like statement that during each iteration assigns the declared loop variables to the
next item in a sequence (a value of array or Range type) and executes a specified block of statements. See
this section for more details.
While Statement
If a specified condition evaluates to true , a block of statements is executed. The execution is repeated
indefinitely until the condition evaluates to false . See this section for more details.
Repeat Statement
Quantum-specific loop that breaks based on a condition. The statement consists of an initial block of
statements that is executed before a specified condition is evaluated. If the condition evaluates to false ,
an optional subsequent fixup -block is executed before entering the next iteration of the loop. The loop
terminates only once the condition evaluates to true . See this section for more details.
If Statement
The statement consists of one or more blocks of statements, each preceded by a boolean expression. The
first block for which the boolean expression evaluates to true is executed. Optionally, a block of
statements can be specified that is executed if none of the conditions evaluates to true . See this section
for more details.
Conjugation
A conjugations is a special quantum-specific statement, where a block of statements that applies a
unitary transformation to the quantum state is executed, followed by another statement block, before the
transformation applied by the first block is reverted again. In mathematical notation, conjugations
describe transformations of the form U†VU to the quantum state. See this section for more details.
Qubit Allocation
Instantiates and initializes qubits and/or arrays of qubits, and binds them to the declared variables.
Executes a block of statements. The instantiated qubits are available for the duration of the block, and will
be automatically released when the statement terminates. See this section for more details.
Visibility of Local Variables
13/05/2021 • 2 minutes to read

In general, symbol bindings become inoperative at the end of the statement block they occur in. The exceptions
to this rule are:
Bindings of the loop variables in a for loop are defined for the body of the loop, but not after the end of the
loop.
Bindings of allocated qubits in use - and borrow -statements are defined for the body of the allocation, but
not after the statement terminates. This only applies to use and borrow -statements that have an associated
statement block.
For repeat -statements, both blocks as well as the condition are treated as a single scope; i.e. symbols that
are bound in the body are accessible in the condition and in the fixup -block.
For loops, each iteration executes in its own scope, and all defined variables are bound anew for each iteration.
Bindings in outer blocks are visible and defined in inner blocks. A symbol may only be bound once per block; it
is illegal to define a symbol with the same name as another symbol that is accessible (no "shadowing").
The following sequences would be legal:

if (a == b) {
...
let n = 5;
... // n is 5
}
let n = 8;
... // n is 8

and

if (a == b) {
...
let n = 5;
... // n is 5
} else {
...
let n = 8;
... // n is 8
}
... // n is not bound to a value

The following sequences would be illegal:

let n = 5;
... // n is 5
let n = 8; // Error
...

and
let n = 8;
if (a == b) {
... // n is 8
let n = 5; // Error
...
}
...

This section gives further details on variable declarations and reassignments.


Call Statements
01/05/2021 • 2 minutes to read

Call statements are a very important part of any programming language. While operation and function calls,
much like partial applications, can be used as an expression anywhere as long as the returned value is of a
suitable type, they can also be used as statements if they return Unit . The usefulness of calling functions in this
form primarily lays in debugging, whereas such operation calls are one of the most common constructs in any
Q# program. At the same time, operations can only be called from within other operations and not from within
functions (for context, see also this section).
With callables being first-class values, call statements in a sense are the generic way of supporting patterns that
aren't common enough to merit their own dedicated language construct or dedicated syntax has not (yet) been
introduced for other reasons. Examples for library methods that serve exactly that purpose are ApplyIf , that
invokes an operation conditional on a classical bit being set, ApplyToEach , that applies a given operation to each
element in an array, and ApplyWithInputTransformation shown below to give to just a few.

operation ApplyWithInputTransformation<'TArg, 'TIn>(


fn : 'TIn -> 'TArg,
op : 'TArg => Unit,
input : 'TIn
) : Unit {

op(fn(input));
}

ApplyWithInputTransformation takes a function fn , an operation op , and an input value as argument, applies


the give function to the input, before invoking the given operation with the value returned form the function.
For the compiler to be able to auto-generate the specializations to support particular functors usually requires
that the called operations support those functors as well. The exception are calls in outer blocks of conjugations,
which always need to support the Adjoint functor but never need to support the Controlled functor, and self-
adjoint operations, which support the Adjoint functor without imposing any additional requirements on the
individual calls.
Discussion
More sophisticated generation directives may allow to further relax that requirement in the future.
Returns and Termination
13/05/2021 • 2 minutes to read

There are two statements available that conclude the execution of the current subroutine or the program; the
return - and the fail -statement. For callables that return any other type than Unit each execution path needs
to terminate either in a return - or a fail -statement

Return-Statement
The return -statement exits from the current callable and returns control to the callee. It changes the context of
the execution by popping a stack frame.
The statement always returns a value back to the context of the callee; it consists of the keyword return ,
followed by an expression of the appropriate type, and a terminating semicolon. The return value is evaluated
before any terminating actions are performed and control is returned. Such terminating actions include, e.g.,
cleaning up and releasing qubits that have been allocated within the context of the callable. When executing on a
simulator or validator, terminating actions often also include checks related to the state of those qubits, like, e.g.,
whether they are properly disentangled from all qubits that remain live.
The return -statement at the end of a callable that returns a Unit value may be omitted. In that case, control is
returned automatically when all statements have been executed and all terminating actions were performed.
Callables may contain multiple return -statements, one for each possible execution path, albeit the adjoint
implementation for operations containing multiple return -statements cannot be automatically generated.
For example,

return 1;

or

return ();

Fail-Statement
The fail -statement on the other hand aborts the computation entirely. It corresponds to a fatal error that was
not expected to happen as part of normal execution.
It consists of the keyword fail , followed by an expression of type String and a terminating semicolon. The
String value should be used to give information about the encountered failure.

For example,

fail "Impossible state reached";

or, using an interpolated string,

fail $"Syndrome {syn} is incorrect";


In addition to the given String , ideally a fail - statement collects and permit to retrieve information about the
program state that facilitate diagnosing and remedying the source of the error. This requires support from the
executing runtime and firmware which may vary across different targets.
Variable Declarations and Reassignments
13/05/2021 • 4 minutes to read

Values can be bound to symbols via let - and mutable -statements. Such bindings provide a convenient way to
access a value via the defined handle. Despite the somewhat misleading terminology, borrowed from other
languages, we will call handles that are declared on a local scope and contain values variables. The reason that
this may be somewhat misleading is that let -statements define "single-assignment handles", i.e. handles that
for the duration of their validity will always be bound to the same value. Variables that can be re-bound to
different values at different points in the code need to be explicitly declared as such, as specific by the mutable -
statement.

let var1 = 3;
mutable var2 = 3;
set var2 = var2 + 1;

Line 1 declares a variable named var1 that cannot be reassigned and will always contain the value 3 . Line 2
on the other hand defines a variable var2 that is temporarily bound to the value 3 , but can be reassigned to a
different value later on. Such a reassignment can be done via a set -statement, as shown in Line 3. The same
could have been expressed with the shorter version set var2 += 1; explained further below, as it is common in
other languages as well.
To summarize:
let is used to create an immutable binding.
mutable is used to create a mutable binding.
set is used to change the value of a mutable binding.

For all three statements, the left hand side consists of a symbol or a symbol tuple; i.e. if the right-hand side of the
binding is a tuple, then that tuple may be fully or partially deconstructed upon assignment. The only
requirement for deconstruction is that the shape of the tuple on the right hand side matches the shape of the
symbol tuple. The symbol tuple may contain nested tuples and/or omitted symbols, indicated by an underscore.
For example:

let (a, (_, b)) = (1, (2, 3)); // a is bound to 1, b is bound to 3


mutable (x, y) = ((1, 2), [3, 4]); // x is bound to (1, 2), y is bound to [3, 4]
set (x, _, y) = ((5, 6), 7, [8]); // x is re-bound to (5,6), y is re-bound to [8]

The same deconstruction rules are obeyed by all assignments in Q#, including, e.g., qubit allocations and loop-
variable assignments.
For both kinds of binding, the types of the variables are inferred from the right-hand side of the binding. The
type of a variable always remains the same and a set -statement cannot change it. Local variable can be
declared as either being mutable or immutable, with some exceptions like loop-variables in for -loops for
which the behavior is predefined and cannot be specified. Function and operation arguments are always
immutably bound; in combination with the lack of reference types, as discussed in the section on immutability,
that means that a called function or operation can never change any values on the caller side. Since the states of
Qubit values are not defined or observable from within Q#, this does not preclude the accumulation of
quantum side effects, that are observable (only) via measurements (see also this section).
Independent on how a value is bound, the values themselves are immutable. This in particular also holds for
arrays and array items. In contrast to popular classical languages where arrays often are reference types, arrays
- like all type - are value types in Q# and always immutable; they cannot be modified after initialization.
Changing the values accessed by variables of array type thus requires explicitly constructing a new array and
reassigning it to the same symbol, see also the section on immutability and copy-and-update expressions for
more details.

Evaluate-and-Reassign Statements
Statements of the form set intValue += 1; are common in many other languages. Here, intValue needs to be
a mutably bound variable of type Int . Such statements provide a convenient way of concatenation if the right
hand side consists of the application of a binary operator and the result is to be rebound to the left argument to
the operator. For example,

mutable counter = 0;
for i in 1 .. 2 .. 10 {
set counter += 1;
// ...
}

increments the value of the counter counter in each iteration of the for loop. The code above is equivalent to

mutable counter = 0;
for i in 1 .. 2 .. 10 {
set counter = counter + 1;
// ...
}

Similar statements exist for a wide range of operators. The set keyword in this case needs to be followed by a
single mutable variable, which is inserted as the left-most sub-expression by the compiler. Such evaluate-and-
reassign statements exist for all operators where the type of the left-most sub-expression matches the
expression type. More precisely, they are available for binary logical and bitwise operators including right and
left shift, for arithmetic expressions including exponentiation and modulus, for concatenations, as well as for
copy-and-update expressions.
The following function for example computes the sum of an array of Complex numbers:

function ComplexSum(values : Complex[]) : Complex {


mutable res = Complex(0., 0.);
for complex in values {
set res w/= Re <- res::Re + complex::Re;
set res w/= Im <- res::Im + complex::Im;
}
return res;
}

Similarly, the following function multiplies each item in an array with the given factor:

function Multiplied(factor : Double, array : Double[]) : Double[] {


mutable res = new Double[Length(array)];
for i in IndexRange(res) {
set res w/= i <- factor * array[i];
}
return res;
}

The section on contextual expressions contains other examples where expressions can be omitted in a certain
context when a suitable expression can be inferred by the compiler.
Iterations
13/05/2021 • 2 minutes to read

Loops that iterate over a sequence of values are expressed as for -loops in Q#. A for -loop in Q# does not
break based on a condition, but instead corresponds to what is often expressed as foreach or iter in other
languages. There are currently two data types in Q# that support iteration: arrays and ranges.
The statement consists of the keyword for , followed by a symbol or symbol tuple, the keyword in , an
expression of array or Range type, and a statement block.
The statement block (the body of the loop) is executed repeatedly, with the defined symbol(s) (the loop
variable(s)) bound to each value in the range or array. The same deconstruction rules apply to the defined loop
variable(s) as to any other variable assignment, such as bindings in let -, mutable -, set -, use - and borrow -
statements. The loop variables themselves are immutably bound, cannot be reassigned within the body of the
loop, and go out of scope when the loop terminates. The expression over which the loop iterates is fully
evaluated before entering the loop, and will not change while the loop is executing.
Supposing qubits is a value of type Qubit[] . The following examples illustrate what is described above:

for qubit in qubits {


H(qubit);
}

mutable results = new (Int, Result)[0];


for index in 0 .. Length(qubits) - 1 {
set results += [(index, M(qubits[index]))];
}

mutable accumulated = 0;
for (index, measured) in results {
if measured == One {
set accumulated += 1 <<< index;
}
}

Target-Specific Restrictions
There are no break - or continue -primitives in Q#, such that the length of the loop is perfectly predictable as
soon as the value to iterate over is known. Such for -loops can hence be executed on all quantum hardware.
Conditional Loops
01/05/2021 • 2 minutes to read

Much like most classical programming languages, Q# supports loops that break based on a condition, i.e. loops
for which the number of iterations is unknown and may vary from run to run. Since the instruction sequence is
unknown at compile time, these kinds of loops need to be handled with particular care in a quantum runtime.
As long as the condition does not depend on quantum measurements, such loops can be handled without issues
by doing a just-in-time compilation before sending off the instruction sequence to the quantum processor. In
particular, their use within functions is unproblematic, since code within functions can always be executed on
conventional (non-quantum) hardware. Q# therefore supports to use of traditional while -loops within
functions.
Additionally, Q# allows to express control flow that depends on the results of quantum measurements. This
capability enables probabilistic implementations that can significantly reduce the computational costs. A
common example are repeat-until-success patterns, which repeat a computation until a certain condition -
which usually depends on a measurement - is satisfied. Such repeat -loops are widely used in particular classes
of quantum algorithms, and Q# hence has a dedicated language construct to express them, despite that they still
pose a challenge for execution on quantum hardware.

Repeat-Statement
The repeat -statement takes the following form:

repeat {
// ...
}
until condition
fixup {
// ...
}

or alternatively

repeat {
// ...
}
until condition;

where condition is an arbitrary expression of type Bool .


The repeat -statement executes a block of statements before evaluating a condition. If the condition evaluates
to true, the loop exists. If the condition evaluates to false, an addition block of statements defined as part of an
optional fixup -block, if present, is executed prior to entering the next loop iteration.
All parts of the repeat -statement (both blocks and the condition) are treated as a single scope for each
repetition; i.e. symbols that are defined within the repeat -block are visible both to the condition and within the
fixup -block. As for other loops, symbols go out of scope after each iteration, such that symbols defined in the
fixup -block are not visible in the repeat -block.

Target-Specific Restrictions
Loops that break based on a condition are a challenge to process on quantum hardware if the condition
depends on measurement outcomes, since the length of the instruction sequence to execute is not known ahead
of time.
Despite their common presence in particular classes of quantum algorithms, current hardware does not yet
provide native support for these kind of control flow constructs. Execution on quantum hardware can potentially
be supported in the future by imposing a maximum number of iterations.

While-Loop
A more familiar looking statement for classical computations is the while -loop. It is supported only within
functions.
A while statement consists of the keyword while , an expression of type Bool , and a statement block. For
example, if arr is an array of positive integers,

mutable (item, index) = (-1, 0);


while index < Length(arr) && item < 0 {
set item = arr[index];
set index += 1;
}

The statement block is executed as long as the condition evaluates to true .


Discussion
Due to the challenge they pose for execution, we would like to discourage the use of loops that break based
on a condition and hence do not support while-loops within operations. We may consider allowing the use
of while -loops within operations in the future, imposing that the condition cannot depend on the outcomes
of quantum measurements.
Conditional Branching
13/05/2021 • 2 minutes to read

Conditional branching is expressed in the form of if -statements. An if -statement consists of an if -clause,


followed by zero or more elif -clauses and optionally an else-block. Each clause follows the pattern:

keyword condition {
<statements>
}

where keywordis to be replaced with if or elif respectively, condition is an expression of type Bool , and
<statements> is to be replaced with zero or more statements. The optional else -block consists of the keyword
else followed by zero or more statements enclosed in { and } .

The first block for which the condition evaluates to true will be executed. The else -block, if present, is
executed if none of the conditions evaluate to true . The block is executed in its own scope, meaning any
bindings made as part of the statement block are not visible after its end.
For example, suppose qubits is value of type Qubit[] and r1 and r2 are of type Result ,

if r1 == One {
let q = qubits[0];
H(q);
}
elif r2 == One {
let q = qubits[1];
H(q);
}
else {
H(qubits[2]);
}

Additionally, Q# also allows to express simple branching in the form of a conditional expression.

Target-Specific Restrictions
A tight integration between control-flow constructs and quantum computations poses a challenge for current
quantum hardware. Certain quantum processors do not support branching based on measurement outcomes.
Comparison for values of type Result will hence always result in a compilation error for Q# programs that are
targeted to execute on such hardware.
Other quantum processors support specific kinds of branching based on measurement outcomes. The more
general if -statements supported in Q# are compiled into suitable instructions that can be executed on such
processors. The imposed restrictions are that values of type Result may only be compared as part of the
condition within if-statements in operations. The conditionally executed blocks furthermore cannot contain any
return statements or update mutable variables that are declared outside that block.
Conjugations
13/05/2021 • 2 minutes to read

Conjugations are fairly omnipresent in quantum computations. Expressed in mathematical terms, they are
patterns of the form U†VU for unitary transformations U and V. That pattern is especially relevant due to the
particularities of quantum memory: To leverage the unique assets of quantum, computations build up quantum
correlations, i.e. entanglement. However, that also means that once qubits are no longer needed for a particular
subroutine, they cannot easily be reset and released, since observing their state would impact the rest of the
system. For that reason, the effects of a previous computation commonly need to be reversed prior to being
able to release and reuse quantum memory.
Q# hence has a dedicated statement for expressing computation that require such a subsequent clean-up. The
statement consists of two code blocks, one containing the implementation of U and one containing the
implementation of V. The uncomputation (i.e. the application of U†) is done automatically as part of the
statement.
The statement takes the form

within {
<statements>
}
apply {
<statements>
}

where <statements> is to be replaced with any number of statements defining the implementation of U and V
respectively. Both blocks may contain arbitrary classical computations, aside from the usual restrictions for
automatically generating adjoint versions that apply to the within -block. Mutably bound variables that are
used as part of the within -block may not be reassigned as part of the apply -block.
The example of the ApplyXOrIfGreater operation defined in the arithmetic library illustrates the usage of such a
conjugation: The operation maps |lhs |rhs |res → |lhs |rhs |res ⊕ (lhs>rhs) , i.e. it coherently applies an XOR
to a given qubit res if the quantum integer represented by lhs is greater than the one in rhs . The two
integers are expected to be represented in little endian encoding, as indicated by the usage of the corresponding
data type.
operation ApplyXOrIfGreater(
lhs : LittleEndian,
rhs : LittleEndian,
res : Qubit
) : Unit is Adj + Ctl {

let (x, y) = (lhs!, rhs!);


let shuffled = Zip3(Most(x), Rest(y), Rest(x));

use anc = Qubit();


within {
ApplyToEachCA(X, x + [anc]);
ApplyMajorityInPlace(x[0], [y[0], anc]);
ApplyToEachCA(MAJ, shuffled);
}
apply {
X(res);
CNOT(Tail(x), res);
}
}

A temporarily used storage qubit anc is automatically cleaned up before it is released at the end of the
operation; the statements in the within -block are applied first, followed by the statements in the apply -block,
and finally the automatically generated adjoint of the within -block is applied to clean up the temporarily used
helper qubit anc .
Discussion
Returning control from within the apply -block is not yet supported. It should be possible to support this in
the future. The expected behavior in this case is to evaluate the returned value before the adjoint of the
within -block is executed, any qubits going out of scope are released ( anc in this case), and the control is
returned to the caller. In short, the statement should behave similarly to a try-finally pattern in C#.
However, the necessary functionality is not yet implemented.
Quantum Memory Management
30/04/2021 • 4 minutes to read

A program always starts with no qubits, meaning values of type Qubit cannot be passed as entry point
arguments. This restriction is intentional, since a purpose of Q# is to express and reason about a program in its
entirety. Instead, a program allocates and releases quantum memory as it goes. In this regard, Q# models the
quantum computer as a qubit heap.
Rather than supporting separate allocate and release statements for quantum memory, Q# supports quantum
memory allocation in the form of block statements, where the memory is accessible only within the scope of
that statement. An attempt to access that memory after the statement terminates will result in a runtime
exception.
Discussion
Forcing that qubits cannot escape their scope greatly facilitates reasoning about quantum dependencies and
how the quantum parts of the computation can impact the continuation of the program. An additional
benefit of this setup is that qubits cannot get allocated and never freed, which avoids a class of common
bugs in manual memory management languages without the overhead of qubit garbage collection.

Q# has two statements to instantiate qubit values, arrays of qubits, or any combination thereof. Both of these
statements can only be used within operations. They gather the instantiated qubit values, bind them to the
variable(s) specified in the statement, and then execute a block of statements. At the end of the block, the bound
variables go out of scope and are no longer defined.
Q# distinguishes between the allocation of "clean" qubits, meaning qubits that are unentangled and are not used
by another part of the computation, and what is often referred to as "dirty" qubits, meaning qubits whose state
is unknown and can even be entangled with other parts of the quantum processor's memory.

Use-Statement
Clean qubits are allocated by the use -statement. The statement consists of the keyword use followed by a
binding and an optional statement block. If a statement block is present, the qubits are only available within that
block. Otherwise, the qubits are available until the end of the current scope. The binding follows the same
pattern as let statements: either a single symbol or a tuple of symbols, followed by an equals sign = , and
either a single tuple or a matching tuple of initializers.
Initializers are available either for a single qubit, indicated as Qubit() , or an array of qubits, Qubit[n] , where
n is an Int expression. For example,
use qubit = Qubit();
// ...

use (aux, register) = (Qubit(), Qubit[5]);


// ...

use qubit = Qubit() {


// ...
}

use (aux, register) = (Qubit(), Qubit[5]) {


// ...
}

The qubits are guaranteed to be in a |0 state upon allocation. They are released at the end of the scope and are
required to either be in a |0 state upon release, or to have been measured right beforehand. This requirement
is not compiler-enforced, since this would require a symbolic evaluation that quickly gets prohibitively
expensive. When executing on simulators, the requirement can be runtime enforced. On quantum processors,
the requirement cannot be runtime enforced; an unmeasured qubit may be reset to |0 via unitary
transformation. Failing to do so will result in incorrect behavior.
The use -statement allocates the qubits from the quantum processor's free qubit heap, and returns them to the
heap no later than the end of the scope in which the qubits are bound.

Borrow-Statement
The borrow -statement is used to make qubits available for temporary use, which do not need to be in a specific
state: Some quantum algorithms are capable of using qubits without relying on their exact state - or even that
they are unentangled with the rest of the system. That is, they require extra qubits temporarily, but they can
ensure that those qubits are returned exactly to their original state independent of which state that was.
If there are qubits that are in use but not touched during the execution of a subroutine, those qubits can be
borrowed for use by such an algorithm instead of having to allocate additional quantum memory. Borrowing
instead of allocating can significantly reduce the overall quantum memory requirements of an algorithm, and is
a quantum example of a typical space-time tradeoff.
A borrow -statement follows the same pattern as described above for a use -statement, with the same
initializers being available. For example,

borrow qubit = Qubit();


// ...

borrow (aux, register) = (Qubit(), Qubit[5]);


// ...

borrow qubit = Qubit() {


// ...
}

borrow (aux, register) = (Qubit(), Qubit[5]) {


// ...
}

The borrowed qubits are in an unknown state and go out of scope at the end of the statement block. The
borrower commits to leaving the qubits in the same state they were in when they were borrowed, i.e. their state
at the beginning and at the end of the statement block is expected to be the same.
The borrow -statement retrieves in-use qubits that are guaranteed not to be used from the time the qubit is
bound until the last usage of that qubit. If there aren't enough qubits available to borrow, then qubits will be
allocated from and returned to the heap like a use statement.
Discussion
Among the known use cases of dirty qubits are implementations of multi-controlled CNOT gates that
require only very few qubits and implementations of incrementers. This paper gives an example of an
algorithm that utilizes borrowed qubits.
Expressions
23/04/2021 • 2 minutes to read

At the core, Q# expressions are either value literals or identifiers, where identifiers can refer to either locally
declared variables or to globally declared callables (there are currently no global constants in Q#). Operators,
combinators, and modifiers can be used to combine these into a wider variety of expressions.
Operators in a sense are nothing but dedicated syntax for particular callables.

Even though Q# is not yet expressive enough to formally capture the capabilities of each operator in
the form of a backing callable declaration, that should be remedied in the future.

Modifiers can only be applied to certain expressions. One or more modifiers can be applied to
expressions that are either identifiers, array item access expressions, named item access expressions, or
an expression within parenthesis which is the same as a single item tuple (see this section). They can
either precede (prefix) the expression or follow (postfix) the expression. They are thus special unary
operators that bind tighter than function or operation calls, but less tight than any kind of item access.
Concretely, functors are prefix modifiers, whereas the unwrap operator is a postfix modifier.
Like modifiers, function and operation calls as well as item access can be seen as a special kind of
operator subject to the same restrictions regarding where they can be applied; we refer to them as
combinators.
The section on precedence and associativity contains a complete list of all operators as well as a complete list of
all modifiers and combinators.
Precedence and Associativity
01/05/2021 • 2 minutes to read

Precedence and associativity define the order in which operators are applied. Operators with higher precedence
will be bound to their arguments (operands) first, while operators with the same precedence will bind be bound
in the direction of their associativity. For example, the expression 1+2*3 according to the precedence for
addition and multiplication is equivalent to 1+(2*3) , and 2^3^4 equals 2^(3^4) since exponentiation is right-
associative.

Operators
The following table lists the available operators, as well as their precedence and associativity. Additional
modifiers and combinators are listed further below and bind tighter than any of these operators.

DESC RIP T IO N SY N TA X O P ERATO R A SSO C IAT IVIT Y P REC EDEN C E

copy-and-update w/ <- ternary left 1


operator

range operator .. infix left 2

conditional operator ? \| ternary right 5

logical OR or infix left 10

logical AND and infix left 11

bitwise OR \|\|\| infix left 12

bitwise XOR ^^^ infix left 13

bitwise AND &&& infix left 14

equality == infix left 20

inequality != infix left 20

less-than-or-equal <= infix left 25

less-than < infix left 25

greater-than-or- >= infix left 25


equal

greater-than > infix left 25

right shift >>> infix left 28


DESC RIP T IO N SY N TA X O P ERATO R A SSO C IAT IVIT Y P REC EDEN C E

left shift <<< infix left 28

addition or + infix left 30


concatenation

subtraction - infix left 30

multiplication * infix left 35

division / infix left 35

modulus % infix left 35

exponentiation ^ infix right 40

bitwise NOT ~~~ prefix right 45

logical NOT not prefix right 45

negative - prefix right 45

Copy-and-update expressions necessarily need to have the lowest precedence to ensure a consistent behavior
of the corresponding evaluate-and-reassign statement. Similar considerations hold for the range operator to
ensure a consistent behavior of the corresponding contextual expression.

Modifiers and Combinators


Modifiers can be seen as special operators that can be applied to certain expressions only (see this section for
more detail). We can assign them an artificial precedence to capture their behavior.
This artificial precedence is listed in the table below, which also shows how the precedence of operators and
modifiers relates to how tight item access combinators ( [ , ] and :: respectively) and call combinators ( ( ,
) ) bind.

DESC RIP T IO N SY N TA X O P ERATO R A SSO C IAT IVIT Y P REC EDEN C E

Call combinator ( ) n/a left 900

Adjoint functor Adjoint prefix right 950

Controlled functor Controlled prefix right 950

Unwrap application ! postfix left 1000

Named item access :: n/a left 1100

Array item access [ ] n/a left 1100

To illustrate the implications of the assigned precedences, suppose we have a unitary operation DoNothing as
defined in this section, a callable GetStatePrep that returns a unitary operation, and an array algorithms
containing items of type Algorithm defined as follows

newtype Algorithm = (
Register : LittleEndian,
Initialize : Transformation,
Apply : Transformation
);

newtype Transformation =
LittleEndian => Unit is Adj + Ctl;

where LittleEndian is defined in this section. Then the following expressions are all valid:

GetStatePrep()(arg)
(Transformation(GetStatePrep()))!(arg)
Adjoint DoNothing()
Controlled Adjoint DoNothing(cs, ())
Controlled algorithms[0]::Apply!(cs, _)
algorithms[0]::Register![i]

Looking at the precedences defined in the table above, we see that the parentheses around
(Transformation(GetStatePrep())) are necessary for the subsequent unwrap operator to be applied to the
Transformation value rather than the returned operation. However, parentheses are not required in
GetStatePrep()(arg) ; functions are applied left-to-right, so this expression is equivalent to
(GetStatePrep())(arg) . Functor applications also don't require parentheses around them in order to invoke the
corresponding specialization. Neither do array or named item access expressions, such that an expression
arr2D[i][j] is perfectly valid, just like algorithms[0]::Register![i] is.
Copy-and-Update Expressions
13/05/2021 • 3 minutes to read

To reduce the need for mutable bindings, Q# supports copy-and-update expressions for value types with item
access. User defined types and arrays both are immutable and fall into this category. User defined types allow to
access items via name, whereas arrays allow to access items via an index or range of indices.
Copy-and-update expressions instantiate a new value of with all items set to the corresponding value in the
original expression, except certain specified items(s), which are set to the one(s) defined on the right hand side
of the expression. They are constructed using a ternary operator w/ <- ; the syntax w/ should be read as the
commonly used short notation for "with":

original w/ itemAccess <- modification

where original is either an expression of user defined type or an array expression. The corresponding
requirements for itemAccess and modification are specified in the corresponding subsection below.
In terms of precedence, the copy-and-update operator is left-associative and has lowest precedence, and in
particular lower precedence than the range operator ( .. ) or the ternary conditional operator ( ? | ). The
chosen left associativity allows easy chaining of copy-and-update expressions:

let model = Default<SequentialModel>()


w/ Structure <- ClassifierStructure()
w/ Parameters <- parameters
w/ Bias <- bias;

Like for any operator that constructs an expression that is of the same type as the left-most expression involved,
the corresponding evaluate-and-reassign statement is available. The two statements below for example achieve
the following: The first statement declares a mutable variable arr and binds it to the default value of an integer
array. The second statement then builds a new array with the first item (with index 0) set to 10, and reassigns it
to arr .

mutable arr = new Int[3]; // arr contains [0,0,0]


set arr w/= 0 <- 10; // arr contains [10,0,0]

The second statement is nothing but a short-hand for the more verbose syntax set arr = arr w/ 0 <- 10; .

Copy-and-Update of User Defined Types


If the value original is of user defined type, then itemAccess denotes the name of the item that diverges from
the original value. The reason that this is not simply another expression, like original and modification , is that
the ability to simply use the item name without any further qualification is limited to this context; it is one of two
contextual expressions in Q#.
The type of the modification expression needs to match the type of the named item that diverges. For instance,
if complex contains the value Complex(0., 0.) , where the type Complex is defined here, then

complex w/ Re <- 1.
is an expression of type Complex that evaluates to Complex(1.,0.) .

Copy-and-Update of Arrays
For arrays, itemAccess can be an arbitrary expression of a suitable type; the same types that are valid for array
slicing are valid in this context. Concretely, the itemAccess expression can be of type Int or Range . If
itemAccess is a value of type Int , then the type of modification has to match the item type of the array. If
itemAccess is a value of type Range then the type of modification has to be the same as the array type.

For example, if arr contains an array [0,1,2,3] , then


arr w/ 0 <- 10 is the array [10,1,2,3] .
arr w/ 2 <- 10 is the array [0,1,10,3] .
arr w/ 0..2..3 <- [10,12] is the array [10,1,12,3] .
Copy-and-update expressions allow efficient creation of new arrays based on existing ones. The implementation
for copy-and-update expressions avoids copying the entire array but merely duplicates the necessary parts to
achieve the desired behavior, and performs an in-place modification if possible. Array initializations hence do
not incur additional overhead due to immutability.
The Microsoft.Quantum.Arrays namespace provides and arsenal of convenient tools for array creation and
manipulation. For instance, the function ConstantArray creates an array of the specified length and initializes
each item to a given value.
Copy-and-update expressions are a convenient way to construct new arrays on the fly; the following expression,
e.g., evaluates to an array with all items set to PauliI , except the item at index i , which is set to PauliZ :

ConstantArray(n, PauliI) w/ i <- PauliZ


Conditional Expressions
13/05/2021 • 2 minutes to read

Conditional expressions consist of three sub-expressions, where the left-most one is of type Bool and
determines which one of the two other sub-expressions is evaluated. They are of the form

cond ? ifTrue | ifFalse

Specifically, if cond evaluates to true then the conditional expression evaluates to the ifTrue expressions,
and otherwise it evaluates to the ifFalse expression. The other expression (the ifFalse and ifTrue
expression respectively) is never evaluated, much like for the branches in an if -statement. For instance, in an
expression a == b ? C(qs) | D(qs) , if a equals b then the callable C will be invoked, and otherwise D will
be invoked.
The types of the ifTrue and the ifFalse expression have to have a common base type. Independent of which
one of the two ultimately yields the value to which the expression evaluates, its type will always match the
determined base type.
For example, if
Op1 is of type Qubit[] => Unit is Adj
Op2 is of type Qubit[] => Unit is Ctl
Op3 is of type Qubit[] => Unit is Adj + Ctl

then
cond ? Op1 | Op2 is of type Qubit[] => Unit
cond ? Op1 | Op3 is of type Qubit[] => Unit is Adj
cond ? Op2 | Op3 is of type Qubit[] => Unit is Ctl

See the section on subtyping for more detail.


Comparative Expressions
13/05/2021 • 2 minutes to read

Equality Comparison
Equality comparison ( == ) and inequality comparison ( != ) is currently limited to the following data types: Int ,
BigInt , Double , String , Bool , Result , Pauli , and Qubit . The comparison for equality of arrays, tuples,
ranges, user defined types, or callables is currently not supported.
Equality comparison for values of type Qubit evaluates whether the two expressions identify the same qubit.
There is no notion of a quantum state in Q#; equality comparison in particular does not access, measure, or
modify the quantum state of the qubits.
Equality comparison for Double values may be misleading due to rounding effects. For instance, the following
comparison evaluates to false due to rounding errors: 49.0 * (1.0/49.0) == 1.0 .
Discussion

In the future, we may support the comparisons of ranges, as well as arrays, tuples, and user defined types,
provided their items support comparison. As for all types, the comparison will be by value, meaning two
values are considered equal if all of their items are. For values of user defined type, additionally their type
also needs to match. Future support for the comparison of values of type Range follows the same logic;
they should be equal as long as they produce the same sequence of integers, meaning the two ranges

let r1 = 0..2..5; // generates the sequence 0,2,4


let r2 = 0..2..4; // generates the sequence 0,2,4

should be considered equal.


Conversely, there is a good reason not to allow the comparison of callables as the behavior would be ill-
defined. Suppose we will introduce the capability to define functions locally via a possible syntax

let f1 = (x -> Bar(x)); // not yet supported


let f2 = Bar;

for some globally declared function Bar . The first line defines a new anonymous function that takes an
argument x and invokes a function Bar with it and assigns it to the variable f1 . The second line assigns
the function Bar to f2 . Since invoking f1 and invoking f2 will do the same thing, it should be possible
to replace those with each other without changing the behavior of the program. This wouldn't be the case if
the equality comparison for functions was supported and f1 == f2 evaluates to false . If conversely
f1 == f2 were to evaluate to true , then this leads to the question of determining whether two callable will
have the same side effects and evaluate to the same value for all inputs, which is not possible to reliably
determine. Therefore, if we would like to be able to replace f1 with f2 , we can't allow equality
comparisons for callables.

Quantitative Comparison
The operators less-than ( < ), less-than-or-equal ( <= ), greater-than ( > ), and greater-than-or-equal ( >= ) define
quantitative comparisons. They can only be applied to data types that support such comparisons; these are the
same data types that can also support arithmetic expressions.
Logical Expressions
13/05/2021 • 2 minutes to read

Logical operators are expressed as keywords. Q# supports the standard logical operators AND ( and ), OR ( or ),
and NOT ( not ). There is currently no operator for a logical XOR . All of these operators act on operands of type
Bool , and result in an expression of type Bool as well. As it is common in most languages, the evaluation of
AND and OR short-circuits, meaning if the first expression of OR evaluates to true , the second expression is
not evaluated, and the same holds if the first expression of AND evaluates to false . The behavior of conditional
expressions in a sense is similar, in that only ever the condition and one of the two expressions is evaluated.
Bitwise Expressions
13/05/2021 • 2 minutes to read

Bitwise operators are expressed as three non-letter characters. In addition to bitwise versions for AND ( &&& ),
OR ( ||| ), and NOT ( ~~~ ), a bitwise XOR ( ^^^ ) exists as well. They expect operands of type Int or BigInt ,
and for binary operators, the type of both operands has to match. The type of the entire expression equals the
type of the operand(s).
Additionally, left- and right-shift operators ( <<< and >>> respectively) exist, multiplying or dividing the given
left-hand-side (lhs) expression by powers of two. The expression lhs <<< 3 shifts the bit representation of lhs
by three, meaning lhs is multiplied by 2^3 , provided that is still within the valid range for the data type of
lhs . The lhs may be of type Int or BigInt . The right-hand-side expression always has to be of type Int . The
resulting expression will be of the same type as the lhs operand.
For left- and right-shift, the shift amount (i.e. the right-hand-side operand) must be greater than or equal to zero;
the behavior for negative shift amounts is undefined. If the left-hand-side operand is of type Int , then the shift
amount additionally needs to be smaller than 64; the behavior for larger shifts is undefined.
Arithmetic Expressions
13/05/2021 • 2 minutes to read

Arithmetic operators are addition ( + ), subtraction ( - ), multiplication ( * ), division ( / ), negation ( - ),


exponentiation ( ^ ). They can be applied to operands of type Int , BigInt , or Double . Additionally, for integral
types ( Int and BigInt ) an operator computing the modulus ( % ) is available.
For binary operators, the type of both operands has to match, except for exponentiation; an exponent for a value
of type BigInt always has to be of type Int . The type of the entire expression matches the type of the left
operand. For exponentiation of Int and BitInt , the behavior is undefined if the exponent is negative or if it
requires more than 32 bits to represent (i.e. it is larger than 2147483647).
Division and modulus for values of type Int and BigInt follow the following behavior for negative numbers:

A B A / B A % B

5 2 2 1

5 -2 -2 1

-5 2 -2 -1

-5 -2 2 -1

That is, a % b will always have the same sign as a , and b * (a / b) + a % b will always equal a .
Q# does not support any automatic conversions between arithmetic data types - or any other data types for that
matter. This is of importance especially for the Result data type, and facilitates to restrict how runtime
information can propagate. It has the benefit of avoiding accidental errors e.g. related to precision loss.
Concatenation
13/05/2021 • 2 minutes to read

Concatenations are supported for values of type String and arrays. In both cases they are expressed via the
operator + . For instance, "Hello " + "world!" evaluates to "Hello world!" , and [1,2,3] + [4,5,6] evaluates
to [1,2,3,4,5,6] .
Concatenating two arrays requires that both arrays are of the same type, in contrast to constructing an array
literal where a common base type for all array items is determined. This is due to the fact that arrays are treated
as invariant. The type of the entire expression matches the type of the operands.
Partial Application
13/05/2021 • 2 minutes to read

Callables are declared at a global scope and by default can be used anywhere in the same project and in a
project that references the assembly in which they are declared. However, often there is a need to construct a
callable for use in a local context only. Q# currently provides one rather powerful mechanism to construct new
callables on the fly: partial applications.
Partial application refers to that some of the argument items to a callable are provided while others are still
missing as indicated by an underscore. The result is a new callable value that takes the remaining argument
items, combines them with the already given ones, and invokes the original callable. Naturally, partial
application preserves the characteristics of a callable, i.e. a callable constructed by partial application supports
the same functors as the original callable.
Q# allows any subset of the parameters to be left unspecified, not just a final sequence, which ties in more
naturally with the design to have each callable take and return exactly one value. For a function Foo whose
argument type is (Int, (Double, Bool), Int) for instance, Foo(_, (1.0, _), 1) is a function that takes an
argument of type (Int, (Bool)) , which is the same as an argument of type (Int, Bool) , see this section.
Because partial application of an operation does not actually evaluate the operation, it has no impact on the
quantum state. This means that building a new operation from existing operations and computed data may be
done in a function; this is useful in many adaptive quantum algorithms and in defining new control flow
constructs.
Functor Application
01/05/2021 • 2 minutes to read

Functors are factories that allow to access particular specialization implementations of a callable. Q# currently
supports two functors; the Adjoint and the Controlled , both of which can be applied to operations that
provide the necessary specialization(s).
The Controlled and Adjoint functors commute; if ApplyUnitary is an operation that supports both functors,
then there is no difference between Controlled Adjoint ApplyUnitary and Adjoint Controlled ApplyUnitary .
Both have the same type and upon invocation execute the implementation defined for the controlled adjoint
specialization.

Adjoint Functor
If the operation ApplyUnitary defines a unitary transformation U of the quantum state, Adjoint ApplyUnitary
accesses the implementation of U†. The Adjoint functor is its own inverse, since (U†)† = U by definition; i.e.
Adjoint Adjoint ApplyUnitary is the same as ApplyUnitary .

The expression Adjoint ApplyUnitary is an operation of the same type as ApplyUnitary ; it has the same
argument and return type and supports the same functors. Like any operation, it can be invoked with an
argument of suitable type. The following expression will apply the adjoint specialization of ApplyUnitary to an
argument arg :

Adjoint ApplyUnitary(arg)

Controlled Functor
For an operation ApplyUnitary that defines a unitary transformation U of the quantum state,
Controlled ApplyUnitary accesses the implementation that applies U conditional on all qubits in an array of
control qubits being in the |1 state.
The expression Controlled ApplyUnitary is an operation with the same return type and operation characteristics
as ApplyUnitary , meaning it supports the same functors. It takes an argument of type (Qubit[], <TIn>) , where
<TIn> should be replaced with the argument type of ApplyUnitary , taking singleton tuple equivalence into
account.

O P ERAT IO N A RGUM EN T T Y P E C O N T RO L L ED A RGUM EN T T Y P E

X Qubit (Qubit[], Qubit)

SWAP (Qubit, Qubit) (Qubit[], (Qubit, Qubit))

ApplyQFT LittleEndian (Qubit[], LittleEndian)

Concretely, if cs contains an array of qubits, q1 and q2 are two qubits, and the operation SWAP is as defined
here, then the following expression exchanges the state of q1 and q2 if all qubits in cs are in the |1 state:

Controlled SWAP(cs, (q1, q2))


Discussion
Conditionally applying an operation based on the control qubits being in another state than a zero-state
may be achieved by applying the appropriate adjointable transformation to the control qubits before
invocation, and applying is inverses after. Conditioning the transformation on all control qubits being in the
|0 state, for example, can be achieved by applying the X operation before and after. This can be
conveniently expressed using a conjugation. Nonetheless, the verbosity of such a construct may merit
additional support for a more compact syntax in the future.
Item Access
19/05/2021 • 2 minutes to read

Q# supports item access for array items and for items in user defined types. In both cases, the access is read-
only, i.e. the value cannot be changed without creating a new instance using a copy-and-update expression.

Array Item Access and Array Slicing


Given an array expression and an expression of type Int or Range , a new expression may be formed using the
array item access operator consisting of [ and ] .
If the expression inside the brackets is of type Int then the new expression will contain the array item at that
index.
For instance, if arr is of type Double[] and contains five or more items, then arr[4] is an expression of type
Double .

If the expression inside the brackets is fo type Range then the new expression will contain an array of all items
indexed by the specified Range . If the Range is empty, then the resulting array will be empty.
For instance,

let arr = [10, 11, 36, 49];


let ten = arr[0]; // contains the value 10
let odds = arr[1..2..4]; // contains the value [11, 49]
let reverse = arr[...-1...]; // contains the value [49, 36, 11, 10]

In the last line, the start and end value of the range have been omitted for convenience; see this section for more
detail.
If the array expression is not a simple identifier, it must be enclosed in parentheses in order to extract an item or
a slice, see also the section on precedence. For instance, if arr1 and arr2 are both arrays of integers, an item
from the concatenation would be expressed as (arr1 + arr2)[13] .
All arrays in Q# are zero-based. That is, the first element of an array arr is always arr[0] . An exception will be
thrown at runtime if the index or one of the indices used for slicing is outside the bounds of the array, i.e. if it is
less than zero or larger or equal to the length of the array.

Item Access for User Defined Types


This section describes how to define custom types, containing one or more named or anonymous items.
The contained items can be accessed via their name or by deconstruction, illustrated by the following statements
that may be used as part of a operation or function implementation:

let complex = Complex(1.,0.); // create a value of type Complex


let (re, _) = complex!; // item access via deconstruction
let im = complex::Imaginary; // item access via name

The item access operator ( :: ) retrieves named items. While named items can be accessed by their name or via
deconstruction, anonymous items can only be accessed by the latter. Since deconstruction relies on all of the
contained items, the usage anonymous items is discourage when these items need to be accessed outside the
compilation unit in which the type is defined.
Access via deconstruction makes use of the unwrap operator ( ! ). That operator will return a tuple of all
contained items, where the shape of the tuple matches the one defined in the declaration, and a single item
tuple is equivalent to the item itself (see this section).
For example, for a value nested of type Nested defined as follows

newtype Nested = (Double, (ItemName : Int, String));

the expression nested! return a value of type (Double, (Int, String)) .


The ! operator has lower precedence than both item access operators, but higher precedence than any other
operator. A complete list of precedences can be found here.
Contextual and Omitted Expressions
01/05/2021 • 2 minutes to read

The usage of item names in copy-and-update expressions without having to qualify them is an example for an
expression that is only valid in a certain context.
Furthermore, expressions can be omitted when they can be inferred and automatically inserted by the compiler,
as it is the case in evaluate-and-reassign statements.
There is one more example for both; open-ended ranges are valid only within a certain context, and the compiler
will translate them into normal Range expressions during compilation by inferring suitable boundaries.
A value of type Range generates a sequence of integers, specified by a start, optionally a step, and an end value.
For example, the Range literal expressions 1..3 generates the sequence 1,2,3, and the expression 3..-1..1
generates the sequence 3,2,1. Ranges can be used for example to create a new array from an existing one by
slicing:

let arr = [1,2,3,4];


let slice1 = arr[1..2..4]; // contains [2,4]
let slice2 = arr[2..-1..0]; // contains [3,2,1]

No infinite ranges exist in Q#, such that start and end value always need to be specified, expect when a Range is
used to slice an array. In that case, the start and/or end value of the range can reasonably be inferred.
Looking at the array slicing expressions above, it is reasonable for the compiler to assume that the intended
range end should be the index of the last item in the array if the step size is positive. If the step size on the other
hand is negative, then the range end likely should be the index of the first item in the array, i.e. 0 . The converse
holds for the start of the range.
To summarize, if the range start value is omitted, then the inferred start value
is zero if no step is specified or the specified step is positive,
is the length of the array minus one if the specified step is negative.
If the range end value is omitted, then the inferred end value
is the length of array minus one if no step is specified or the specified step is positive, and
is zero if the specified step is negative.
Q# hence allows to use open-ended ranges within array slicing expressions:

let arr = [1,2,3,4,5,6];


let slice1 = arr[3...]; // slice1 is [4,5,6];
let slice2 = arr[0..2...]; // slice2 is [1,3,5];
let slice3 = arr[...2]; // slice3 is [1,2,3];
let slice4 = arr[...2..3]; // slice4 is [1,3];
let slice5 = arr[...2...]; // slice5 is [1,3,5];
let slice7 = arr[4..-2...]; // slice7 is [5,3,1];
let slice8 = arr[...-1..3]; // slice8 is [6,5,4];
let slice9 = arr[...-1...]; // slice9 is [6,5,4,3,2,1];
let slice10 = arr[...]; // slice10 is [1,2,3,4,5,6];

Since the information whether the range step is positive or negative is runtime information, the compiler inserts
a suitable expression that will be evaluated at runtime. For omitted end values, the inserted expression is
step < 0 ? 0 | Length(arr)-1 , and for omitted start values it is step < 0 ? Length(arr)-1 | 0 , where step is
the expression given for the range step, or 1 if no step is specified.
Literals
13/05/2021 • 6 minutes to read

Unit Literal
The only existing literal for the Unit type is the value () .
The Unit value is commonly used as an argument to callables, e.g. either because no other arguments need to
be passed or to delay execution. It is also used as return value when no other value needs to be returned, which
is in particular the case for unitary operations, i.e. operations that support the Adjoint and/or the Controlled
functor.

Int Literals
Value literals for the Int type can be expressed in binary, octal, decimal, or hexadecimal representation. Literals
expressed in binary are prefixed with 0b , with 0o for octal, and with 0x for hexadecimal. There is no prefix for
the commonly used decimal representation.

REP RESEN TAT IO N VA L UE L IT ERA L

Binary 0b101010

Octal 0o52

Decimal 42

Hexadecimal 0x2a

BigInt Literals
Value literals for the BigInt type are always postfixed with L and can be expressed in binary, octal, decimal, or
hexadecimal representation. Literals expressed in binary are prefixed with 0b , with 0o for octal, and with 0x
for hexadecimal. There is no prefix for the commonly used decimal representation.

REP RESEN TAT IO N VA L UE L IT ERA L

Binary 0b101010L

Octal 0o52L

Decimal 42L

Hexadecimal 0x2aL

Double Literals
Value literals for the Double type can be expressed in standard or scientific notation.
REP RESEN TAT IO N VA L UE L IT ERA L

Standard 0.1973269804

Scientific 1.973269804e-1

If nothing follows after the decimal point, then the digit after dot may be omitted, e.g. 1. is a valid Double
literal and the same as 1.0 . Similarly, if the digits before the decimal point are all zero, then they may be
omitted, e.g. .1 is a valid Double literal and the same as 0.1 .

Bool Literals
Existing literals for the Bool type are true and false .

String Literals
A value literal for the String type is an arbitrary length sequence of Unicode characters enclosed in double
quotes. Inside of a string, the back-slash character \ may be used to escape a double quote character, and to
insert a new-line as \n , a carriage return as \r , and a tab as \t .
The following are examples for valid string literals:

"This is a simple string."


"\"This is a more complex string.\", she said.\n"

Q# also supports interpolated strings. An interpolated string is a string literal that may contain any number of
interpolation expressions. These expressions can be of arbitrary types. Upon construction, the expressions are
evaluated and their String representation is inserted at the corresponding location within the defined literal.
Interpolation is enabled by prepending the special character $ directly before the initial quote, i.e. without any
white space between them.
For instance, if res is an expression that evaluates to 1 , then the second sentence in the following String
literal will say "The result was 1.":

$"This is an interpolated string. The result was {res}."

Qubit Literals
No literals for the Qubit type exist, since quantum memory is managed by the runtime. Values of type Qubit
can hence only be obtained via allocation.
Values of type Qubit represent an opaque identifier by which a quantum bit, a.k.a. a qubit, can be addressed.
The only operator they support is equality comparison. See this section for more details on the Qubit data type.

Result Literals
Existing literals for the Result type are Zero and One .
Values of type Result represent the result of a binary quantum measurement. Zero indicates a projection onto
the +1 eigenspace, One indicates a projection onto the -1 eigenspace.

Pauli Literals
Existing literals for the Pauli type are PauliI , PauliX , PauliY , and PauliZ .
Values of type Pauli represent one of the four single-qubit Pauli matrices, with PauliI representing the
identity. Values of type Pauli are commonly used to denote the axis for rotations and to specify with respect to
which basis to measure.

Range Literals
Value literals for the Range type are expressions of the form start..step..stop , where start , step , and end
are expressions of type Int . If the step size is one, it may be omitted, i.e. start..stop is a valid Range literal
and the same as start..1..stop .
Values of type Range represent a sequence of integers, where the first element in the sequence is start , and
subsequent elements are obtained by adding step to the previous one, until stop is passed. Range values are
inclusive at both ends; i.e. the last element of the range will be stop if the difference between start and stop
is a multiple of step . A range may be empty if, for instance, step is positive and stop < start .
The following are examples for valid Range literals:
1..3 is the range 1, 2, 3.
2..2..5 is the range 2, 4.
2..2..6 is the range 2, 4, 6.
6..-2..2 is the range 6, 4, 2.
2..-2..1 is the range 2.
2..1 is the empty range.

See also the section on contextual expressions.

Array Literals
An array literal is a sequence of one or more expressions, separated by commas, enclosed in [ and ] , e.g.
[1,2,3] . All expressions must have a common base type, which will be the item type of the array.

Arrays or arbitrary length, and in particular empty arrays, may be created using a new array expression. Such an
expression is of the form new <ItemType>[expr] , where expr can be any expression of type Int and
<ItemType> is to be replace by the type of the array items.

For instance, new Int[10] creates an array of integers with containing ten items. The length of an array can be
queries with the function Length . It is defined in the automatically opened namespace Microsoft.Quantum.Core
and returns an Int value.
All items in the create array are set to the default value of the item type. Arrays containing qubits or callables
must be properly initialized with non-default values before their elements may be safely used. Suitable
initialization routines can be found in the Microsoft.Quantum.Arrays namespace.

Tuple Literals
A tuple literal is a sequence of one or more expressions of any type, separated by commas, enclosed in ( and
) . The type of the tuple includes the information about each item type.

VA L UE L IT ERA L TYPE

("Id", 0, 1.) (String, Int, Double)


VA L UE L IT ERA L TYPE

(PauliX,(3,1)) (Pauli, (Int, Int))

Tuples containing a single item are treated as identical to the item itself, both in type and value. We refer to this a
singleton tuple equivalence.
Tuples are used to bundle values together into a single value, making it easier to pass them around. This makes
it possible that every callable takes exactly one input and returns exactly one output.

Literals for User Defined Types


Values of a user defined type are constructed by invoking their constructor. A default constructor is
automatically generated when declaring the type. It is currently not possible to define custom constructors.
For instance, if IntPair has two items of type Int , then IntPair(2, 3) creates a new instance by invoking the
default constructor.

Operation Literals
No literals exist for values of operation type; operations have to be declared on a global scope and new
operations can be constructed locally using partial application.

Function Literals
No literals exist for values of function type; functions have to be declared on a global scope and new functions
can be constructed locally using partial application.

Default Values
TYPE DEFA ULT

Unit ()

Int 0

BigInt 0L

Double 0.0

Bool false

String ""

Qubit invalid qubit

Result Zero

Pauli PauliI

Range empty range


TYPE DEFA ULT

Array empty array

Tuple all items are set to default values

User defined type all items are set to default values

Operation invalid operation

Function invalid function

For qubits and callables, the default is an invalid reference that cannot be used without causing a runtime error.
Type System
15/04/2021 • 2 minutes to read

With the focus for quantum algorithm being more towards what should be achieved rather than on a problem
representation in terms of data structures, taking a more functional perspective on language design is a natural
choice. At the same time, the type system is a powerful mechanism that can be leveraged for program analysis
and other compile-time checks that facilitate formulating robust code.
All in all, the Q# type system is fairly minimalist, in the sense that there isn't an explicit notion of classes or
interfaces as one might be used to from classical languages like C# or Java. We also take a somewhat pragmatic
approach making incremental progress, such that certain construct are not yet fully integrated into the type
system. An example are functors, which can be used within expressions but don't yet have a representation in
the type system. Correspondingly, they cannot currently be assigned or passed as arguments, similar as it is the
case for type parametrized callables. We expect to make incremental progress in extending the type system to
be more complete, and balance immediate needs with longer-term plans.

Available Types
All types in Q# are immutable.

TYPE DESC RIP T IO N

Unit Represents a singleton type whose only value is () .

Int Represents a 64-bit signed integer. Values range from -


9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.

BigInt Represents signed integer values of any size.

Double Represents a double-precision 64-bit floating-point number.


Values range from -1.79769313486232e308 to
1.79769313486232e308 as well as NaN (not a number).

Bool Represents Boolean values. Possible values are true or


false .

String Represents text as values that consist of a sequence of UTF-


16 code units.

Qubit Represents an opaque identifier by which virtual quantum


memory can be addressed. Values of type Qubit are
instantiated via allocation.

Result Represents the result of a projective measurement onto the


eigenspaces of a quantum operator with eigenvalues ±1.
Possible values are Zero or One .

Pauli Represents a single-qubit Pauli matrix. Possible values are


PauliI , PauliX , PauliY , or PauliZ .
TYPE DESC RIP T IO N

Range Represents an ordered sequence of equally spaced Int


values. Values may represent sequences in ascending or
descending order.

Array Represents values that each contain a sequence of values of


the same type.

Tuple Represents values that each contain a fixed number of items


of different types. Tuples containing a single element are
equivalent to the element they contain.

User defined type Represents a user defined type consisting of named and
anonymous items of different types. Values are instantiated
by invoking the constructor.

Operation Represents a non-deterministic callable that takes one


(possibly tuple-valued) input argument returns one (possibly
tuple-valued) output. Calls to operation values may have
side effects and the output may vary for each call even when
invoked with the same argument.

Function Represents a deterministic callable that takes one (possibly


tuple-valued) input argument returns one (possibly tuple-
valued) output. Calls to function values do not have side
effects and the output is will always be the same given the
same input.
Quantum-Specific Data Types
13/05/2021 • 2 minutes to read

In addition to the Qubit type explained in detail below, there are two other types that are somewhat specific to
the quantum domain: Pauli and Result . Values of type Pauli specify a single-qubit Pauli operator; the
possibilities are PauliI , PauliX , PauliY , and PauliZ . Pauli values are used primarily to specify the basis for
a measurement. The Result type specifies the result of a quantum measurement. Q# mirrors most quantum
hardware by providing measurements in products of single-qubit Pauli operators; a Result of Zero indicates
that the +1 eigenvalue was measured, and a Result of One indicates that the -1 eigenvalue was measured.
That is, Q# represents eigenvalues by the power to which -1 is raised. This convention is more common in the
quantum algorithms community, as it maps more closely to classical bits.

Qubits
Q# treats qubits as opaque items that can be passed to both functions and operations, but that can only be
interacted with by passing them to instructions that are native to the targeted quantum processor. Such
instructions are always defined in the form of operations, since their intent is indeed to modify the quantum
state. That functions cannot modify the quantum state despite that qubits can be passed as input arguments is
enforced by the restriction that functions can only call other functions, and cannot call operations.
The Q# libraries are compiled against a standard set of intrinsic operations, meaning operations which have no
definition for their implementation within the language. Upon targeting, the implementations that expresses
them in terms of the instructions that are native to the execution target are linked in by the compiler. A Q#
program thus combines these operations as defined by a target machine to create new, higher-level operations
to express quantum computation. In this way, Q# makes it very easy to express the logic underlying quantum
and hybrid quantum-classical algorithms, while also being very general with respect to the structure of a target
machine and its realization of quantum state.
Within Q# itself, there is no type or construct in Q# that represents the quantum state. Instead, a qubit
represents the smallest addressable physical unit in a quantum computer. As such, a qubit is a long-lived item,
so Q# has no need for linear types. Importantly, we hence do not explicitly refer to the state within Q#, but
rather describe how the state is transformed by the program, e.g., via application of operations such as X and
H . Similar to how a graphics shader program accumulates a description of transformations to each vertex, a
quantum program in Q# accumulates transformations to quantum states, represented as entirely opaque
reference to the internal structure of a target machine.
A Q# program has no ability to introspect into the state of a qubit, and thus is entirely agnostic about what a
quantum state is or on how it is realized. Rather, a program can call operations such as Measure to learn
information about the quantum state of the computation.
Immutability
01/05/2021 • 2 minutes to read

All types in Q# are value types. Q# does not have a concept of a reference or pointer. Instead, it allows to
reassign a new value to a previously declared variable via a set -statement. There is no distinction in behavior
between reassignments for, e.g., variables of type Int or variables of type Int[] . To give an explicit illustration,
consider the following sequence of statements:

mutable arr1 = new Int[3];


let arr2 = arr1;
set arr1 w/= 0 <- 3;

The first statements instantiates a new arrays of integers [0,0,0] and assigns it to arr1 . Line 2 assigns that
value to a variable with name arr2 . Line 3 then creates a new array instance based on arr1 with the same
values except for the value at index 0 which is set to 3. The newly created array is then assigned to the variable
arr1 . The last line makes use of the abbreviated syntax for evaluate-and-reassign statements, and could
equivalently have been written as set arr1 = arr1 w/ 0 <- 1; .
After executing the three statements, arr1 will contain the value [3,0,0] while arr2 remains unchanged and
contains [0,0,0] .
Q# clearly thus distinguishes the mutability of a handle and the behavior of a type. Mutability within Q# is a
concept that applies to a symbol rather than a type or value; it applies to the handle that allows one to access a
value rather than to the value itself. It is not represented in the type system, implicitly or explicitly.
Of course, this is merely a description of the formally defined behavior; under the hood, the actual
implementation uses a reference counting scheme to avoid copying memory as much as possible. The
modification is specifically done in-place as long as there is only one currently valid handle that accesses a
certain value.
Operations and Functions
01/05/2021 • 3 minutes to read

As elaborated in more detail in the description of the qubits, quantum computations are executed in the form of
side effects of operations that are natively supported on the targeted quantum processor. These are in fact the
only side effects in Q#; since all types are immutable, there are no side effect that impact a value that is explicitly
represented in Q#. Hence, as long as an implementation of a certain callable does not directly or indirectly call
any of these natively implemented operations, its execution will always produce the same output given the same
input.
Q# allows to explicitly split out such purely deterministic computations into functions. Since the set of natively
supported instructions is not fixed and built into the language itself, but rather fully configurable and expressed
as a Q# library, determinism is guaranteed by requiring that functions can only call other functions, but cannot
call any operations. Additionally, native instructions that are not deterministic, e.g., because they impact the
quantum state are represented as operations. With these two restrictions, function can be evaluated as soon as
their input value is known, and in principle never need to be evaluated more than once for the same input.
Q# therefore distinguishes between two types of callables: operations and functions. All callables take a single
(potentially tuple-valued) argument as input and produce a single value (tuple) as output. Syntactically, the
operation type is expressed as <TIn> => <TOut> is <Char> , where <TIn> is to be replaced by the argument type,
<TOut> is to be replaced by the return type, and <Char> is to be replaced by the operation characteristics. If no
characteristics need to be specified, the syntax simplifies to <TIn> => <TOut> . Similarly, function types are
expressed as <TIn> -> <TOut> .
There is little difference between operations and functions beside this determinism guarantee. Both are first-
class values that can be passed around freely; they can be used as return values or arguments to other callables,
as illustrated by the example below.

function Pow<'T>(op : 'T => Unit, pow : Int) : 'T => Unit {
return PowImpl(op, pow, _);
}

They can be instantiated based on a type parametrized definition such as, e.g., the type parametrized function
Pow above, and they can be partially applied as done in Line 2 in the example.

Operation Characteristics
In addition to the information about in- and output type, the operation type contains information about the
characteristics of an operation. This information for example describes what functors are supported by the
operation. Additionally, the internal representation also contains optimization relevant information that is
inferred by the compiler.
The characteristics of an operation are a set of predefined and built-in labels. They are expressed in the form of a
special expression that is part of the type signature. The expression consists either of one of the predefined sets
of labels, or of a combination of characteristics expressions via a supported binary operator.
There are two predefined sets, Adj and Ctl .
Adj is the set that contains a single label indicating that an operation is adjointable - meaning it supports
the Adjoint functor and the applied quantum transformation can be "undone" (i.e. it can be inverted).
Ctl is the set that contains a single label indicating that an operation is controllable - meaning it supports
the Controlled functor and its execution can be conditioned on the state of other qubits.
The two operators that are supported as part of characteristics expressions are the set union + and the set
intersection * . In EBNF,

predefined = "Adj" | "Ctl";


characteristics = predefined
| "(", characteristics, ")"
| characteristics ("+"|"*") characteristics;

As one would expect, * has higher precedence than + and both are left-associative. The type of a unitary
operation for example is expressed as <TIn> => <TOut> is Adj + Ctl where <TIn> should be replaced with the
type of the operation argument, and <TOut> with the type of the returned value.
Discussion
Indicating the characteristics of an operation in this form has two major advantages; for one, new labels can
be introduced without having exponentially many language keywords for all combinations of labels. Perhaps
more importantly, using expressions to indicate the characteristics of an operation also permits to support
parameterizations over operation characteristics in the future.
Singleton Tuple Equivalence
01/05/2021 • 2 minutes to read

To avoid any ambiguity between tuples and parentheses that group sub-expressions, a tuple with a single
element is considered to be equivalent to the contained item. This includes its type; for instance, the types Int ,
(Int) , and ((Int)) are treated as identical. The same holds for the values 5 , (5) and (((5))) , or for
(5, (6)) and (5, 6) . This equivalence applies for all purposes, including assignment. Since there is no
dynamic dispatch or reflection in Q# and all types in Q# are resolvable at compile-time, singleton tuple
equivalence can be readily implemented during compilation.
Subtyping and Variance
13/05/2021 • 4 minutes to read

Q# supports only very few conversion mechanisms. Implicit conversions can happen only when applying binary
operators, when evaluating conditional expressions, and when constructing an array literal. In these cases, a
common supertype is determined and the necessary conversions are performed automatically. Aside from such
implicit conversions, explicit conversation via function calls are possible and often necessary.
At present time, the only subtyping relation that exists applies to operations. Intuitively it makes sense that one
should be allowed to substitute an operation that supports more than the required set of functors. Concretely,
for any two concrete types TIn and TOut , the subtyping relation is

(TIn => TOut) :>


(TIn => TOut is Adj), (TIn => TOut is Ctl) :>
(TIn => TOut is Adj + Ctl)

where A :> B indicates that B is a subtype of A . Phrased differently, B is more restrictive than A such that a
value of type B can be used wherever a value of type A is required. If a callable relies on an argument (item)
of being of type A , then an argument of type B can safely be substituted since if provides all the necessary
capabilities.
This kind of polymorphism extends to tuples in that a tuple type B is a subtype of a tuple type A if it contains
the same number of items and the type of each item is a subtype of the corresponding item type in A . This is
known as depth subtyping. There is currently no support for width subtyping; there is no subtype relation
between any two user defined types or a user defined type and any built-in type. The existence of the unwrap
operator that allows to extract a tuple containing all named and anonymous items prevents this.
Discussion
Looking at callables, if a callable processes an argument of type A , then it is also capable of processing an
argument of type B . If a callable is passed as an argument to another callable, then it has to be capable of
processing anything that the type signature requires. This means that if the callable needs to be able to
process an argument of type B , any callable that is capable of processing a more general argument of type
A can safely be passed. Conversely, we expect that if we require that the passed callable returns an a value
of type A , then the promise to return a value of type B is sufficient since that value will provide all
necessary capabilities.

The operation or function type is contravariant in its argument type and covariant in its return type. A :> B
hence implies that for any concrete type T1 ,

(B → T1) :> (A → T1), and


(T1 → A) :> (T1 → B)

where → here can mean either a function or operation, and we omit any annotations for characteristics.
Substituting A with (B → T2) and (T2 → A) respectively, and substituting B with (A → T2) and (T2 → B)
respectively leads to the conclusion that for any concrete type T2 ,
((A → T2) → T1) :> ((B → T2) → T1), and
((T2 → B) → T1) :> ((T2 → A) → T1), and
(T1 → (B → T2)) :> (T1 → (A → T2)), and
(T1 → (T2 → A)) :> (T1 → (T2 → B))

By induction, it follows that every additional indirection reverses the variance of the argument type, and leaves
the variance of the return type unchanged.
Discussion
This also makes it clear what the variance behavior of arrays needs to be; retrieving items via an item access
operator corresponds to invoking a function of type (Int -> TItem) , where TItem is the type of the
elements in the array. Since this function is implicitly passed when passing an array, it follows that arrays
need to be covariant in their item type. The same considerations also hold for tuples, which are immutable
and thus covariant with respect to each item type. If arrays weren't immutable, the existence of an construct
that would allow to set items in an array and thus takes an argument of type TItem would imply that arrays
also need to be contravariant. The only option for data types that support getting and setting items is hence
to be invariant, meaning there is no subtyping relation whatsoever; B[] is not a subtype of A[] even if B
is a subtype of A .

Despite that arrays in Q# are immutable, they are invariant rather than covariant. This, e.g., means that a value of
type (Qubit => Unit is Adj)[] cannot be passed to a callable that requires an argument of type
(Qubit => Unit)[] . Keeping arrays invariant allows for more flexibility related to how arrays are handled and
optimized in the runtime, but it may be possible to revise that in the future.
Type Parameterizations
28/04/2021 • 5 minutes to read

Q# supports type-parameterized operations and functions. The Q# standard libraries make heavy use of type
parametrized callables to provide a host of useful abstractions, including functions like Mapped and Fold that
are familiar from functional languages.
Discussion
To motivate the concept of type parameterizations, consider the example of the function Mapped , which
applies a given function to each value in an array and returns a new array with the computed values. This
functionality can be perfectly described without specifying the item types of the in- and output array. Since
the exact types do not change the implementation of the function Mapped , it makes sense that it should be
possible to define this implementation for arbitrary item types; we want to define a factory or template that
given the concrete types for the items in the in- and output array returns the corresponding function
implementation. This notion is formalized in the form of type parameters.

Any operation or function declaration may specify one or more type parameters that can be used as the types or
part of the types of the callable's input and/or output. The exception are entry points, which must be concrete
and cannot be type parametrized. Type parameter names start with a tick (') and may appear multiple times in
the input and output types. All arguments that correspond to the same type parameter in the callable signature
must be of the same type.
A type parametrized callable needs to be concretized — i.e. provided with the necessary type argument(s) —
before it can be assigned or passed as argument, such that all type parameters can be replaced with concrete
types. A type is considered to be concrete if it is either one of the built-in types, a user defined type, or if it is
concrete within the current scope. The following example illustrates what it means for a type to be concrete
within the current scope, and is explained in more detail below:

function Mapped<'T1, 'T2> (


mapper : 'T1 -> 'T2,
array : 'T1[]
) : 'T2[] {

mutable mapped = new 'T2[Length(array)];


for (i in IndexRange(array)) {
set mapped w/= i <- mapper(array[i]);
}
return mapped;
}

function AllCControlled<'T3> (
ops : ('T3 => Unit)[]
) : ((Bool,'T3) => Unit)[] {

return Mapped(CControlled<'T3>, ops);


}

The function CControlled is defined in the Microsoft.Quantum.Canon namespace. It takes an operation op of


type 'TIn => Unit as argument and returns a new operation of type (Bool, 'TIn) => Unit that applies the
original operation provided a classical bit (of type Bool ) is set to true; this is often referred to as the classically
controlled version of op .
The function Mapped takes an array of an arbitrary item type 'T1 as argument, applies the given mapper
function to each item and returns a new array of type 'T2[] containing the mapped items. It is defined in the
Microsoft.Quantum.Array namespace. For the purpose of the example, the type parameters are numbered to
avoid making the discussion more confusing by giving the type parameters in both functions the same name.
This is not necessary; type parameters for different callables may have the same name, and the chosen name is
only visible and relevant within the definition of that callable.
The function AllCControlled takes an array of operations and returns a new array containing the classically
controlled versions of these operations. The call of Mapped resolves its type parameter 'T1 to 'T3 => Unit ,
and its type parameter 'T2 to (Bool,'T3) => Unit . The resolving type arguments are inferred by the compiler
based on the type of the given argument. We say that they are implicitly defined by the argument of the call
expression. Type arguments can also be specified explicitly as it is done for CControlled in the same line. The
explicit concretization CControlled<'T3> is necessary when the type arguments cannot be inferred.
The type is concrete within the context of AllCControlled , since it is known for each invocation of
'T3
AllCControlled . That means that as soon as the entry point of the program - which cannot be type
parametrized - is know, so is the concrete type 'T3 for each call to AllCControlled , such that a suitable
implementation for that particular type resolution can be generated; once the entry point to a program is
known, all usages of type parameters can be eliminated at compile-time. We refer to this process as
monomorphization.
A couple of restrictions are needed to ensure that this can indeed be done at compile-time opposed to only at
run time.
Discussion
Consider the following example:

operation Foo<'TArg> (
op : 'TArg => Unit,
arg : 'TArg
) : Unit {

let cbit = RandomInt(2) == 0;


Foo(CControlled(op), (cbit, arg));
}

Ignoring that an invocation of Foo will result in an infinite loop, it serves the purpose of illustration. Foo
invokes itself with the classically controlled version of the original operation op that has been passed in as
well as a tuple containing a random classical bit in addition to the original argument.
For each iteration in the recursion, the type parameter 'TArg of the next call is resolved to (Bool, 'TArg) ,
where 'TArg is the type parameter of the current call. Concretely, suppose Foo is invoked with the
operation H and an argument arg of type Qubit . Foo will then invoke itself with a type argument
(Bool, Qubit) , which will then invoke Foo with a type argument (Bool, (Bool, Qubit)) , and so on. Clearly,
in this case Foo cannot be monomorphized at compile-time.

Additional restrictions apply to cycles in the call graph that involve only type parametrized callables. Each
callable needs to be invoked with the same set of type arguments after traversing the cycle.
Discussion
It would be possible to be less restrictive and require that for each callable in the cycle, there is a finite
number of cycles after which it is invoked with the original set of type arguments, such as it is the case for
the following function:
function Bar<'T1,'T2,'T3>(a1:'T1, a2:'T2, a3:'T3) : Unit{
Bar<'T2,'T3,'T1>(a2, a3, a1);
}

For simplicity, the more restrictive requirement is enforced. Note that for cycles that involve at least one
concrete callable without any type parameter, such a callable will ensure that the type parametrized callables
within that cycle are always called with a fixed set of type arguments.
Grammar
13/05/2021 • 2 minutes to read

A reference implementation of the Q# grammar is available in the ANTLR4 format. The grammar source files are
listed below:
QSharpLexer.g4 describes the lexical structure of Q#.
QSharpParser.g4 describes the syntax of Q#.
The Q# grammar uses actions and semantic predicates. These features allow grammars to include custom
source code in the ANTLR-generated parser, which means that the code needs to be written in the same
language as the ANTLR target language. If you are using the Q# grammar to generate parsers in a language
other than Java, you may need to update the code used by the actions and semantic predicates to match the
target language. Target-specific code is marked by curly braces { } in the grammar.
Simuladores quânticos
15/04/2021 • 2 minutes to read

Simuladores quânticos são programas de software executados em computadores clássicos e atuam como um
computador de destino para um programa Q#. Assim, é possível executar e testar programas quânticos em um
ambiente que prevê como os qubits reagem a diferentes operações. Este artigo descreve os simuladores
quânticos incluídos no QDK (Quantum Development Kit) e maneiras diferentes de passar um programa Q# para
os simuladores quânticos, por exemplo, por meio da linha de comando ou usando o código de driver escrito em
uma linguagem clássica.

Atualizar os simuladores quânticos do QDK (Quantum Development


Kit)
O simulador quântico é responsável por fornecer implementações de primitivos quânticos para o algoritmo.
Isso inclui operações primitivas, como H , CNOT e Measure , bem como gerenciamento e acompanhamento de
qubits. O QDK inclui diferentes classes de simuladores quânticos que representam diferentes maneiras de
simular o mesmo algoritmo quântico.
Cada tipo de simulador quântico pode fornecer diferentes implementações desses primitivos. Por exemplo, o
simulador de estado completo executa o algoritmo quântico ao simular totalmente o vetor de estado quântico,
enquanto o simulador de rastreamento de computador quântico não considera o estado quântico real. Em vez
disso, ele rastreia o uso de portões, qubits e outros recursos para o algoritmo.
Classes de computador quântico
No futuro, o QDK definirá classes adicionais de computadores quânticos para dar suporte a outros tipos de
simulação e à execução em hardware quântico. Permitir que o algoritmo permaneça constante enquanto varia a
implementação de computador subjacente facilita o teste e a depuração de um algoritmo em simulação e, em
seguida, executa-o em um hardware real com a confiança de que o algoritmo não foi alterado.
O QDK inclui várias classes de computadores quânticos, todas definidas no namespace
Microsoft.Quantum.Simulation.Simulators .

SIM UL A DO R C L A SSE DESC RIÇ Ã O

Simulador de estado completo QuantumSimulator Executa e depura algoritmos quânticos


e é limitado a cerca de 30 qubits.

Avaliador de recursos simples ResourcesEstimator Realiza uma análise de nível superior


dos recursos necessários para executar
um algoritmo quântico e dá suporte a
milhares de qubits.

Avaliador de recursos baseado em QCTraceSimulator Executa uma análise avançada de


rastreamento consumos de recursos para todo o
grafo de chamadas do algoritmo e dá
suporte a milhares de qubits.
SIM UL A DO R C L A SSE DESC RIÇ Ã O

Simulador do Toffoli ToffoliSimulator Simula os algoritmos quânticos que


são limitados a X , CNOT e a
operações quânticas X com vários
controles e dá suporte a milhões de
qubits.

Invocando o simulador quântico


Em Maneiras de executar um Q# programa, três modos de passar o código Q# para o simulador quântico são
demonstrados:
Usando a linha de comando
Usando um programa de host em Python
Usando um programa de host em C#
Os computadores quânticos são instâncias de classes .NET normais e, portanto, são criadas invocando o
construtor delas, assim como qualquer classe .NET. O procedimento depende de como você executa o programa
Q#.

Próximas etapas
Para saber detalhes sobre como invocar computadores de destino para programas Q# em ambientes
distintos, confira Maneiras de executar um Q# programa.
Simulador de estado completo Quantum
Development Kit (QDK)
01/05/2021 • 2 minutes to read

O QDK oferece um simulador de estado completo que simula um computador quântico em seu computador
local. Você pode usar o simulador de estado completo para executar e depurar algoritmos quânticos gravados
em Q#, utilizando até 30 qubits. O simulador de estado completo é semelhante ao simulador do Quantum
usado na interface do usuário do LIQ$Ui|\rangle$ da Microsoft Research.

Como invocar e executar o simulador de estado completo


Você expõe o simulador de estado completo por meio da classe QuantumSimulator . Para obter detalhes
adicionais, consulte Maneiras de executar um Q# programa.
Como invocar o simulador a partir de C#
Crie uma instância da classe QuantumSimulator e, em seguida, passe-a para o método Run de uma operação
quântica, juntamente com quaisquer parâmetros adicionais.

using (var sim = new QuantumSimulator())


{
var res = myOperation.Run(sim).Result;
///...
}

Como a classe QuantumSimulator implementa a interface IDisposable, você deve chamar o método Dispose
assim que não precisar mais da instância do simulador. A melhor maneira de fazer isso é encapsular a
declaração do simulador e as operações em uma instrução de uso, que chama automaticamente o método
Dispose .

Como invocar o simulador a partir do Python


Use o método simulate() da biblioteca Q# do Python com a operação Q# importada :

qubit_result = myOperation.simulate()

Como invocar o simulador da linha de comando


Ao executar um programa Q# a partir da linha de comando, o simulador de estado completo é o computador de
destino padrão. Opcionalmente, você pode usar o parâmetro --simulator (ou o atalho -s ) para especificar o
computador de destino desejado. Ambos os comandos a seguir executam um programa usando o simulador de
estado completo.

dotnet run
dotnet run -s QuantumSimulator

Como invocar o simulador de notebooks Juptyer


Use o Q# comando mágico I %simulate para executar a Q# operação.

%simulate myOperation
Como propagar o simulador
Por padrão, o simulador de estado completo usa um gerador de número aleatório para simular a aleatoriedade
quântica. Para fins de testes, pode ser útil ter resultados determinísticos. Em um programa em C#, você pode
fazer isso fornecendo uma semente para o gerador de números aleatórios no QuantumSimulator Construtor por
meio do randomNumberGeneratorSeed parâmetro.

using (var sim = new QuantumSimulator(randomNumberGeneratorSeed: 42))


{
var res = myOperationTest.Run(sim).Result;
///...
}

Como configurar threads


O simulador de estado completo usa o OpenMP para paralelizar a álgebra linear necessária. Por padrão, o
OpenMP usa todos os threads de hardware disponíveis, o que significa que programas com pequenos números
de qubits geralmente são executados lentamente, pois a coordenação que é necessária diminui o trabalho real. É
possível corrigir isso definindo a variável de ambiente OMP_NUM_THREADS para um número pequeno. Como regra
prática, configure um thread para até quatro qubits e, em seguida, um thread adicional por qubit. Talvez seja
necessário ajustar a variável dependendo do algoritmo.

Confira também
Avaliador de recursos quânticos
Simulador quântico do Toffoli
Simulador de rastreamento quântico
Avaliador de recursos do QDK (Quantum
Development Kit)
15/05/2021 • 5 minutes to read

Como o nome indica, a classe ResourcesEstimator estima os recursos necessários para executar uma
determinada instância de uma operação Q# em um computador quantum. Para isso, ela executa a operação
quantum sem realmente simular o estado de um computador quantum. Dessa forma, ela estima recursos para
as operações em Q# que usam milhares de qubits, desde que a parte clássica do código seja executada em um
tempo razoável.
O avaliador de recursos é criado sobre o simulador de rastreamento Quantum, que fornece um conjunto mais
completo de métricas e ferramentas para ajudar a depurar programas Q#.

Como invocar e executar o avaliador de recursos


Você pode usar o avaliador de recursos para executar qualquer operação Q#. Para mais detalhes, consulte
Maneiras de executar um programa Q#.
Como invocar o avaliador de recursos a partir de C#
Assim como acontece com outros computadores de destino, primeiramente você cria uma instância da classe
ResourcesEstimator e, em seguida, passa-a como o primeiro parâmetro do método Run de uma operação.

Observe que, diferentemente da classe QuantumSimulator , a classe ResourcesEstimator não implementa a


interface IDisposable e, portanto, você não precisa colocá-la em uma instrução using .

using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;

namespace Quantum.MyProgram
{
class Driver
{
static void Main(string[] args)
{
ResourcesEstimator estimator = new ResourcesEstimator();
MyQuantumProgram.Run(estimator).Wait();
Console.WriteLine(estimator.ToTSV());
}
}
}

Como mostra o exemplo, o ResourcesEstimator fornece o método ToTSV() , que gera uma tabela com TSV
(valores separados por tabulação). Você pode salvar a tabela em um arquivo ou exibi-la no console para análise.
Veja a seguir um exemplo de saída do programa anterior:
Metric Sum
CNOT 1000
QubitClifford 1000
R 0
Measure 4002
T 0
Depth 0
Width 2
BorrowedWidth 0

NOTE
Uma instância de ResourcesEstimator não redefine os cálculos em todas as execuções. Se você usar a mesma instância
para executar outra operação, ela agregará os novos resultados aos resultados existentes. Se precisar redefinir cálculos
entre execuções, crie uma nova instância para cada execução.

Como invocar o avaliador de recursos a partir do Python


Use o método estimate_resources() da biblioteca do Python com a operação Q# importada:

qubit_result = myOperation.estimate_resources()

Como invocar o avaliador de recursos pela linha de comando


Ao executar um programa Q# a partir da linha de comando, use o parâmetro --simulator (ou o atalho -s ) para
especificar o computador de destino ResourcesEstimator . O comando a seguir executa um programa usando o
avaliador de recursos:

dotnet run -s ResourcesEstimator

Como invocar o avaliador de recursos a partir de Juptyer Notebooks


Use o comando mágico IQ# %estimate para executar a operação Q#.

%estimate myOperation

Recuperação programática dos dados estimados


Além de uma tabela TSV, você pode recuperar programaticamente os recursos estimados durante a execução
por meio da propriedade Data do avaliador de recursos. A propriedade Data fornece uma instância
System.DataTable com duas colunas: Metric e Sum , indexadas pelos nomes de métricas.

O código a seguir mostra como recuperar e imprimir o número total de operações QubitClifford , T e CNOT
usadas por uma operação Q#:
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;

namespace Quantum.MyProgram
{
class Driver
{
static void Main(string[] args)
{
ResourcesEstimator estimator = new ResourcesEstimator();
MyQuantumProgram.Run(estimator).Wait();

var data = estimator.Data;


Console.WriteLine($"QubitCliffords: {data.Rows.Find("QubitClifford")["Sum"]}");
Console.WriteLine($"Ts: {data.Rows.Find("T")["Sum"]}");
Console.WriteLine($"CNOTs: {data.Rows.Find("CNOT")["Sum"]}");
}
}
}

Métricas relatadas
O avaliador de recursos acompanha as seguintes métricas:

M ÉT RIC A DESC RIÇ Ã O

CNOT A contagem de execuções de operações CNOT (também


conhecida como operações Pauli X Controladas).

QubitClifford A contagem de execuções de operações Clifford e Pauli de


qubit único.

Medida A contagem de execuções de qualquer medida.

R A contagem de execuções de qualquer rotação de qubit


único, com exceção das operações T , Clifford e Pauli.

T A contagem de execuções de operações T e seus


conjugados, incluindo as operações T , T_x = H.T.H e T_y =
Hy.T.Hy.

Profundidade Profundidade do circuito de quantum executado pela


operação Q# (confira abaixo). Por padrão, a métrica de
profundidade conta apenas portões T . Para obter mais
detalhes, confira Contador de profundidade.

Largura Largura do circuito de quantum executado pela operação Q#


(confira abaixo). Por padrão, a métrica de profundidade conta
apenas portões T . Para obter mais detalhes, confira
Contador de largura.

QubitCount O limite inferior para o número máximo de qubits alocados


durante a execução da operação Q#. Essa métrica pode não
ser compatível com Profundidade (veja abaixo).

BorrowedWidth O número máximo de qubits emprestados dentro da


operação Q#.
Profundidade, largura e QubitCount
As estimativas de profundidade e largura relatadas são compatíveis entre si. (Anteriormente, os dois números
eram obtidos, mas circuitos diferentes eram necessários para profundidade e largura.) Atualmente, ambas as
métricas nesse par podem ser obtidas pelo mesmo circuito ao mesmo tempo.
As seguintes métricas são relatadas:
Profundidade: para a operação raiz, o tempo necessário para executá-la assumindo tempos de portão
específicos. Para operações chamadas ou subsequentes, a diferença de tempo entre o tempo de disponibilidade
qubit mais recente no início e no fim da operação.
Largura: para a operação raiz, número de qubits usados para executá-la (e a operação que ele chama). Para
operações chamadas ou subsequentes, o adicional de qubits usados além dos qubits já usados no início da
operação.
Observe que os qubits reutilizados não contribuem para esse número. Por exemplo, se alguns qubits tiverem
sido liberados antes da operação A ser iniciada, e todos os qubits exigidos por essa operação A (e as operações
chamadas a partir de A) são atendidos com a reutilização de qubits liberados anteriormente, a largura da
operação A é relatada como 0. Os qubits emprestados com êxito não contribuem com a largura.
QubitCount: para a operação raiz, o número mínimo de qubits suficientes para executar esta operação (e as
operações chamadas a partir dela). Para operações chamadas ou subsequentes, o número mínimo de qubits
suficientes para executar essa operação separadamente. Esse número não inclui qubits de entrada. Ele inclui
qubits emprestados.
Há suporte para dois modos de operação. O modo é selecionado por meio da configuração
QCTraceSimulatorConfiguration.OptimizeDepth.
OptimizeDepth=true: QubitManager é desencorajado da reutilização de qubit e aloca novos qubits toda vez
que for solicitado por um qubit. Para a operação raiz, Profundidade se torna a profundidade mínima (limite
inferior). A Largura compatível é relatada para essa profundidade (as duas podem ser obtidas ao mesmo
tempo). Observe que essa largura provavelmente não será ideal, considerando a profundidade. QubitCount
pode ser menor que a Largura da operação raiz porque ela assume reutilização.
OptimizeDepth=false: QubitManager é incentivado a reutilizar qubits e reutilizará o qubits liberados antes de
alocar novos. Para a operação raiz, Largura se torna a largura mínima (limite inferior). A Profundidade
compatível é relatada na qual ela pode ser obtida. QubitCount será igual à Largura da operação raiz, supondo
que não haja empréstimo.

Como fornecer a probabilidade de resultados de medida


Você pode usar AssertMeasurementProbability operation do namespace Microsoft.Quantum.Diagnostics
namespace para fornecer informações sobre a probabilidade esperada de uma operação de medida. Para mais
informações, confira Simulador de rastreamento quântico

Confira também
Simulador de rastreamento quântico
Simulador quântico do Toffoli
Simulador de estado completo quântico
Simulador de rastreamento quântico do Microsoft
QDK (Quantum Development Kit)
15/04/2021 • 3 minutes to read

A classe QDK QCTraceSimulator executa um programa quântico sem realmente simular o estado de um
computador quântico. Por esse motivo, o simulador de rastreamento quântico pode executar programas
quânticos que usam milhares de qubits. Ele é útil para duas finalidades principais:
Depuração de código clássico que faça parte de um programa quântico.
Estimativa dos recursos necessários para executar determinada instância de um programa quântico em um
computador quântico. Na verdade, o Avaliador de recursos, que fornece um conjunto mais limitado de
métricas, é criado com base no simulador de rastreamento.

Invocando o simulador de rastreamento quântico


É possível usar o simulador de rastreamento quântico para executar qualquer operação Q#.
Assim como acontece com outros computadores de destino, primeiramente você cria uma instância da classe
QCTraceSimulator e, em seguida, passa-a como o primeiro parâmetro do método Run de uma operação.

Observe que, diferentemente da classe QuantumSimulator , a classe QCTraceSimulator não implementa a


interface IDisposable e, portanto, você não precisa colocá-la em uma instrução using .

using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;
using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators;

namespace Quantum.MyProgram
{
class Driver
{
static void Main(string[] args)
{
QCTraceSimulator sim = new QCTraceSimulator();
var res = MyQuantumProgram.Run(sim).Result;
System.Console.WriteLine("Press any key to continue...");
System.Console.ReadKey();
}
}
}

Como fornecer a probabilidade de resultados de medida


Como o simulador de rastreamento quântico não simula o estado real quântico, ele não pode calcular a
probabilidade de resultados de medida em uma operação.
Portanto, se uma operação incluir medidas, você precisará fornecer explicitamente essas probabilidades usando
a operação AssertMeasurementProbability operation do namespace Microsoft.Quantum.Diagnostics
namespace. O exemplo a seguir ilustra isso:
operation TeleportQubit(source : Qubit, target : Qubit) : Unit {
use qubit = Qubit();
H(qubit);
CNOT(qubit, target);
CNOT(source, qubit);
H(source);

AssertMeasurementProbability([PauliZ], [source], Zero, 0.5, "Outcomes must be equally likely", 1e-5);


AssertMeasurementProbability([PauliZ], [q], Zero, 0.5, "Outcomes must be equally likely", 1e-5);

if M(source) == One { Z(target); X(source); }


if M(q) == One { X(target); X(q); }
}

Quando o simulador de rastreamento quântico executar AssertMeasurementProbability , ele registrará isso


medindo PauliZ em source e q deverá mostrar um resultado igual a Zero com a probabilidade de 0,5 .
Quando ele executar a operação M posteriormente, ele localizará os valores gravados das probabilidades de
resultado e M retornará Zero ou One , com a probabilidade de 0,5 . Quando o mesmo código for executado
em um simulador que controla o estado quântico, esse simulador verificará se as probabilidades fornecidas em
AssertMeasurementProbability estão corretas.

Observe que, se houver pelo menos uma operação de medida que não está anotada usando
AssertMeasurementProbability , o simulador gerará um UnconstrainedMeasurementException .

Ferramentas de simulador de rastreamento quântico


O QDK inclui cinco ferramentas que você pode usar com o simulador de rastreamento quântico para detectar
bugs nos seus programas e executar estimativas de recursos do programa quântico:

F ERRA M EN TA DESC RIÇ Ã O

Verificador de entradas distintas Verifica se há conflitos potenciais com qubits compartilhados

Verificador de uso de qubits invalidados Verifica se o programa aplica uma operação a um qubit que
já foi liberado

Contador de operações primitivas Conta o número de primitivos usados em cada operação


invocada em um programa quântico

Contador de profundidade Coleta contagens que representam o limite inferior da


profundidade de cada operação invocada em um programa
quântico

Contador de largura Conta o número de qubits alocados e emprestados em cada


operação em um programa quântico

Cada uma dessas ferramentas é habilitada definindo sinalizadores apropriados no


QCTraceSimulatorConfiguration e, em seguida, passando a configuração para a declaração de QCTraceSimulator .
Para obter informações sobre como usar cada uma dessas ferramentas, confira os links na lista anterior. Para
obter mais informações sobre como configurar o QCTraceSimulator , confira QCTraceSimulatorConfiguration.

Métodos QCTraceSimulator
O QCTraceSimulator tem vários métodos internos para recuperar os valores das métricas rastreadas durante
uma operação quântica. Exemplos dos métodos QCTraceSimulator.GetMetric e QCTraceSimulator.ToCSV podem
ser encontrados nos artigos Contador de operações primitivo, Contador de profundidade e Contador de largura.
Para saber mais sobre todos os métodos disponíveis, confira QCTraceSimulator na referência da API do Q#.

Confira também
Avaliador de recursos quânticos
Simulador quântico do Toffoli
Simulador de estado completo quântico
Simulador de rastreamento do Quantum:
verificador de entradas distintas
13/05/2021 • 2 minutes to read

O verificador de entradas distintas faz parte do Simulador de rastreamento do Quantum do Quantum


Development Kit. Você pode usá-lo para detectar possíveis bugs no código causados por conflitos com os qubits
compartilhados.

Conflitos com qubits compartilhados


Considere o seguinte trecho de código Q# para ilustrar os problemas detectados pelo verificador de entradas
distintas:

operation ApplyBoth(
q1 : Qubit,
q2 : Qubit,
op1 : (Qubit => Unit),
op2 : (Qubit => Unit))
: Unit {
op1(q1);
op2(q2);
}

Ao examinar esse programa, assume-se que a ordem em que ele chama op1 e op2 não importa, porque q1 e
q2 são diferentes qubits, e as operações que atuam em diferentes qubits se alternam.

Considere este exemplo:

operation ApplyWithNonDistinctInputs() : Unit {


use qubits = Qubit[3];
let op1 = CNOT(_, qubits[1]);
let op2 = CNOT(qubits[1], _);
ApplyBoth(qubits[0], qubits[2], op1, op2);
}

Observe que op1 e op2 são obtidos usando o aplicativo parcial e compartilham um qubit. Quando você
chama ApplyBoth neste exemplo, o resultado da operação depende da ordem de op1 e op2 dentro de
ApplyBoth . Não é isso que se espera. Quando você habilita o verificador de entradas distintas, ele detecta essas
situações e gera um DistinctInputsCheckerException . Para saber mais, consulte DistinctInputsCheckerException
na biblioteca de APIs Q#.

Invocar o verificador de entradas distintas


Para executar o simulador de rastreamento do Quantum com o verificador de entradas distintas, crie uma
instância QCTraceSimulatorConfiguration, defina a propriedade UseDistinctInputsChecker como true e crie
uma nova instância QCTraceSimulator com QCTraceSimulatorConfiguration como o parâmetro.

var config = new QCTraceSimulatorConfiguration();


config.UseDistinctInputsChecker = true;
var sim = new QCTraceSimulator(config);
Uso do verificador de entradas distintas em um programa de host C#
Veja a seguir um exemplo de programa de host C# que usa o simulador de rastreamento do Quantum com o
verificador de entradas distintas habilitado:

using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;
using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators;

namespace Quantum.MyProgram
{
class Driver
{
static void Main(string[] args)
{
var traceSimCfg = new QCTraceSimulatorConfiguration();
traceSimCfg.UseDistinctInputsChecker = true; //enables distinct inputs checker
QCTraceSimulator sim = new QCTraceSimulator(traceSimCfg);
var res = MyQuantumProgram.Run().Result;
System.Console.WriteLine("Press any key to continue...");
System.Console.ReadKey();
}
}
}

Confira também
Simulador de rastreamento do Quantum do Quantum Development Kit.
Referência da API QCTraceSimulator.
Referência da API QCTraceSimulatorConfiguration.
Referência da API DistinctInputsCheckerException.
Simulador de rastreamento do Quantum:
verificador de uso de qubits invalidados
13/05/2021 • 2 minutes to read

O verificador de uso de qubits invalidados faz parte do Simulador de rastreamento do Quantum do Quantum
Development Kit. Você pode usá-lo para detectar possíveis bugs no código causados por qubits inválidos.

Qubits inválidos
Considere o seguinte trecho de código Q# para ilustrar os problemas detectados pelo verificador de uso de
qubits invalidados:

operation UseReleasedQubit() : Unit {


mutable q = new Qubit[1];
use ans = Qubit();
set q w/= 0 <- ans;
H(q[0]);
}

Quando você aplica a operação H ao q[0] , ele aponta para um qubit já liberado, o que pode causar um
comportamento indefinido. Quando o verificador de uso de qubits invalidados estiver habilitado, ele lançará a
exceção InvalidatedQubitsUseCheckerException , se o programa aplicar uma operação a um qubit já liberado.
Para obter mais informações, consulte InvalidatedQubitsUseCheckerException.

Invocar o verificador uso de qubits invalidados


Para executar o simulador de rastreamento do Quantum com o verificador de uso de qubits invalidados, crie
uma instância QCTraceSimulatorConfiguration, defina a propriedade UseInvalidatedQubitsUseChecker como
true e crie uma nova instância QCTraceSimulator com QCTraceSimulatorConfiguration como o parâmetro.

var config = new QCTraceSimulatorConfiguration();


config.UseInvalidatedQubitsUseChecker = true;
var sim = new QCTraceSimulator(config);

Utilização do verificador de uso de qubits invalidados em um


programa de host C#
Veja a seguir um exemplo de programa de host C# que usa o simulador de rastreamento do Quantum com o
verificador de uso de qubits invalidados habilitado:
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;
using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators;

namespace Quantum.MyProgram
{
class Driver
{
static void Main(string[] args)
{
var traceSimCfg = new QCTraceSimulatorConfiguration();
traceSimCfg.UseInvalidatedQubitsUseChecker = true; // enables UseInvalidatedQubitsUseChecker
QCTraceSimulator sim = new QCTraceSimulator(traceSimCfg);
var res = MyQuantumProgram.Run().Result;
System.Console.WriteLine("Press any key to continue...");
System.Console.ReadKey();
}
}
}

Confira também
Simulador de rastreamento do Quantum do Quantum Development Kit.
Referência da API QCTraceSimulator.
Referência da API QCTraceSimulatorConfiguration.
Referência da API InvalidatedQubitsUseCheckerException.
Simulador de rastreamento quantum: contador de
operações primitivas
13/05/2021 • 2 minutes to read

O contador de operações primitivas faz parte do Simulador de rastreamento do quantum do Quantum


Development Kit. Ele conta o número de processos primitivos usados em cada operação invocada em um
programa quantum.
Todas as operações Microsoft.Quantum.Intrinsic namespace são expressas em termos de rotações de qubit
único, operações T, operações de Clifford de qubit único, operações CNOT e medidas de Pauli de vários qubit
observáveis. O Contador de Operações Primitivas agrega e coleta estatísticas sobre todas as bordas do grafo de
chamada da operação.

Invocar contador de operações primitivas


Para executar o simulador de rastreamento do quantum com o contador de operações primitivas, crie uma
instância QCTraceSimulatorConfiguration, defina a propriedade UsePrimitiveOperationsCounter como true e
crie uma nova instância QCTraceSimulator com QCTraceSimulatorConfiguration como o parâmetro.

var config = new QCTraceSimulatorConfiguration();


config.UsePrimitiveOperationsCounter = true;
var sim = new QCTraceSimulator(config);

Utilização do contador de operações primitivas em um programa de


host C#
O exemplo de C# a seguir nesta seção conta quantas operações T operation são necessárias para implementar a
operação CCNOT operation, com base no seguinte código de exemplo Q#:

open Microsoft.Quantum.Intrinsic;
operation ApplySampleWithCCNOT() : Unit {

use qubits = Qubit[3]);


CCNOT(qubits[0], qubits[1], qubits[2]);
T(qubits[0]);
}

Para verificar se CCNOT requer sete operações T e que ApplySampleWithCCNOT executa oito operações T , use o
seguinte código C#:

// using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators;
// using System.Diagnostics;
var config = new QCTraceSimulatorConfiguration();
config.UsePrimitiveOperationsCounter = true;
var sim = new QCTraceSimulator(config);
var res = ApplySampleWithCCNOT.Run(sim).Result;

double tCountAll = sim.GetMetric<ApplySampleWithCCNOT>(PrimitiveOperationsGroupsNames.T);


double tCount = sim.GetMetric<Primitive.CCNOT, ApplySampleWithCCNOT>(PrimitiveOperationsGroupsNames.T);
A primeira parte do programa executa ApplySampleWithCCNOT . A segunda parte usa o método
QCTraceSimulator.GetMetric para recuperar o número de operações T executadas por ApplySampleWithCCNOT :

Quando você chama GetMetric com dois parâmetros de tipo, ele retorna o valor da métrica associada a uma
determinada borda de grafo de chamada. No exemplo anterior, o programa chama a operação Primitive.CCNOT
dentro de ApplySampleWithCCNOT , portanto o grafo de chamada contém a borda
<Primitive.CCNOT, ApplySampleWithCCNOT> .

Para recuperar o número de operações CNOT usadas, adicione a seguinte linha:

double cxCount = sim.GetMetric<Primitive.CCNOT, ApplySampleWithCCNOT>(PrimitiveOperationsGroupsNames.CX);

Por fim, gere todas as estatísticas coletadas pelo Contador de Operações Primitivas no formato CSV usando o
seguinte:

string csvSummary = sim.ToCSV()[MetricsCountersNames.primitiveOperationsCounter];

Confira também
Simulador de rastreamento Quantum do Quantum Development Kit.
Referência da API QCTraceSimulator.
Referência da API QCTraceSimulatorConfiguration.
Referência da API PrimitiveOperationsGroupsNames.
Simulador de rastreamento Quantum: contador de
profundidade
13/05/2021 • 2 minutes to read

O contador de profundidade faz parte do Simulador de rastreamento do Quantum do Quantum Development


Kit. Você pode usá-lo para coletar contagens que representam o limite inferior da profundidade de cada
operação invocada em um programa quantum.

Valores de profundidade
Por padrão, todas as operações têm uma profundidade de 0 , exceto a operação T que tem uma profundidade
de 1 . Isso significa que, por padrão, apenas a profundidade T das operações é computada (o que geralmente é
desejável). O contador de profundidade agrega e coleta estatísticas sobre todas as bordas do grafo de
chamadada operação.
Todas as operações Microsoft.Quantum.Intrinsic namespace são expressas em termos de rotações de qubit
único, operações T operation, operações de Clifford de qubit único, operações CNOT operation e medidas de
Pauli de vários qubit observáveis. Os usuários podem definir a profundidade para cada uma das operações
primitivas por meio do campo gateTimes de QCTraceSimulatorConfiguration.

Invocar o contador de profundidade


Para executar o simulador de rastreamento do Quantum com o contador de profundidade, crie uma instância
QCTraceSimulatorConfiguration, defina a propriedade UseDepthCounter como true e crie uma nova instância
QCTraceSimulator com QCTraceSimulatorConfiguration como o parâmetro.

var config = new QCTraceSimulatorConfiguration();


config.UseDepthCounter = true;
var sim = new QCTraceSimulator(config);

Utilização do contador de profundidade em um programa de host C#


O exemplo de C# a seguir nesta seção computa a profundidade T da operação CCNOT , com base no seguinte
código de exemplo Q#:

open Microsoft.Quantum.Intrinsic;

operation ApplySampleWithCCNOT() : Unit {


use qubits = Qubit[3]);
CCNOT(qubits[0], qubits[1], qubits[2]);
T(qubits[0]);
}

Para verificar se CCNOT tem a profundidade T 5e ApplySampleWithCCNOT tem profundidade T 6 , use o


seguinte código C#:
using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators;
using System.Diagnostics;
var config = new QCTraceSimulatorConfiguration();
config.UseDepthCounter = true;
var sim = new QCTraceSimulator(config);
var res = ApplySampleWithCCNOT.Run(sim).Result;

double tDepth = sim.GetMetric<Intrinsic.CCNOT, ApplySampleWithCCNOT>(DepthCounter.Metrics.Depth);


double tDepthAll = sim.GetMetric<ApplySampleWithCCNOT>(DepthCounter.Metrics.Depth);

A primeira parte do programa executa ApplySampleWithCCNOT . A segunda parte usa o método GetMetric para
recuperar a profundidade T de CCNOT e ApplySampleWithCCNOT .
Por fim, gere todas as estatísticas coletadas pelo contador de profundidade no formato CSV usando o seguinte:

string csvSummary = sim.ToCSV()[MetricsCountersNames.depthCounter];

Confira também
Simulador de rastreamento Quantum do Quantum Development Kit.
Referência da API QCTraceSimulator.
Referência da API QCTraceSimulatorConfiguration.
Referência da API MetricsNames.DepthCounter.
Simulador de rastreamento Quantum: contador de
largura
13/05/2021 • 2 minutes to read

O contador de largura faz parte do Simulador de rastreamento do Quantum do Quantum Development Kit. Ele
conta o número de qubits alocados e emprestados em cada operação em um programa Q#. Algumas operações
primitivas podem alocar qubits extras, por exemplo, operações X controladas multiplicadas ou operações T
controladas.

Invocar contador de largura


Para executar o simulador de rastreamento do quantum com o contador de largura, crie uma instância
QCTraceSimulatorConfiguration, defina a propriedade UseWidthCounter como true e crie uma nova instância
QCTraceSimulator com QCTraceSimulatorConfiguration como o parâmetro.

var config = new QCTraceSimulatorConfiguration();


config.UseWidthCounter = true;
var sim = new QCTraceSimulator(config);

Utilização do contador de largura em um programa de host C#


O exemplo de C# a seguir nesta seção computa o número de qubits extras alocados pela implementação de
uma operação X operation de multiplicação controlada, com base no seguinte código de exemplo Q#:

open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Arrays;
operation ApplyMultiControlledX( numberOfQubits : Int ) : Unit {
use qubits = Qubit[numberOfQubits];
Controlled X (Rest(qubits), Head(qubits));
}

A operação X operation de multiplicação controlada atua em um total de cinco qubits, aloca dois qubits
auxiliares e tem uma largura de entrada de 5 . Use o seguinte programa C# para verificar as contagens:

var config = new QCTraceSimulatorConfiguration();


config.UseWidthCounter = true;
var sim = new QCTraceSimulator(config);
int totalNumberOfQubits = 5;
var res = ApplyMultiControlledX.Run(sim, totalNumberOfQubits).Result;

double allocatedQubits =
sim.GetMetric<Primitive.X, ApplyMultiControlledX>(
WidthCounter.Metrics.ExtraWidth,
functor: OperationFunctor.Controlled);

double inputWidth =
sim.GetMetric<Primitive.X, ApplyMultiControlledX>(
WidthCounter.Metrics.InputWidth,
functor: OperationFunctor.Controlled);

A primeira parte do programa executa a operação ApplyMultiControlledX . A segunda parte usa o método
QCTraceSimulator.GetMetric para recuperar o número de qubits alocados, bem como o número de qubits que a
operação Controlled X recebeu como entrada.
Por fim, gere todas as estatísticas coletadas pelo contador de largura no formato CSV usando o seguinte:

string csvSummary = sim.ToCSV()[MetricsCountersNames.widthCounter];

Confira também
Simulador de rastreamento do Quantum do Quantum Development Kit.
Referência da API QCTraceSimulator.
Referência da API QCTraceSimulatorConfiguration.
Referência da API MetricsNames.WidthCounter.
Simulador Toffoli do QDK (Quantum Development
Kit)
01/05/2021 • 2 minutes to read

O simulador Toffoli do QDK é um simulador com um propósito especial e escopo limitado e só dá suporte a X ,
CNOT e operações quânticas X multicontroladas. Toda a lógica clássica e computações estão disponíveis.

Embora o simulador do Toffoli seja mais restrito em funcionalidade do que o simulador de estado completo, ele
tem a vantagem de ser capaz de simular muito mais qubits. O simulador do Toffoli pode ser usado com milhões
de qubits, enquanto o simulador de estado completo é limitado a cerca de 30 qubits. Isso é útil, por exemplo,
com oracles que avaliam funções booleanas. Elas podem ser implementadas usando o conjunto limitado de
algoritmos suportados e testadas em um grande número de qubits.

Invocando o simulador do Toffoli


Você expõe o simulador do Toffoli por meio da ToffoliSimulator classe. Para mais detalhes, consulte Maneiras
de executar um Q# programa.
Invocando o simulador do Toffoli em C#
Assim como acontece com outros computadores de destino, primeiramente você cria uma instância da classe
ToffoliSimulator e, em seguida, passa-a como o primeiro parâmetro do método Run de uma operação.

Observe que, diferentemente da classe QuantumSimulator , a classe ToffoliSimulator não implementa a


interface IDisposable e, portanto, você não precisa colocá-la em uma instrução using .

var sim = new ToffoliSimulator();


var res = myOperation.Run(sim).Result;
///...

Invocando o simulador do Toffoli do Python


Use o método toffoli_simulate () da biblioteca do Python com a Q# operação importada:

qubit_result = myOperation.toffoli_simulate()

Invocando o simulador do Toffoli da linha de comando


Ao executar um Q# programa a partir da linha de comando, use o parâmetro --simulator (ou o atalho -s ) para
especificar o computador de destino do simulador do Toffoli. O comando a seguir executa um programa usando
o avaliador de recursos:

dotnet run -s ToffoliSimulator

Invocando o simulador do Toffoli do Juptyer Notebooks


Use o IQ# comando mágico % toffoli para executar a Q# operação.

%toffoli myOperation
Operações com suporte
O simulador do Toffoli dá suporte a:
Rotações e exponenciação Paulis, como R e Exp , quando a operação resultante é igual a X ou a matriz da
identidade.
Operações de medição e declaração, mas apenas na Z base Pauli. Observe que a probabilidade de uma
operação de medida é sempre 0 ou 1 ; não há aleatoriedade no simulador de Toffoli.
Funções DumpMachine e DumpRegister . Ambas as funções produzem o estado de Z base atual de cada qubit,
um qubit por linha.

Especificando o número de qubits


Por padrão, uma ToffoliSimulator instância aloca espaço para 65.536 qubits. Se o algoritmo exigir mais qubits,
você poderá especificar a contagem de qubit fornecendo um valor para o qubitCount parâmetro para o
construtor. Cada qubit adicional requer apenas um byte de memória, portanto, não há nenhum custo
significativo para superestimar o número de qubits que você precisará.
Por exemplo:

var sim = new ToffoliSimulator(qubitCount: 1000000);


var res = myLargeOperation.Run(sim).Result;

Confira também
Avaliador de recursos quânticos
Simulador de rastreamento quântico
Simulador de estado completo quântico
Visão geral das bibliotecas de Q#
23/04/2021 • 2 minutes to read

O QDK (Quantum development kit) é fornecido com várias bibliotecas para facilitar o desenvolvimento de
aplicativos quânticos em Q#. Nesta seção da documentação, descrevemos essas bibliotecas e como usá-las em
seus programas.
Bibliotecas padrão : Esta seção descreve o prelúdio, que define a interface entre programas Q# e os
computadores de destino, e o cânone, uma biblioteca de Q# que fornece operações e funções de uso geral
para escrever programas Q#.
Biblioteca de química quântica : esta seção descreve a biblioteca química quântica, que fornece um
modelo de dados para carregar representações de operações de simulação quântica e de Hamiltonianos
fermiônicos, bem como funções que atuam nessas representações.
Biblioteca de numéricos quânticos : esta seção descreve a biblioteca de numéricos quânticos, que fornece
implementações para uma gama de funções matemáticas. Ela dá suporte a representações de ponto fixo e
inteiro (com e sem sinal).
Biblioteca de machine learning quântica : Esta seção descreve a biblioteca de machine learning quântica,
que fornece uma implementação dos classificadores sequenciais que aproveitam a computação quântica
para entender os dados.
As fontes das bibliotecas, bem como exemplos de código, podem ser obtidos no GitHub. Confira Licenciamento
para obter mais informações. Observe que as referências do pacote ("binários") também estão disponíveis para
as bibliotecas e fornecem outra maneira de incluir as bibliotecas em projetos. Um modo conveniente de obtê-las
é por meio do NuGet.
Introdução às Bibliotecas Padrão Q#
15/04/2021 • 2 minutes to read

O Q# é compatível com uma variedade de diferentes operações, funções e tipos definidos pelo usuário úteis que
abrangem as bibliotecas padrão do Q#. O Microsoft.Quantum.Development.Kit pacote NuGet instalado durante a
instalação e validação fornece o compilador do Q# e partes da biblioteca padrão implementadas pelos
computadores de destino. O Microsoft.Quantum.Standard pacote oferece a parte das bibliotecas padrão Q#
portáteis entre os computadores de destino.
Os símbolos definidos pelas bibliotecas padrão Q# são definidos em muito mais detalhes na documentação da
API.
Nas seções a seguir, descreveremos os recursos mais evidentes de cada parte da biblioteca padrão e
forneceremos algum contexto sobre como cada recurso pode ser usado na prática.
O Prelúdio
04/05/2021 • 12 minutes to read

O Q# compilador e os computadores de destino incluídos no Quantum Development Kit fornecem um conjunto


de funções intrínsecas e operações que podem ser usadas ao gravar programas quânticos em Q#.

Operações e funções intrínsecas


As operações intrínsecas definidas na biblioteca padrão basicamente se enquadram em uma das várias
categorias:
Funções clássicas essenciais, coletadas no Microsoft.Quantum.Core namespace namespace.
Operações que representam unidades compostas por Clifford e portas de $T$ .
Operações que representam rotações sobre vários operadores.
Operações de implementação de medidas.
Como o conjunto de Clifford + porta $T$ é universal para a computação quântica, essas operações são
suficientes para implementar aproximadamente qualquer algoritmo quântico dentro de um erro pequeno
insignificante. Ao fornecer rotações também, Q# permite que o programador trabalhe dentro da unidade qubit
única e da biblioteca de porta CNOT. Essa biblioteca é muito mais fácil de pensar porque não exige que o
programador expresse diretamente a decomposição Clifford + $T$ e porque existem métodos altamente
eficientes para a compilação de unidades de qubit únicas em Clifford e portas de $T$ (consulte aqui para obter
mais informações).
Sempre que possível, as operações definidas no prelúdio que atuam em qubits permitem a aplicação da
Controlled variante, de modo que o computador de destino execute a decomposição apropriada.

Muitas das funções e operações definidas nesta parte do prelúdio estão no @"microsoft.quantum.intrinsic"
namespace, de modo que a maioria dos Q# arquivos de origem terá uma open Microsoft.Quantum.Intrinsic;
diretiva imediatamente após a declaração de namespace inicial. O Microsoft.Quantum.Core namespace
namespace é aberto automaticamente, para que funções como o Length function possam ser usadas sem uma
open instrução.

Operações unitárias comuns de Qubit único


O prelúdio também define muitas operações comuns de qubit único. Todas essas operações permitem tanto
Controlled como Adjoint functors.

Operadores Pauli
A operação X operation implementa o operador Pauli $X$. Isto às vezes também é conhecido como a porta NOT
. Ele tem assinatura (Qubit => Unit is Adj + Ctl) . Ele corresponde à unidade de qubit único:
\begin{Equation} \begin{bmatrix} 0 & 1 \ \ % FIXME: isso atualmente usa o quadwhack hack. 1 & 0
\end{bmatrix} \end{equation}
A operaçãoY operation implementa o operador Pauli $X$. Ele tem assinatura (Qubit => Unit is Adj + Ctl) . Ele
corresponde à unidade de qubit único:
\begin{Equation} \begin{bmatrix} 0 & -i \ \ % FIXME: isso atualmente usa o quadwhack hack. i & 0 \end{bmatrix}
\end{equation}
A operaçãoZ operation implementa o operador Pauli $Z$. Ele tem assinatura (Qubit => Unit is Adj + Ctl) . Ele
corresponde à unidade de qubit único:
\begin{Equation} \begin{bmatrix} 1 & 0; 0 \\ % FIXME: isso atualmente usa o quadwhack hack. 0 & -1
\end{bmatrix} \end{equation}
Abaixo, vemos essas transformações mapeadas para a esfera Bloch (o eixo de rotação em cada caso é realçado
em vermelho):

É importante observar que aplicar a mesma porta Pauli duas vezes ao mesmo qubit cancela a operação (porque
você executou uma rotação completa de 2π (360°) na superfície para a esfera Bloch, chegando ao ponto de
partida). Isso nos leva à seguinte identidade:
$$ X^2=Y^2=Z^2=\boldone $$
Isso pode ser visualizado na esfera Bloch:
Outros Cliffords de qubit único
A operação H operation implementa a porta Hadamard. Isso troca os eixos Pauli $X$ e $Z$ do qubit de destino,
de modo que $H\ket{0} = \ket{+} \mathrel{:=} (\ket{0} + \ket{1}) / \sqrt{2}$ and $H\ket{+} = \ket{0}$. Ele tem
assinatura (Qubit => Unit is Adj + Ctl) e corresponde à unidade de qubit único:
\begin{equation} \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 1 \\ % FIXME: isso usa atualmente o quadwhack hack. 1
& -1 \end{bmatrix} \end{equation}
A porta Hadamard é particularmente importante, pois pode ser usada para criar uma superposição dos estados
$\ket{0}$ e $\ket{1}$. Na representação da esfera Bloch, é mais fácil imaginar isso como uma rotação de
$\ket{\psi}$ em volta do eixo x por radianos $\pi$ ($180^\circ$) seguido por uma rotação (no sentido horário)
ao lado do eixo y em radianos $\pi/2$ ($90^\circ$):
A operaçãoS operation implementa a porta de fase $S$. Esta é a raiz quadrada da matriz da operação Pauli $Z$.
Ou seja, $S^2 = Z$. Ele tem assinatura (Qubit => Unit is Adj + Ctl) e corresponde à unidade de qubit único:
\begin{Equation} \begin{bmatrix} 1 & 0; 0 \\ % FIXME: isso atualmente usa o quadwhack hack. 0 & i
\end{bmatrix} \end{equation}
Escalas
Além das operações Pauli e Clifford acima, o Q# prelúdio fornece uma variedade de formas de expressar
rotações. Conforme descrito em operações de qubit único, a capacidade de girar é essencial para algoritmos
quânticos.
Começamos com a recuperação de que podemos expressar qualquer operação de qubit único usando o $H$ e
portas $T$, onde $H$ é a operação Hadamard e onde \begin{equation} T \mathrel{:=} \begin{bmatrix} 1 & 0 \ \
% FIXME: atualmente, isso usa quad back whack hack. 0 & e^{i \pi / 4} \end{bmatrix} \end{equation} Esta é a raiz
quadrada da operação S operation, de modo que $T^2 = S$. A porta $T$ é, por sua vez, implementada pela
operação T operation e tem assinatura (Qubit => Unit is Adj + Ctl) , indicando que se trata de uma operação
unitária em um qubit único.
Embora isso seja, a princípio, suficiente para descrever qualquer operação arbitrária de qubit único, diferentes
computadores de destino podem ter representações mais eficientes para rotações sobre operadores Pauli, de
modo que o prelúdio inclui várias maneiras de expressar convenientemente tais rotações. O mais básico deles é
a operação R operation, que implementa uma rotação em torno de um eixo Pauli específico, \begin{equation}
R(\sigma, \phi) \mathrel{:=} \exp(-i \phi \sigma / 2), \end{equation} onde $\sigma$ é um operador Pauli, $\phi$
é um ângulo e $\exp$ representa o exponencial de matriz. Ele tem assinatura
((Pauli, Double, Qubit) => Unit is Adj + Ctl) , onde as duas primeiras partes da entrada representam os
argumentos clássicos $\sigma$ e $\phi$ necessários para especificar o operador unitário $R(\sigma, \phi)$.
Podemos aplicar parcialmente $\sigma$ e $\phi$ para obter uma operação cujo tipo é o de uma unidade de
qubit único. Por exemplo, R(PauliZ, PI() / 4, _) tem tipo (Qubit => Unit is Adj + Ctl) .
NOTE
A operaçãoR operation divide o ângulo de entrada por 2 e o multiplica por -1. Para rotações $Z$, isso significa que
$\ket{0}$ eigenstate é rotacionado por $-\phi / 2$ e o $\ket{1}$ eigenstate é rotacionado por $\phi/$2, de modo que
$\ket{1}$ eigenstate é rotacionado por $\phi$ em relação ao $\ket {0}$ eigenstate.
Em particular, isso significa que T e R(PauliZ, PI() / 8, _) diferem apenas por uma fase globalirrelevante. Por esse
motivo, $T$ às vezes é conhecido como a porta $\frac{\pi}{8}$.
Observe também que girar em volta PauliI simplesmente aplica uma fase global de $\phi / 2$. Embora essas fases
sejam irrelevantes, como argumentadas nos documentos conceituais, elas são relevantes para PauliI rotações
controladas.

Em algoritmos quânticos, geralmente é útil expressar rotações como frações dyadic, de modo que $\phi = \pi k /
2^n$ para algumas $k \in \mathbb{Z}$ e $n \in \mathbb{N}$. A operaçãoRFrac operation implementa uma
rotação em um eixo Pauli especificado usando essa convenção. Ele difere de R operation quando o ângulo de
rotação é especificado como duas entradas do tipo Int , interpretado como uma fração dyadic. Assim, RFrac
tem assinatura ((Pauli, Int, Int, Qubit) => Unit is Adj + Ctl) . Ele implementa a unidade de qubit único
$\exp(i \pi k \sigma / 2^n)$, onde $\sigma$ é a matriz Pauli correspondente ao primeiro argumento, $k$ é o
segundo argumento e $n$ é o terceiro argumento. RFrac(_,k,n,_) é o mesmo que R(_,-πk/2^n,_) ; note que o
ângulo é o negativo da fração.
A operação Rx operation implementa uma rotação em volta do eixo Pauli $X$. Ele tem assinatura
((Double, Qubit) => Unit is Adj + Ctl) . Rx(_, _) é o mesmo que R(PauliX, _, _) .

A operação Ry operation implementa uma rotação em volta do eixo Pauli $Y$. Ele tem assinatura
((Double, Qubit) => Unit is Adj + Ctl) . Ry(_, _) é o mesmo que R(PauliY,_ , _) .

A operação Rz operation implementa uma rotação em volta do eixo Pauli $Z$. Ele tem assinatura
((Double, Qubit) => Unit is Adj + Ctl) . Rz(_, _) é o mesmo que R(PauliZ, _, _) .

A operação R1 operation implementa uma rotação pelo valor determinado em relação a $\ket{1}$, a eigenstate
$-$1 de $Z$. Ele tem assinatura ((Double, Qubit) => Unit is Adj + Ctl) . R1(phi,_) é o mesmo que
R(PauliZ,phi,_) seguido de R(PauliI,-phi,_) .

A operação R1Frac operation implementa uma rotação fracionária pelo valor determinado em relação ao
eigenstate Z=1. Ele tem assinatura ((Int,Int, Qubit) => Unit is Adj + Ctl) . R1Frac(k,n,_) é o mesmo que
RFrac(PauliZ,-k.n+1,_) seguido de RFrac(PauliI,k,n+1,_) .

Um exemplo de uma operação de rotação (em volta do eixo Pauli $Z$, nessa instância) mapeada para a esfera
Bloch é mostrado abaixo:
Operações de qubit múltiplos
Além das operações de qubit único acima, o prelúdio também define várias operações de qubit múltiplos.
Primeiro, a operação CNOT operation executa um padrão de portas controladas NOT , \begin{equation}
\operatorname{CNOT} \mathrel{:=} \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 &
1 & 0 \end{bmatrix}. \end{Equation} tem assinatura ((Qubit, Qubit) => Unit is Adj + Ctl) , representando que
$ \operatorname{CNOT}$ atua de forma unitária em dois qubits individuais. CNOT(q1, q2) é o mesmo que
(Controlled X)([q1], q2) . Como o functor Controlled permite controle em um registro, usamos o literal de
matriz [q1] para indicar que queremos apenas um controle.
A operação CCNOT operation executa controle duplo da porta NOT, às vezes também conhecida como porta
Toffoli. Ele tem assinatura ((Qubit, Qubit, Qubit) => Unit is Adj + Ctl) . CCNOT(q1, q2, q3) é o mesmo que
(Controlled X)([q1, q2], q3) .

A operação SWAP operation troca os estados de quantum de dois qubits. Ou seja, ele implementa a matriz
unitária \begin{equation} \operatorname{SWAP} \mathrel{:=} \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\
0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}. \end{Equation} Ele tem assinatura
((Qubit, Qubit) => Unit is Adj + Ctl) . SWAP(q1,q2) é equivalente a CNOT(q1, q2) seguido de CNOT(q2, q1) e
depois CNOT(q1, q2) .
NOTE
A porta SWAP não é o mesmo que reorganizar os elementos de uma variável com o tipo Qubit[] . A aplicação de
SWAP(q1, q2) causa uma alteração no estado do qubits referenciado por q1 e q2 , enquanto
let swappedRegister = [q2, q1]; afeta apenas a forma como nos referimos a esses qubits. Além disso,
(Controlled SWAP)([q0], (q1, q2)) permite que SWAP seja condicionado ao estado de um terceiro qubit, que não
podemos representar pela reorganização dos elementos. A porta SWAP controlada, também conhecida como porta
Fredkin, é eficiente o suficiente para incluir toda a computação clássica.

Por fim, o prelúdio fornece duas operações para representar exponencias de operadores Pauli de qubit
múltiplos. A operação Exp operation executa uma rotação com base em um produto tensor de matrizes Pauli,
conforme representado pelo qubit unitário \begin{equation} \operatorname{Exp}(\vec{\sigma}, \phi)
\mathrel{:=} \exp\left(i \phi \sigma_0 \otimes \sigma_1 \otimes \cdots \otimes \sigma_n \right), \end{equation}
where $\vec{\sigma} = (\sigma_0, \sigma_1, \dots, \sigma_n)$ é uma sequência de operadores Pauli de qubit
único e onde $\phi$ é um ângulo. A rotação Exp representa $\vec{\sigma}$ como uma matriz de elementos
Pauli , de modo que ele tem assinatura ((Pauli[], Double, Qubit[]) => Unit is Adj + Ctl) .

A operação ExpFrac operation executa a mesma rotação, usando a notação de fração dyadic discutida acima. Ele
tem assinatura ((Pauli[], Int, Int, Qubit[]) => Unit is Adj + Ctl) .

WARNING
Os exponenciais do produto tensor dos operadores Pauli não são os mesmos que os produtos tensor dos operadores
Pauli. Ou seja, $e^{i (Z \otimes Z) \phi} \ne e^{i Z \phi} \otimes e^{i Z \phi}$.

Medidas
Ao medir, a eigenvalue + 1 do operador que está sendo medido corresponde a um resultado Zero e-1
eigenvalue a um resultado One .

NOTE
Embora essa regra possa parecer estranha, ela tem duas vantagens muito interessantes. Primeiro, observar o resultado
$\ket{0}$ é representado pelo Result valor Zero , enquanto que a observação $\ket{1}$ corresponde a One . Em
segundo lugar, podemos anotar que o eigenvalue $\lambda$ corresponde a um resultado $r$ é $\lambda = (-1)^r$.

As operações de medição não dão suporte nem a Adjoint nem a Controlled functor.
A operação Measure operation executa uma medida conjunta de um ou mais qubits no produto especificado
dos operadores Pauli. Se a matriz Pauli e a matriz qubit tiverem comprimentos diferentes, a operação falhará.
Measure tem assinatura ((Pauli[], Qubit[]) => Result) .

Observe que uma medida conjunta não é a mesma de medir cada qubit individualmente. Por exemplo,
considere o estado $\ket{11} = \ket{1} \otimes \Ket {1} = X\otimes X \ket{00}$. Medindo $Z_0$ e $Z_1$
individualmente, obtemos os resultados $r_0 = $1 e $r_1 = $1. Medindo $Z_0 Z_1$, no entanto, obtemos o
resultado único $r_ {\textrm{Joint}} = $0, representando que o emparelhamento de $\ket{11}$ é positivo.
Colocar de forma diferente, $(-1)^{r_0 + r_1} = (-1)^r_{\textrm{ joint}})$. Criticamente, como aprendemos
apenas a paridade dessa medição, qualquer informação quântica representada na superposição entre os dois
estados quibit duplo de paridade positiva, $\ket{00}$ e $\ket{11}$, são preservadas. Essa propriedade será
essencial posteriormente, à medida que discutiremos a correção do erro.
Para sua conveniência, o prelúdio também fornece duas outras operações para medir qubits. Primeiro, como a
execução de medidas de qubit único é bastante comum, o prelúdio define uma abreviação para esse caso. A
operação M operation mede o operador Pauli $Z$ em um qubit único e tem assinatura (Qubit => Result) .
M(q) é equivalente a Measure([PauliZ], [q]) .
O MultiM operation mede o operador Pauli $Z$ separadamente em cada uma das matrizes de qubits,
retornando a matriz de Result valores obtidos para cada qubit. Em alguns casos, isso pode ser otimizado. Ele
tem assinatura ( Qubit[] => Result[]) . MultiM(qs) é equivalente a:

mutable rs = new Result[Length(qs)];


for (index in 0..Length(qs)-1)
{
set rs[index] = M(qs[index]);
}
return rs;

Funções e operações de extensão


Além disso, o prelúdio define um conjunto avançado de funções matemáticas e de conversão de tipo no nível do
.NET para uso no código Q#. Por exemplo, o Microsoft.Quantum.Math namespace namespace define operações
úteis, como Sin function e Log function. A implementação fornecida pelo Quantum Development Kit usa a
biblioteca de classes base do .NET clássica e, portanto, pode envolver uma viagem de ida e volta de
comunicação adicional entre os programas quânticos e seus drivers clássicos. Embora isso não apresente um
problema para um simulador local, isso pode ser um problema de desempenho ao usar um simulador remoto
ou hardware real, como um computador de destino. Dito isso, um computador de destino individual pode
atenuar esse impacto no desempenho substituindo essas operações por versões mais eficientes para esse
sistema específico.
Matemática
O Microsoft.Quantum.Math namespace namespace fornece muitas funções úteis da System.Math classeda
biblioteca de classes base do .NET. Essas funções podem ser usadas da mesma maneira que qualquer outra
função Q#:

open Microsoft.Quantum.Math;
// ...
let y = Sin(theta);

Onde um método estático do .NET foi sobrecarregado com base no tipo de seus argumentos, a função Q#
correspondente é anotada com um sufixo que indica o tipo de sua entrada:

let x = AbsI(-3); // x : Int = 3


let y = AbsD(-PI()); // y : Double = 3.1415...

Operações bit a bit


Por fim, o Microsoft.Quantum.Bitwise namespace namespace fornece várias funções úteis para manipular
inteiros por meio de operadores bit a bit. Por exemplo, Parity function retorna a paridade de um número de bits
para um inteiro como outro inteiro.
Funções Matemáticas Clássicas
13/05/2021 • 2 minutes to read

Essas funções são usadas principalmente para trabalhar com os Q# tipos de dados internos Int , Double e
Range .

A operação Random operation tem assinatura (Double[] => Int) . Ela usa uma matriz de duplicatas como
entrada e retorna um índice selecionado aleatoriamente para a matriz como um Int . A probabilidade de
selecionar um índice específico é proporcional ao valor do elemento da matriz nesse índice. Os n elementos da
matriz que são iguais a zero são ignorados, e seus índices nunca são retornados. Se qualquer elemento da
matriz for menor do que zero, ou se nenhum elemento da matriz for maior do que zero, a operação falhará.
Conversões de tipo
01/05/2021 • 2 minutes to read

Q# é uma linguagem for temente tipada . Em particular, Q# não é implicitamente convertido entre tipos
distintos. Por exemplo, 1 + 2.0 não é uma expressão Q# válida. Em vez disso, o Q# fornece uma variedade de
funções de conversão de tipo para construir novos valores de um determinado tipo.
Por exemplo, Length function tem um tipo de saída de Int , portanto sua saída deve primeiro ser convertida
para um Double antes que possa ser usada como parte de uma expressão de ponto flutuante. Isso pode ser
feito usando a IntAsDouble function função:

open Microsoft.Quantum.Convert as Convert;

function HalfLength<'T>(arr : 'T[]) : Double {


return Convert.IntAsDouble(Length(arr)) / 2.0;
}

O Microsoft.Quantum.Convert namespace namespace fornece funções de conversão de tipo comuns para


trabalhar com os tipos internos básicos, como Int , Double , BigInt , Result e Bool :

let bool = Convert.ResultAsBool(One); // true


let big = Convert.IntAsBigInt(271); // 271L
let indices = Convert.RangeAsIntArray(0..4); // [0, 1, 2, 3, 4]

O Microsoft.Quantum.Convert namespace namespace também fornece algumas conversões mais exóticas,


como FunctionAsOperation , que converte as funções 'T -> 'U em novas operações 'T => 'U .
Por fim, a Q# biblioteca padrão fornece vários tipos definidos pelo usuário, como Complex user defined type e
LittleEndian user defined type. Com esses tipos, a biblioteca padrão fornece funções como
BigEndianAsLittleEndian function:

open Microsoft.Quantum.Arithmetic as Arithmetic;

let register = Arithmetic.BigEndian(qubits);


IncrementByInteger(Arithmetic.BigEndianAsLittleEndian(register));
Fluxo de controle de ordem superior
11/05/2021 • 9 minutes to read

Uma das principais funções da biblioteca padrão é facilitar a expressão de ideias de algoritmos de alto nível
como programas quânticos. Assim, o cânone Q# fornece diferentes construções de controle de fluxo, cada uma
implementada usando um aplicativo parcial de funções e operações. Passando imediatamente para um
exemplo, considere o caso em que se deseja construir uma "escada CNOT" em um registro:

let nQubits = Length(register);


CNOT(register[0], register[1]);
CNOT(register[1], register[2]);
// ...
CNOT(register[nQubits - 2], register[nQubits - 1]);

Podemos expressar este padrão usando iteração e loops for :

for idxQubit in 0..nQubits - 2 {


CNOT(register[idxQubit], register[idxQubit + 1]);
}

Expresso em termos de ApplyToEachCA operation e funções de manipulação de matrizes, como Zipped function.
No entanto, isto é muito mais curto e mais fácil de ler:

ApplyToEachCA(CNOT, Zip(register[0..nQubits - 2], register[1..nQubits - 1]));

No restante desta seção, forneceremos exemplos de como usar as várias operações de controle de fluxo e
funções fornecidas pelo cânone para expressar os programas quânticos de forma compacta.

Aplicar operações e funções em matrizes e intervalos


Uma das abstrações primárias fornecidas pelo cânone é a de iteração. Por exemplo, considere uma unidade da
forma $U \otimes U \otimes \cdots \otimes U$ para uma unidade de qubit único $U$. Em Q#, podemos usar
IndexRange function para representar isso como um loop for em um registro:

/// # Summary
/// Applies $H$ to all qubits in a register.
operation ApplyHadamardToAll(
register : Qubit[])
: Unit is Adj + Ctl {
for qubit in register {
H(qubit);
}
}

Podemos usar essa nova operação como HAll(register) para aplicar $H \otimes H \otimes \cdots \otimes H$.
No entanto, isso é complicado de fazer, especialmente em um algoritmo maior. Além disso, essa abordagem é
especializada para a operação específica que desejamos aplicar a cada qubit. Podemos usar o fato de que as
operações são de primeira classe para expressar este conceito de algoritmo mais explicitamente:
ApplyToEachCA(H, register);

Aqui, o sufixo CA indica que a chamada para ApplyToEach é, por si, adjacente e controlável. Portanto, se U
oferecer suporte para Adjoint e Controlled , as seguintes linhas serão equivalentes:

Adjoint ApplyToEachCA(U, register);


ApplyToEachCA(Adjoint U, register);

Em particular, isso significa que as chamadas para ApplyToEachCA podem aparecer em operações para as quais
uma especialização adjacente foi gerada automaticamente. Da mesma forma, ApplyToEachIndex operation é útil
para representar padrões da forma U(0, targets[0]); U(1, targets[1]); ... e oferece versões para cada
combinação de functors compatíveis com a entrada.

TIP
ApplyToEach é parametrizado por tipo, de forma que possa ser usado com operações que usam entradas diferentes de
Qubit . Por exemplo, suponha que codeBlocks seja uma matriz de valores LogicalRegister user defined type que
precisam ser recuperados. ApplyToEach(Recover(code, recoveryFn, _), codeBlocks) aplicará o código de correção
de erro code e a função de recuperação recoveryFn a cada bloco de forma independente. Isso se aplica também a
entradas clássicas: ApplyToEach(R(_, _, qubit), [(PauliX, PI() / 2.0); (PauliY(), PI() / 3.0])) aplicará uma
rotação de $\pi / 2$ sobre $X$ seguida de uma rotação de $pi / 3$ sobre $Y$.

O cânone Q# também fornece suporte para padrões de enumeração clássica familiares à programação
funcional. Por exemplo, Fold function implementa o padrão $f(f(f(s_{\text{initial}}, x_0), x_1), \dots)$ para reduzir
uma função em uma lista. Esse padrão pode ser usado para implementar somas, produtos, mínimo, máximo e
outras funções desse tipo:

open Microsoft.Quantum.Arrays;
function Plus(a : Int, b : Int) : Int { return a + b; }
function Sum(xs : Int[]) {
return Fold(Sum, 0, xs);
}

Da mesma forma, funções como Mapped function e MappedByIndex function podem ser usadas para expressar
conceitos de programação funcional em Q#.

Compor operações e funções


As construções de fluxo de controle oferecidas pelo cânone tomam operações e funções como suas entradas, de
modo que é útil poder compor várias operações ou funções em um único chamador. Por exemplo, o padrão
$UVU^{\dagger}$ é extremamente comum na programação quântica, de maneira que o cânone fornece a
operação ApplyWith operation como uma abstração para esse padrão. Essa abstração também permite uma
compilação mais eficiente em circuitos, pois Controlled agindo na sequência
U(qubit); V(qubit); Adjoint U(qubit); não precisa agir em cada U . Para isso, permita que $c(U)$ seja a
unidade que representa Controlled U([control], target) e permita que $c(V)$ seja definido da mesma
maneira. Em seguida, para um estado arbitrário $\ket{\psi}$, \begin{align} c(U) c(V) c(U)^\dagger \ket{1}
\otimes \ket{\psi} & = \ket{1} \otimes (UVU^{\dagger} \ket{\psi}) \\ & = (\boldone \otimes U) (c(V)) (\boldone
\otimes U^\dagger) \ket{1} \otimes \ket{\psi}. \end{align} pela definição de Controlled . Por outro lado,
\begin{align} c(U) c(V) c(U)^\dagger \ket{0} \otimes \ket{\psi} & = \ket{0} \otimes \ket{\psi} \\ & = \ket{0}
\otimes (UU^\dagger \ket{\psi}) \\ & = (\boldone \otimes U) (c(V)) (\boldone \otimes U^\dagger) \ket{0}
\otimes \ket{\psi}. \end{align} Por linearidade, podemos concluir que podemos fatorar $U$ dessa maneira para
todos os estados de entrada. Ou seja, $c(UVU^\dagger) = U c(V) U^\dagger$. Como o controle de operações
pode ser caro, o uso de variantes controladas como WithC e WithCA pode ajudar a reduzir o número de
functors de controle que precisam ser aplicados.

NOTE
Uma outra consequência de fatorar $U$ é que não precisamos nem mesmo saber como aplicar o functor Controlled a
U . Portanto, ApplyWithCA tem uma assinatura mais fraca do que o esperado:

ApplyWithCA<'T> : (('T => Unit is Adj),


('T => Unit is Adj + Ctl), 'T) => Unit

Da mesma forma, Bound function produz operações que aplicam uma sequência de outras operações
sucessivamente. Por exemplo, os seguintes são equivalentes:

H(qubit); X(qubit);
Bound([H, X], qubit);

Combinar com padrões de iteração pode tornar isso especialmente útil:

// Bracket the quantum Fourier transform with $XH$ on each qubit.


ApplyWith(ApplyToEach(Bound([H, X]), _), QFT, _);

Composição ordenada por tempo


Podemos ir ainda mais além ao considerar o controle de fluxo em termos de aplicativos parciais e funções
clássicas e modelar conceitos quânticos ainda mais sofisticados em termos de controle de fluxo clássico. Essa
analogia se torna precisa ao reconhecer que os operadores unitários correspondem exatamente aos efeitos
colaterais das operações de chamada. Toda decomposição de operadores unitários em termos de outros
operadores unitários corresponde à construção de uma sequência de chamada específica para sub-rotinas
clássicas que emitem instruções para agir como operadores unitários específicos. Sob essa perspectiva, Bound é
precisamente a representação do produto de matriz, pois Bound([A, B])(target) é equivalente a
A(target); B(target); , que por sua vez é a sequência de chamada correspondente a $BA$.

Um exemplo mais sofisticado é a expansão Trotter–Suzuki. Conforme discutido na seção Representação do


Gerador Dinâmico de estruturas de dados, a expansão Trotter–Suzuki fornece uma maneira particularmente útil
de expressar exponenciais de matriz. Por exemplo, aplicar a expansão em sua ordem mais baixa produz isso
para qualquer operador $A$ e $B$, de modo que $A = A^\dagger$ e $B = B^\dagger$, \begin{align} \tag{★}
\label{eq:trotter-suzuki-0} \exp(i [A + B] t) = \lim_{n\to\infty} \left(\exp(i A t / n) \exp(i B t / n)\right)^n.
\end{align} Coloquialmente, isso diz que podemos expandir um estado em $A + B$ ao evoluir alternadamente
em $A$ e $B$ sozinho. Se representarmos a evolução em $A$ por uma operação
A : (Double, Qubit[]) => Unit que aplica $e^{i t A}$$, a representação da expansão Trotter–Suzuki em termos
de reorganizar as sequências de chamada se torna clara. Concretamente, considerando uma operação
U : ((Int, Double, Qubit[]) => Unit is Adj + Ctl como A = U(0, _, _) e B = U(1, _, _) , podemos definir
uma nova operação que representa o integral de U no tempo $t$ ao gerar sequências da forma

U(0, time / Float(nSteps), target);


U(1, time / Float(nSteps), target);
U(0, time / Float(nSteps), target);
U(1, time / Float(nSteps), target);
// ...

Neste ponto, podemos ter uma razão da expansão Trotter–Suzuki sem referência à mecânica quântica. A
expansão é, efetivamente, um padrão de iteração muito específico motivado por $\eqref{eq:trotter-suzuki-0}$.
Esse padrão de iteração é implementado por DecomposedIntoTimeStepsCA function:

// The 2 indicates how many terms we need to decompose,


// while the 1 indicates that we are using the
// first-order Trotter–Suzuki decomposoition.
DecomposeIntoTimeStepsCA((2, U), 1);

A assinatura de DecomposeIntoTimeStepsCA segue um padrão comum no Q#, em que as coleções, que podem ser
apoiadas por matrizes ou por algo que computa elementos em tempo real, são representadas por tuplas cujos
primeiros elementos são valores Int que indicam o comprimento.

Juntando as peças: como controlar as operações


Por fim, o cânone se baseia no functor Controlled fornecendo maneiras adicionais de operações de quantum. É
comum, especialmente em aritmética quântica, condicionar operações em estados de base computacionais
diferentes de $\ket{0\cdots 0}$. Usando as operações e as funções de controle apresentadas acima, podemos
obter mais condições gerais de quântica em uma única instrução. Vamos ver como ControlledOnBitString
function faz (parâmetros de tipo sans), depois dividiremos as partes uma a uma. A primeira coisa que
precisaremos fazer é definir uma operação que realmente faça o trabalho pesado de implementar o controle em
estados de base computacional arbitrários. No entanto, não chamaremos essa operação diretamente e
adicionaremos _ ao início do nome para indicar que é uma implementação de outro constructo em outro
lugar.

operation _ControlledOnBitString(
bits : Bool[],
oracle: (Qubit[] => Unit is Adj + Ctl),
controlRegister : Qubit[],
targetRegister: Qubit[])
: Unit is Adj + Ctl

Observe que pegamos uma cadeia de caracteres de bits, representada como uma matriz Bool , que usamos
para especificar o condicionamento que queremos aplicar à operação oracle recebida. Como essa operação
realmente faz o aplicativo diretamente, também precisamos obter o controle e os registros de destino, ambos
representados aqui como Qubit[] .
Em seguida, observamos que o controle no estado de base computacional $\ket{\vec{s}} = \ket{s_0 s_1 \dots
s_{n - 1}}$ para uma cadeia de caracteres de bits $\vec{s}$ pode ser alcançado ao transformar $\ket{\vec{s}}$
em $\ket{0\cdots 0}$. Em particular, $\ket{\vec{s}} = X^{s_0} \otimes X^{s_1} \otimes \cdots \otimes X^{s_{n -
1}} \ket{0\cdots 0}$. Como $X^{\dagger} = X$, isso implica que $\ket{0\dots 0} = X^{s_0} \otimes X^{s_1}
\otimes \cdots \otimes X^{s_{n - 1}} \ket{\vec{s}}$. Portanto, podemos aplicar $P = X^{s_0} \otimes X^{s_1}
\otimes \cdots \otimes X^{s_{n - 1}}$, aplicar Controlled oracle e transformar de volta em $\vec{s}$. Essa
construção é precisamente ApplyWith , então escrevemos o corpo da nossa nova operação adequadamente:

{
ApplyWithCA(
ApplyPauliFromBitString(PauliX, false, bits, _),
(Controlled oracle)(_, targetRegister),
controlRegister
);
}

Aqui, usamos ApplyPauliFromBitString operation para aplicar $P$, parcialmente aplicando sobre o destino para
uso com ApplyWith . No entanto, observe que precisamos transformar o registro de controle no formato
desejado, portanto, aplicamos parcialmente a operação interna (Controlled oracle) no destino. Isso faz com
que ApplyWith coloque colchetes no registro de controle com $P$, exatamente como desejamos.
Poderíamos considerar finalizado neste ponto. No entanto, a nova operação, de alguma forma, não "quer"
aplicar o functor Controlled . Portanto, terminamos de definir nosso novo conceito de fluxo de controle ao
escrever uma função que controla a oracle e que retorna uma nova operação. Dessa forma, nossa nova função é
muito parecida com Controlled , ilustrando que podemos facilmente definir novas construções poderosas de
fluxo de controle usando Q# e o cânone juntos:

function ControlledOnBitString(
bits : Bool[],
oracle: (Qubit[] => Unit is Adj + Ctl))
: ((Qubit[], Qubit[]) => Unit is Adj + Ctl) {
return _ControlledOnBitString(bits, oracle, _, _);
}
Estruturas e modelagem de dados
19/05/2021 • 17 minutes to read

Estruturas de dados clássicas


Juntamente com tipos definidos pelo usuário para representar conceitos quantum, o cânone também fornece
operações, funções e tipos para trabalhar com os dados clássicos usados no controle de sistemas quantum. Por
exemplo, a função Reversed function usa uma matriz como entrada e retorna a mesma matriz na ordem inversa.
Isso pode ser usado em uma matriz do tipo Qubit[] para evitar a necessidade de aplicar portões
$\operatorname{swap}$ desnecessários ao converter entre representações quantum de inteiros. Da mesma
forma, vimos na seção anterior que tipos de formulário (Int, Int -> T) podem ser úteis para representar
coleções de acesso aleatório, portanto, a função LookupFunction function oferece um modo conveniente de
construir esses tipos com base em tipos de matriz.
Pares
O cânone dá suporte à notação de estilo funcional para pares, complementando o acesso a tuplas por
desconstrução:

let pair = (PauliZ, register); // type (Pauli, Qubit[])


ApplyToEach(H, Snd(pair)); // No need to deconstruct to access the register.

Matrizes
O cânone fornece várias funções para manipular matrizes. Essas funções são de tipo parametrizado, portanto,
podem ser usadas com matrizes de qualquer tipo Q#. Por exemplo, a função Reversed function retorna uma
nova matriz cujos elementos estão na ordem inversa de sua entrada. Isso pode ser usado para alterar como um
registro quantum é representado ao chamar operações:

let leRegister = LittleEndian(register);


// QFT expects a BigEndian, so we can reverse before calling.
QFT(BigEndian(Reversed(leRegister!)));
// This is how the LittleEndianAsBigEndian function is implemented:
QFT(LittleEndianAsBigEndian(leRegister));

Da mesma forma, a função Subarray function pode ser usada para reordenar ou pegar subconjuntos dos
elementos de uma matriz:

// Applies H to qubits 2 and 5.


ApplyToEach(H, Subarray([2, 5], register));

Quando combinado com o controle de fluxo, as funções de manipulação de matriz, como Zipped function,
podem fornecer um modo eficiente de expressar os programas quantum:
// Applies X₃ Y₁ Z₇ to a register of any size.
ApplyToEach(
ApplyPauli(_, register),
Map(
EmbedPauli(_, _, Length(register)),
Zipped([PauliX, PauliY, PauliZ], [3, 1, 7])
)
);

Oráculos
Na estimativa da fase e na amplificação da amplitude, o conceito de um oráculo é exibido com frequência. Aqui,
o termo oráculo se refere a uma sub-rotina quantum que age em um conjunto de qubits e retorna a resposta
como uma fase. Essa sub-rotina muitas vezes pode ser considerada uma entrada para um algoritmo quantum
que aceita o oráculo, além de alguns outros parâmetros, e aplica uma série de operações quantum e trata uma
chamada para essa sub-rotina quantum como se fosse um portão fundamental. Obviamente, para de fato
implementar o algoritmo maior, uma decomposição concreta do oráculo em portões fundamentais deve ser
fornecida, mas essa decomposição não é necessária para entender o algoritmo que chama o oráculo. No Q#,
essa abstração é representada usando essas operações são valores de primeira classe, de modo que as
operações podem ser passadas para implementações de algoritmos quantum de um modo estilo caixa preta.
Além disso, os tipos definidos pelo usuário são usados para rotular as diferentes representações do oráculo de
maneira fortemente tipada, o que dificulta fundir acidentalmente tipos diferentes de operações de caixa preta.
Esses oráculos são exibidos em vários contextos diferentes, incluindo exemplos famosos, como os algoritmos de
simulação de quantum e pesquisa do Grover. Aqui, nos concentramos nos oráculos necessários apenas para
dois aplicativos: amplificação de amplitude e estimativa de fase. Primeiro, vamos discutir a amplitude de
oráculos de amplificação antes de prosseguir para a estimativa de fase.
Oráculos de amplificação de amplitude
O algoritmo de amplificação de amplitude tem como objetivo executar uma rotação entre um estado inicial e
um estado final aplicando uma sequência de reflexos do estado. Para que o algoritmo funcione, ele precisa de
uma especificação de ambos os estados. Essas especificações são dadas por dois oráculos. Esses oráculos
funcionam dividindo as entradas em dois espaços, um subespaço "de destino" e um subespaço "inicial". Os
oráculos identificam esses subespaços, semelhante ao modo como os operadores de Pauli identificam dois
espaços, aplicando uma fase de $\pm$1 a eles. A principal diferença é que esses espaços não precisam conter
semiespaços neste aplicativo. Observe também que esses dois subespaços geralmente não são mutuamente
exclusivos: haverá vetores que são membros de ambos os espaços. Se isso não fosse verdade, a amplificação de
amplitude não teria nenhum efeito, portanto, precisamos que o subespaço inicial tenha uma sobreposição
diferente de zero com o subespaço de destino.
Indicaremos o primeiro oráculo de que precisamos para que a amplificação de amplitude seja $P_0$, definida
para que a ação a seguir seja executada. Para todos os estados $\ket{x}$ no subespaço "inicial"$P_0 \ket{x} = -
\ket{x}$ e para todos os estados $\ket{y}$ que não estão nesse subespaço, temos $P_0\ket{y} = \ket{y}$. O
oráculo que marca o subespaço de destino, $P_1$ assume exatamente a mesma forma. Para todos os estados
$\ket{x}$ no subespaço de destino (ou seja, para todos os estados que você deseja que o algoritmo gere),
$P_1\ket{x} = -\ket{x}$. Da mesma forma, para todos os estados $\ket{y}$ que não estão no subespaço de
destino $P_1\ket{y} = \ket{y}$. Esses dois reflexos então são combinados para formar um operador que atue em
uma só etapa de amplificação de amplitude, $Q = -P_0 P_1$, em que é importante considerar o sinal de
subtração geral apenas em aplicativos controlados. Em seguida, a amplificação de amplitude prossegue com um
estado inicial, $\ket{\psi}$, que está no subespaço inicial e, depois, executa $\ket{\psi}\mapsto Q^m \ket{\psi}$.
A execução dessa iteração garante que, se alguém começar com um estado inicial que tenha a sobreposição de
$\sin^2(\theta)$ com o espaço marcado, depois de $m$ iterações, essa sobreposição se tornará $\sin^2 ([2m +
1]\theta) $. Portanto, normalmente desejamos escolher $m$ para ser um parâmetro gratuito, de modo que
$[2m+1]\theta = \pi/2$; no entanto, essas opções rígidas não são tão importantes para algumas formas de
amplificação de amplitude, como amplificação de amplitude de ponto fixo. Esse processo nos permite preparar
um estado no subespaço marcado usando quadraticamente menos consultas para a função de marcação e a
função de preparação de estado do que seria possível em um dispositivo estritamente clássico. É por isso que a
amplificação de amplitude é um bloco de construção significativo para muitos aplicativos de computação
quântica.
Para entender como usar o algoritmo, é útil fornecer um exemplo que fornece uma construção dos oráculos.
Considere executar o algoritmo de Grover para pesquisas de banco de dados nessa configuração. Na pesquisa
de Grover, a meta é transformar o estado $\ket{+}^{\otimes n} = H^{\otimes n} \ket{0}$ em um dos muitos
estados marcados (potencialmente). Para simplificar ainda mais, vamos apenas examinar o caso em que o único
estado marcado é $\ket{0}$. Então, temos o design de dois oráculos: um que marca apenas o estado inicial
$\ket{+}^{\otimes n}$ com um sinal de menos e outro que marca o estado marcado $\ket{0}$ com um sinal de
subtração. O último portão pode ser implementado usando a seguinte operação de processo, usando as
operações de fluxo de controle no cânone:

operation ReflectAboutAllZeros(register : Qubit[]) : Unit


is Adj + Ctl {

// Apply $X$ gates to every qubit.


ApplyToEach(X, register);

// Apply an $n-1$ controlled $Z$-gate to the $n^{\text{th}}$ qubit.


// This gate will lead to a sign flip if and only if every qubit is
// $1$, which happens only if each of the qubits were $0$ before step 1.
Controlled Z(Most(register), Tail(register));

// Apply $X$ gates to every qubit.


ApplyToEach(X, register);
}

Esse oráculo é então um caso especial da operação RAll1 operation, que permite girar por uma fase arbitrária
em vez do caso de reflexão $\phi = \pi$. Nesse caso, RAll1 é semelhante à operação de prelúdio R1 operation,
pois ela gira aproximadamente $\ket{11\cdots1}$, em vez do estado de qubit único $\ket{1}$.
O oráculo que marca o subespaço inicial pode ser construído da mesma forma. No pseudocódigo:
1. Aplique portões $H$ a cada qubit.
2. Aplique portões $X$ a cada qubit.
3. Aplique um portão $Z$ controlado por $n-1$ ao qubit $n^{\text{th}}$.
4. Aplique portões $X$ a cada qubit.
5. Aplique portões $H$ a cada qubit.
Desta vez, também demonstramos o uso de ApplyWith operation junto com a operação RAll1 operation
discutida acima:

operation ReflectAboutInitial(register : Qubit[]) : Unit


is Adj + Ctl {
ApplyWithCA(ApplyToEach(H, _), ApplyWith(ApplyToEach(X, _), RAll1(_, PI()), _), register);
}

Em seguida, podemos combinar esses dois oráculo para alternar entre os dois estados e transformar de modo
determinístico $\ket{+}^{\otimes n}$ em $\ket{0}$ usando várias camadas de portões Hadamard proporcionais
a $\sqrt{2^n}$ (ou seja, $m\propto\sqrt{2^n}$) versus as aproximadamente $2^n$ camadas que seriam
necessárias para preparar de modo não determinístico o estado $\ket{0}$, preparando e medindo o estado
inicial até que o resultado $0$ seja observado.
Estimativa de fase oráculo
Para a estimativa de fase, os oráculos são um pouco mais naturais. A estimativa de objetivo na fase é criar uma
sub-rotina capaz de realizar a amostragem de eigenvalues de uma matriz unitária. Esse método é indispensável
na simulação quantum porque, para muitos problemas físicos em química e ciência de material, esses
eigenvalues fornecem as energias de estado fundamental de sistemas quantum que apresentam informações
valiosas sobre os diagramas de fase de materiais e a dinâmica da reação de moléculas. Cada variante de
estimativa de fase precisa de uma entrada unitária. Essa entrada unitária costuma ser descrita por um dos dois
tipos de oráculo.

TIP
Os dois tipos de oráculo descritos abaixo são abordados nos exemplos. Para saber mais sobre oráculo de consulta
contínua, confira o exemplo PhaseEstimation . Para saber mais sobre oráculos de consulta discreta, confira o exemplo
IsingPhaseEstimation .

O primeiro tipo de oráculo, que chamamos de consulta discreta do oráculo e que representa com o tipo definido
pelo usuário DiscreteOracle user defined type, simplesmente envolve uma matriz unitária. Se $U$ é o unitário
cujos eigenvalues desejamos estimar, o oráculo para $U$ é simplesmente um substituto para uma sub-rotina
que implementa $U$. Por exemplo, é possível pegar $U$ para ser o oráculo $Q$ definido acima para a
estimativa de amplitude. Os eigenvalues dessa matriz podem ser usado para estimar a sobreposição entre os
estados inicial e de destino, $\sin^2(\theta)$, usando, de maneira quadrática, menos amostras do que seriam
necessárias de outra forma. Isso gera a aplicação da estimativa de fase usando o oráculo Grover $Q$ como
entrada do moniker de estimativa de amplitude. Outra aplicação comum, amplamente usada em metrologia
quantum, envolve a estimativa de um pequeno ângulo de rotação. Em outras palavras, desejamos estimar
$\theta$ para um portão de rotação desconhecida do formulário $R _z (\theta)$. Nesses casos, a sub-rotina com
a qual gostaríamos de interagir para aprender esse valor fixo de $ \theta $ para o portão é $$ \begin{align} U &
= R_z(\theta) \\ & = \begin{bmatrix} e^{-i \theta / 2} & 0 \\ 0 & e^{i\theta/2} \end{bmatrix}. \end{align} $$
O segundo tipo de oráculo usado na estimativa de fase é o oráculo de consulta contínua, representado pelo tipo
ContinuousOracle user defined type. Um oráculo de consulta contínua para estimativa de fase assume a forma
$U(t)$, em que $t$ é um número real conhecido. Se permitirmos que $U$ seja um unitário fixo, o oráculo de
consulta contínua usará a forma $U(t) = U^t$. Isso nos permite consultar matrizes, como $\sqrt{U}$, que não
puderam ser implementadas diretamente no modelo de consulta discreto.
Esse tipo de oráculo é valioso quando você não está investigando um determinado unitário, mas deseja
conhecer as propriedades do gerador do unitário. Por exemplo, na simulação do quantum dinâmico, a meta é
planejar os circuitos quantum que se aproximam muito de $U(t)=e^{-i H t}$ para uma matriz hermitiana $H$ e
tempo de evolução $t$. Os eigenvalues de $U(t)$ estão diretamente relacionados aos eigenvalues de $H$. Para
ver isso, considere um eigenvector de $H$:$H \ket{E} = E\ket {E}$. Então é fácil ver da definição da série de
energia do exponencial da matriz que $U(t) \ket{E} = e^{i\phi}\ket{E}= e^{-iEt}\ket{E}$. Assim, estimar o
eigenphase de $U(t)$ gera o eigenvalue $E$, supondo que eigenvector $\ket{E}$ seja inserido no algoritmo
estimativa de fase. No entanto, nesse caso, o valor $t$ pode ser escolhido de acordo com o critério do usuário,
pois, para qualquer valor suficientemente pequeno de $t$, o eigenvalue $E$ pode ser invertido de modo único
por meio de $E=-\Phi/t$. Como os métodos de simulação quantum permitem executar uma evolução
fracionária, os algoritmos de estimativa de fase têm mais liberdade ao consultar o unitário, especificamente
quando o modelo de consulta discreto permite que apenas unidades na forma $U^j$ sejam aplicadas ao inteiro
$j$. O oráculo de consulta contínua nos permite aproximar unitários na forma $U^t$ para qualquer $t$ de valor
real. Isso é importante para extrair toda a eficiência possível dos algoritmos de estimativa de fase, pois isso nos
permite escolher precisamente o experimento que forneceria mais informações sobre $E$. Os métodos
baseados em consultas discretas, por sua vez, devem conseguir isso fazendo uma concessão ao escolher o
melhor número inteiro de consultas no algoritmo.
Como um exemplo concreto disso, considere o problema de estimar não o ângulo de rotação de um portão,
mas a frequência de processamento de um sistema quantum rotativo. O unitário que descreve tal dinâmica
quantum é $U(t)=R_z(2\omega t)$ para o tempo de evolução $t$ e a frequência desconhecida $\omega$. Nesse
contexto, podemos simular $U(t)$ para qualquer $t$ usando um portão $R _z$ e, como tal, não precisamos nos
restringir às consultas discretas para o unitário. Esse modelo contínuo também tem a propriedade que as
frequências maiores que $2\PI$ podem ser aprendidas dos processos de estimativa de fase que usam consultas
contínuas, pois as informações de fase que de outra forma seriam mascaradas pelos recortes de ramificação da
função logarítmica podem ser reveladas pelos resultados de experimentos executados em valores não
proporcionais de $t$. Portanto, para problemas como esses modelos de consulta contínua para a estimativa de
fase, o oráculo não é apenas apropriado, mas também é preferível ao modelo de consulta discreto. Por esse
motivo, Q# tem funcionalidade para ambas as formas de consultas e deixa a critério do usuário decidir quanto a
um algoritmo de estimativa de fase para atender às suas necessidades e ao tipo de oráculo que está disponível.

Modelagem de gerador dinâmico


Geradores de evolução temporal descrevem como os estados evoluem ao longo do tempo. Por exemplo, a
dinâmica de um estado quantum $\ket{\psi}$ é regida pela equação de Schrödinger $$ \begin{align} i\frac{d
\ket{\psi(t)}}{d t} & = H \ket{\psi(t)}, \end{align} $$ com uma matriz hermitiana $H$, conhecida como
hamiltoniana, como o gerador de movimento. Dado um estado inicial $\ket{\psi(0)}$ no tempo $t=0$, a solução
formal para esta equação no tempo $t$ pode ser, em princípio, escrita como $$ \begin{align} \ket{\psi(t)} =
U(t)\ket{\psi(0)}, \end{align} $$, em que o exponencial de matriz $U(t)=e^{-i H t}$ é conhecido como o operador
de evolução de tempo unitário. Embora o foco dos geradores dessa forma seja dado aos apresentados a seguir,
enfatizamos que o conceito se aplica mais amplamente, como a simulação de sistemas quantum abertos ou a
equações diferenciais mais abstratas.
Uma das metas principais da simulação dinâmica é implementar o operador de evolução temporal em algum
estado quantum codificado em qubits de um computador quantum. Em muitos casos, a Hamiltoniana pode ser
dividida em uma soma de alguns termos $d$ mais simples
$$ \begin{align} H & = \sum^{d-1}_{ j=0} H_j, \end{align} $$
em que a evolução de tempo de cada termo sozinho é fácil de implementar em um computador quantum. Por
exemplo, se $H_j$ for um operador de Pauli $X_1X_2$ atuando nos elementos 1 e 2 do registro qubit qubits , a
evolução do tempo por ele para qualquer momento $t$ poderá ser implementada simplesmente chamando a
operação Exp([PauliX,PauliX], t, qubits[1..2]) , que tem assinatura
((Pauli[], Double, Qubit[]) => Unit is Adj + Ctl) . Conforme discutido posteriormente na simulação
hamiltoniana, uma solução é aproximar a evolução do tempo por $H$ com uma sequência de operações mais
simples
$$ \begin{align} U(t) & = \left( e^{-iH_0 t / r} e^{-iH_1 t / r} \cdots e^{-iH_{d-1} t / r} \right)^{r} + \mathcal{O}
(d^2 \max_j \|H_j\|^2 t^2/r), \end{align} $$
em que o inteiro $r > $0 controla o erro de aproximação.
A biblioteca de modelagem de gerador dinâmico fornece uma estrutura para codificar sistematicamente
geradores complicados em termos de geradores mais simples. Essa descrição então pode ser passada para,
digamos, a biblioteca de simulações para implementar a evolução do tempo por um algoritmo de simulação de
escolha, com muitos detalhes automaticamente resolvidos.

TIP
A biblioteca de gerador dinâmico descrita abaixo é abordada nos exemplos. Para obter um exemplo baseado no modelo
Ising, confira o exemplo IsingGenerators . Para obter um exemplo baseado em hidrogênio molecular, confira as amostras
H2SimulationCmdLine e H2SimulationGUI .

Descrição completa de um gerador


No nível superior, uma descrição completa de um hamiltoniano está contida no tipo EvolutionGenerator
definido pelo usuário que tem dois componentes:

newtype EvolutionGenerator = (EvolutionSet, GeneratorSystem);

O tipo GeneratorSystem definido pelo usuário é uma descrição clássica do hamiltoniano.

newtype GeneratorSystem = (Int, (Int -> GeneratorIndex));

O primeiro elemento da tupla armazena o número de termos $d$ no hamiltoniano e o segundo elemento
Int
(Int -> GeneratorIndex) é uma função que mapeia um índice de número inteiro em ${0,1,...,d-1}$ para um tipo
GeneratorIndex definido pelo usuário que identifica exclusivamente cada termo primitivo no hamiltoniano.
Observe que, ao expressar a coleção de termos no hamiltoniano como uma função, em vez de uma matriz
GeneratorIndex[] , isso permite uma computação imediata do GeneratorIndex , que é especialmente útil ao
descrever hamiltonianos com um grande número de termos.
Crucialmente, não impomos uma convenção sobre quais termos primitivos identificados pelo GeneratorIndex
são fáceis de simular. Por exemplo, os termos primitivos podem ser operadores de Pauli como discutidos acima,
mas também podem ser operadores de aniquilação e criação de fermiônicos comumente usados na simulação
de química quântica. Por si só, um não GeneratorIndex tem sentido, pois não descreve como a evolução
temporal pelo termo para o qual ela aponta pode ser implementada como um circuito quantum.
Isso é resolvido especificando um tipo EvolutionSet definido pelo usuário que mapeia qualquer
GeneratorIndex , extraído de algum conjunto canônico, para um operador unitário, o EvolutionUnitary ,
expresso como um circuito quantum. O EvolutionSet define a convenção de como um GeneratorIndex é
estruturado e define o conjunto de possíveis GeneratorIndex .

newtype EvolutionSet = (GeneratorIndex -> EvolutionUnitary);

Geradores do operador de Pauli


Um exemplo concreto e útil de geradores são hamiltonianos que são uma soma dos operadores de Pauli, cada
um possivelmente com um coeficiente diferente. $$ \begin{align} H & = \sum^{d-1}_{ j=0} a_j H_j, \end{align}
$$ em que cada $\hat H_j$ agora é obtido do grupo de Pauli. Para esses sistemas, fornecemos o
PauliEvolutionSet() do tipo EvolutionSet que define uma Convenção de como um elemento do grupo de
Pauli e um coeficiente podem ser identificados por um GeneratorIndex , que tem a assinatura a seguir.

newtype GeneratorIndex = ((Int[], Double[]), Int[]);

Em nossa codificação, o primeiro parâmetro Int[] especifica uma cadeia de caracteres de Pauli, em que 0$,
$\hat X\rightarrow 1$, $\hat Y\rightarrow 2$ e $\hat Z\rightarrow 3$. O segundo parâmetro Double[]
armazena o coeficiente da cadeia de caracteres de Pauli no hamiltoniano. Observe que apenas o primeiro
elemento dessa matriz é usado. O terceiro parâmetro Int[] indexa o qubits em que essa cadeia de caracteres
de Pauli atua e não deve ter nenhum elemento duplicado. Portanto, o termo hamiltoniano $0.4 \hat X_0 \hat
Y_8\hat I_2\hat Z_1$ pode ser representado como

let generatorIndexExample = GeneratorIndex(([1,2,0,3], [0.4]]), [0,8,2,1]);

O PauliEvolutionSet()é uma função que mapeia qualquer GeneratorIndex dessa forma a um


EvolutionUnitary com a assinatura a seguir.
newtype EvolutionUnitary = ((Double, Qubit[]) => Unit is Adj + Ctl);

O primeiro parâmetro representa um tempo de duração, que será multiplicado pelo coeficiente no
GeneratorIndex , da evolução unitário. O segundo parâmetro é o registro de qubit em que o unitário atua.

Geradores dependentes de tempo


Em muitos casos, também estamos interessados em geradores dependentes de tempo de modelagem, como
pode ocorrer na equação de Schrödinger $$ \begin{align} i\frac{d \ket{\psi(t)}}{d t} & = \hat H(t) \ket{\psi(t)},
\end{align} $$, em que o gerador $\hat H(t)$ agora é dependente do tempo. A extensão dos geradores
independentes de tempo acima para este caso é simples. Em vez de ter um GeneratorSystem fixo descrevendo o
hamiltoniano para todos os tempos $t$, temos o tipo GeneratorSystemTimeDependent definido pelo usuário.

newtype GeneratorSystemTimeDependent = (Double -> GeneratorSystem);

O primeiro parâmetro é um parâmetro de agenda contínua $s\in [0,1]$, e as funções desse tipo retornam um
GeneratorSystem para essa agenda. Observe que o parâmetro de agenda pode estar linearmente relacionado ao
parâmetro de tempo físico, por exemplo, $s = t/T$, para algum tempo total de simulação $T$. No entanto, em
geral, esse não precisa ser o caso.
Da mesma forma, uma descrição completa desse gerador requer um EvolutionSet , portanto, estabelecemos
um tipo definido pelo usuário EvolutionSchedule .

newtype EvolutionSchedule = (EvolutionSet, GeneratorSystemTimeDependent);


Algoritmos quânticos
11/05/2021 • 14 minutes to read

Amplificação de amplitude
A amplificação de amplitude é uma das ferramentas fundamentais da computação quântica. É a ideia
fundamental que está por trás da pesquisa de Grover, da estimativa de amplitude e de muitos algoritmos
quânticos de aprendizado de máquina. Há muitas variantes e no Q# fornecemos uma versão geral de acordo
com a amplificação de amplitude alheia com reflexões parciais para permitir a área mais ampla do aplicativo.
A ideia central por trás da amplificação de amplitude é aumentar a probabilidade de ocorrer um resultado
desejado executando uma sequência de reflexões. Essas reflexões giram o estado inicial mais próximo em
direção a um estado de destino desejado, geralmente chamado de estado marcado. Especificamente, se a
probabilidade de medir o estado inicial que estará em um estado marcado for $\sin^2(\theta)$, depois de
aplicar a amplificação de amplitude $m$ vezes, a probabilidade de êxito se tornará $\sin^2((2m+1)\theta)$. Isso
significa que, se $\theta = \pi/[2(2n+1)]$ para algum valor de $n$, a amplificação de amplitude será capaz de
impulsionar a probabilidade de sucesso para $100\%$ após $n$ iterações da amplificação de amplitude. Como
$\theta = \sin^{-1}(\sqrt{\Pr(success)})$, isso significa que o número de iterações necessárias para obter um
sucesso determinístico é quadraticamente mais baixo do que o número esperado necessário para encontrar um
estado marcado de forma não determinística usando amostragem aleatória.
Cada iteração da amplificação de amplitude exige que dois operadores de reflexão sejam especificados.
Especificamente, se $Q$ for a iteração de amplificação de amplitude e $P_0$ for um operador de projetor no
subespaço inicial e $P_1$ for o projetor para o subespaço marcado, então $Q=-(\boldone-2P_0)(\boldone -
2P_1)$. Lembre-se de que um projetor é um operador Hermitian que tem autovalores de $+$1 e $0$ e, como
resultado, $(\boldone-2P_0)$ é unitário porque tem autovalores que são raízes de unidade (neste caso,
$\pm$1). Como exemplo, considere o caso da pesquisa de Grover com o estado inicial $H^{\otimes n} \ket{0}$
e o estado marcado $\ket{m}$, $P_0 = H^{\otimes n}\ket{0}\bra{0}H^{\otimes n}$ e $P_1= \ket{m}\bra{m}$. Na
maioria dos aplicativos da amplificação de amplitude, $P_0$ será um projetor para um estado inicial, o que
significa que $P_0 = \boldone -2\ket{\psi}\bra{\psi}$ para um vetor $\ket{\psi}$. No entanto, para a
amplificação de amplitude alheia $P_0$ normalmente será projetado para muitos estados quânticos (por
exemplo, a multiplicidade do autovalor $+$1 de $P_0$ é maior que $1$).
A lógica por trás da amplificação de amplitude segue diretamente da autodecomposição de $Q$.
Especificamente, os autovetores de $Q$ em que o estado inicial tem suporte diferente de zero podem ser
mostrados como combinações lineares dos autovetores $+1$ de $P_0$ e $P_1$. Especificamente, o estado
inicial da amplificação de amplitude (supondo que seja um autovetor $+$1 de $P_0$) pode ser escrito como $$
\ket{\psi}=\frac{-i}{\sqrt{2}}\left(e^{i\theta}\ket{\psi_+} + e^{-i\theta}\ket{\psi_-}\right), $$ em que
$\ket{\psi_\pm}$ são autovetores de $Q$ com autovalores $e^{\pm 2i\theta}$ e só têm suporte nos
autovetores $+1$ de $P_0$ e $P_1$. Como os autovalores são $e^{\pm i \theta}$, isso indica que o operador
$Q$ executa uma rotação em um subespaço bidimensional especificado pelos dois projetores e o estado inicial
em que o ângulo de rotação é $2\theta$. É por isso que, depois de iterações $m$ de $Q$, a probabilidade de
êxito é $\sin^2([2m+1]\theta)$.
Outra propriedade resultante que pode ser útil é que o autovalor $\theta$ está diretamente relacionado à
probabilidade de que o estado inicial seja marcado (no caso em que $P_0$ for um projetor apenas para o
estado inicial). Como as autofases de $Q$ são $2\theta = 2\sin^{-1}(\sqrt{\Pr(success)})$, se aplicarmos a
estimativa de fase ao $Q$, vamos encontrar a probabilidade de sucesso para um procedimento quântico
unitário. Isso é útil porque requer quadraticamente menos aplicativos do procedimento quântico para aprender
a probabilidade de êxito do que seria necessário.
Q# apresenta a amplificação de amplitude como uma especialização de amplificação de amplitude alheia. A
amplificação de amplitude alheia ganha esse moniker porque o projetor no autoespaço inicial não precisa ser
um projetor no estado inicial. Nesse sentido, o protocolo está alheio ao estado inicial. O principal uso da
amplificação de amplitude alheia está em determinadas combinações lineares de métodos de simulação
hamiltonianos unitários, em que o estado inicial é desconhecido, mas torna-se intricado com um registro
auxiliar no protocolo de simulação. Se esse registro auxiliar for medido como um valor fixo, como $0$, esses
métodos de simulação aplicarão a transformação unitária desejada aos qubits restantes (chamado de registro
do sistema). No entanto, todos os outros resultados de medida resultam em falha. A amplificação de amplitude
alheia permite que a probabilidade de sucesso dessa medida seja aumentada para $100\%$ usando o raciocínio
acima. Além disso, a amplificação de amplitude comum corresponde ao caso em que o registro do sistema está
vazio. É por isso que o Q# usa amplificação de amplitude de alheia como a sub-rotina básica de amplificação de
amplitude.
A rotina geral ( AmpAmpObliviousByReflectionPhases ) tem dois registros que chamamos de ancillaRegister e
systemRegister . Ela também aceita dois oracles para as reflexões necessárias. O ReflectionOracle age somente
no ancillaRegister enquanto o ObliviousOracle atua em conjunto em ambos os registros. A entrada para
ancillaRegister deve ser inicializada em um autoestado -1 do primeiro operador de reflexão $\boldone -
2P_1$.
Normalmente, o oracle prepara o estado na base computacional $\ket{0...0}$. Em nossa implementação, o
ancillaRegister consiste em um qubit ( flagQubit ) que controla o stateOracle e o restante dos auxiliares
desejados. O stateOracle é aplicado quando flagQubit for $\ket{1}$.
Um deles também pode fornecer os oracles StateOracle e ObliviousOracle , em vez de reflexões, por meio de
uma chamada para AmpAmpObliviousByOraclePhases .
Como mencionado, a amplificação de amplitude tradicional é apenas um caso especial dessas rotinas em que
ObliviousOracle é o operador de identidade e não há qubits de sistema (por exemplo, systemRegister está
vazio). Se você quiser obter fases para reflexões parciais (por exemplo, para pesquisa de Grover), a função
AmpAmpPhasesStandard estará disponível. Consulte DatabaseSearch.qs para obter um exemplo de implementação
do algoritmo de Grover.
Relacionamos as fases de rotação de qubit único às fases do operador de reflexão, conforme descrito no artigo
de G.H. Low, I. L. Chuang. As fases de ponto fixo usadas estão detalhadas em Yoder, Low e Chuang, juntamente
com as fases em Low, Yoder e Chuang.
Para contexto, você pode começar por Amplificação de Amplitude Padrão e seguir para uma introdução à
Amplificação de Amplitude Alheia e, por fim, conferir as generalizações apresentadas em Low e Chuang. Uma
boa apresentação de visão geral de toda a área (a relação com a simulação hamiltoniana) foi fornecida por
Dominic Berry.

Transformada de Fourier Quântica


A transformada de Fourier é uma ferramenta fundamental de análise clássica e é igualmente importante para
computações quânticas. Além disso, a eficiência da QFT (transformada de Fourier quântica) supera muito o que
é possível em um computador clássico, tornando-a em uma das principais ferramentas recomendadas ao criar
um algoritmo quântico.
Como uma generalização aproximada da QFT, fornecemos a operação ApproximateQFT operation que permite
mais otimizações por meio da remoção de rotações que não são estritamente necessárias para a precisão do
algoritmo desejado. A QFT aproximada exige a operação díade de rotação em $Z$ RFrac operation, bem como a
operação H operation. A entrada e a saída são consideradas codificadas em codificação big endian – ou seja, o
qubit com índice 0 é codificado no bit mais à esquerda (mais alto) da representação de número inteiro binário.
Isso se alinha com a notação ket, uma vez que um registro de três qubits no estado $\ket{100}$ corresponde a
$q_0$ estar no estado $\ket{1}$ enquanto $q_1$ e $q_2$ estão ambos no estado $\ket{0}$. O parâmetro de
aproximação $a$ determina o nível de remoção das rotações $Z$, por exemplo, $a \in [0..n]$. Nesse caso, todas
as rotações $Z$ $2\pi/2^k$ em que $k > a$ são removidos do circuito da QFT. Sabe-se que para $k \ge
\log_2(n) + \log_2(1 / \epsilon) + 3$. é possível limitar $\| \operatorname{QFT} - \operatorname{AQFT} \| <
\epsilon$. Aqui $\|\cdot\|$ é a norma do operador que, nesse caso, é a raiz quadrada do maior autovalor de
$(\operatorname{QFT} - \operatorname{AQFT})(\operatorname{QFT} - \operatorname{AQFT})^\dagger$.

Aritmético
Assim como a aritmética desempenha uma função central na computação clássica, ela também é indispensável
na computação quântica. Algoritmos como o algoritmo de fatoração de Shor, métodos de simulação quântica e
muitos algoritmos oraculares dependem de operações aritméticas coerentes. A maioria das abordagens à
aritmética se baseiam em circuitos de somador quântico. O somador mais simples usa uma entrada clássica de
$b$ e adiciona o valor a um estado quântico contendo um número inteiro $\ket{a}$. Matematicamente, o
somador (que denotamos $\operatorname{Add}(b)$ para entrada clássica de $b$) tem a propriedade em que
$$ \operatorname{Add}(b)\ket{a}=\ket{a + b}. $$ Esse circuito de somador básico é mais um incrementador do
que um somador. Ele pode ser convertido em um somador que tem duas entradas quânticas por meio de $$
\operatorname{Add}\ket{a}\ket{b}=\ket{a}\ket{a+b}, $$ usando aplicativos controlados de somadores da forma
\begin{align} \operatorname{Add} \ket{a} \ket{b} & = \Lambda_{a_0} \left(\operatorname{Add}(1) \right)
\Lambda_{a_1} \left(\operatorname{Add}(2) \right) \Lambda_{a_2} \left(\operatorname{Add}(4) \right) \cdots
\Lambda_{a_{n-1}} \left(\operatorname{Add}({{n-1}}) \right) \ket{a}\ket{b} \\ & = \ket{a} \ket{b + a}, \end{align}
para inteiros $a$ e $b$ de $n$-bit e um módulo adicional $2^n$. Lembre-se de que a notação $\Lambda_x(A)$
se refere, para toda operação $A$, à versão controlada dessa operação com o qubit $x$ como controle.
Da mesma forma, a multiplicação clássica controlada (uma forma modular essencial para o algoritmo de
fatoração de Shor) pode ser executada usando uma série semelhante de adições controladas: \begin{align}
\operatorname{Mult}(a)\ket{x}\ket{b} & = \Lambda_{x_0}\left(\operatorname{Add}(2^0 a)\right)
\Lambda_{a_1}\left(\operatorname{Add}(2^1a)\right) \Lambda_{a_2}\left(\operatorname{Add}(2^2 a)\right)
\cdots \Lambda_{x_{n-1}} \left(\operatorname{Add}({2^{n-1}}a) \right)\ket{x}\ket{b} \\ & = \ket{x}\ket{b+ax}.
\end{Align} Há uma sutileza na multiplicação em computadores quânticos que você pode notar na definição de
$\operatorname{Mult}$ acima. Ao contrário da adição, a versão quântica desse circuito armazena o produto das
entradas em um registro auxiliar em vez do registro de entrada. Neste exemplo, o registro é inicializado com o
valor $b$, mas normalmente ele começará ao manter o valor zero. Isso é necessário porque não costuma haver
um inverso multiplicativo para $a$ e $x$ gerais. Como todas as operações quânticas, exceto medida, são
reversíveis, precisamos manter informações suficientes para inverter a multiplicação. Por esse motivo, o
resultado é armazenado em uma matriz separada. Esse truque de salvar a saída de uma operação irreversível,
como a multiplicação, em um registro separado é conhecido como "truque Bennett" em homenagem a Charlie
Bennett e é uma ferramenta fundamental na computação quântica e reversível.
Muitos circuitos quânticos foram propostos para adição e cada um explora uma compensação diferente em
termos do número de qubits (espaço) e do número de operações de portão (tempo) necessário. Examinaremos
dois somadores com alta economia de espaço abaixo, conhecidos como o somador Draper e o somador
Beauregard.
Somador Draper
O somador Draper é possivelmente um dos somadores quânticos mais distintos, pois ele invoca diretamente as
propriedades quânticas para executar a adição. O insight por trás do somador Draper é que a transformada de
Fourier pode ser usada para transformar turnos de fase em um turno de bit. Em seguida, ele aplica uma
transformada de Fourier, aplicando os turnos de fase apropriados. Depois, ao desfazer a transformada de
Fourier, você pode implementar um somador. Ao contrário de muitos outros somadores apresentados, o
somador Draper usa explicitamente os efeitos quânticos introduzidos por meio da transformada de Fourier
quântica. Ele não tem um equivalente clássico natural. As etapas específicas do somador Draper são fornecidas
abaixo.
Suponha que você tem dois registros de qubit $n$-bit que armazenam os inteiros $a$ e $b$, então, para todos
os $a$ $$ \operatorname{QFT}\ket{a}= \frac{1}{\sqrt{2^n}}\sum_{ j=0}^{2^n-1} e^{i2\pi(aj)/2^n} \ket{ j}. $$ Se
definirmos $$ \ket{\phi_k(a)} = \frac{1}{\sqrt{2}}\left(\ket{0} + e^{i2\pi a /2^k}\ket{1} \right), $$ depois de um
pouco de álgebra você verá que $$ \operatorname{QFT}\ket{a}=\ket{\phi_1(a)}\otimes \cdots \otimes
\ket{\phi_n(a)}. $$ O caminho para executar um somador fica claro depois de observar que a soma das entradas
pode ser escrita como $$ \ket{a+b}=\operatorname{QFT}^{-1}\ket{\phi_1(a+b)}\otimes \cdots \otimes
\ket{\phi_n(a+b)}. $$ Os inteiros $b$ e $a$ podem ser adicionados executando a rotação de fase controlada em
cada qubits na decomposição usando os bits de $b$ como controles.
Essa expansão pode ser ainda mais simplificada ao observar que, para qualquer inteiro $j$ e número real $x$,
$e^{i2\pi(x+j)}=e^{i2\pi x}$. Isso ocorre porque se você girar $360^{\circ}$ graus ($2\pi$ radianos) em um
círculo, você acabará precisamente onde começou. A única parte importante de $x$ para $e^{i2\pi x}$ é,
portanto, a parte fracionária de $x$. Especificamente, se tivermos uma expansão binária da forma
$x=y+0.x_0x_2\ldots x_n$ então $e^{i2\pi x}=e^{i2\pi (0.x_0x_2\ldots x_{n-1})}$ e, portanto,
$$\ket{\phi_k(a+b)}=\frac{1}{\sqrt{2}}\left(\ket{0} + e^{i2\pi [a/2^k+0.b_k\ldots b_1]}\ket{1} \right).$$ Isso
significa que, se adicionarmos ao incrementar cada um dos fatores de tensor na expansão da transformada de
Fourier de $\ket{a}$, o número de rotações diminuirá conforme $k$ diminuir. Isso reduz substancialmente o
número de portões quânticos necessários no somador. Denotamos a transformada de Fourier, a adição de fase e
as etapas da transformada de Fourier inversa que compõem o somador Draper como $\operatorname{QFT}^{-
1} \left(\phi\!\operatorname{ADD}\right) \operatorname{QFT}$. Um circuito quântico que usa essa simplificação
para implementar todo o processo pode ser conferido abaixo.

Cada portão $e^{i2\pi/k}$ controlado no circuito se refere a um portão de fase controlada. Esses portões têm a
propriedade que está no par de qubits em que atuam, $\ket{00}\mapsto \ket{00}$, mas $\ket{11}\mapsto
e^{i2\pi/k}\ket{11}$. Esse circuito nos permite realizar adições sem usar qubits adicionais além daqueles
necessários para armazenar as entradas e as saídas.
Somador Beauregard
O somador Beauregard é um somador modular quântico que usa o somador Draper para executar o módulo de
adição $N$ para um inteiro positivo de valor arbitrário $N$. A significância de somadores modulares quânticos,
como o somador Beauregard, se deve a uma ampla extensão de uso na etapa de exponenciação modular dentro
do algoritmo de Shor para fatoração. Um somador modular quântico tem a seguinte ação para a entrada
quântica $\ket{b}$ e a entrada clássica $a$, em que $a$ e $b$ são prometidos como inteiros mod $N$, o que
significa que eles estão no intervalo $[0,\ldots, N-1]$.
$$ \ket{b}\rightarrow \ket{b+a \text{ mod }N}=\begin{cases} \ket{b+a},& b+a < N\\ \ket{b+a-N},& (b+a)\ge N
\end{cases}. $$
O somador Beauregard usa o somador Draper, ou mais especificamente$\phi\!\operatorname{ADD}$, para
adicionar $a$ e $b$ na fase. Em seguida, ele usa a mesma operação para identificar se $a+b <N$ ao subtrair
$N$ e testar se $a+b-N<0$. O circuito armazena essas informações em um qubit auxiliar e adiciona $N$ de
volta ao registro se $a+b<N$. Em seguida, ele é concluído por meio da descomputação deste bit auxiliar (essa
etapa é necessária para garantir que o auxiliar possa ser desalocado depois de chamar o somador). O circuito
para o somador Beauregard é fornecido abaixo.
Aqui, o portão $\Phi\!\operatorname{ADD}$ usa a mesma forma que $\phi\!\operatorname{ADD}$, exceto que
nesse contexto a entrada é clássica em vez de quântica. Isso permite que as fases controladas em
$\Phi\!\operatorname{ADD}$ sejam substituídas por portões de fase que podem ser compilados em menos
operações, para reduzir o número de qubits e o número de portões necessários para o somador.
Para obter mais detalhes, consulte M. Roetteler, Th. Beth e D. Coppersmith.
Estimativa de fase quântica
Um uso especialmente importante da transformada de Fourier quântica é saber os autovalores de operadores
unitários, um problema conhecido como estimativa de fase. Considere um unitário $U$ e um estado
$\ket{\phi}$, de forma que $\ket{\phi}$ seja um autoestado de $U$ com autovalor desconhecido $\phi$,
\begin{equation} U\ket{\phi} = \phi\ket{\phi}. \end{equation} Se só tivermos acesso ao $U$ como oracle,
podemos saber que a fase $\phi$ utilizando as rotações em $Z$ aplicadas ao destino de uma operação
controlada se propaga de volta para o controle.
Suponha que $V$ é um aplicativo controlado de $U$, de modo que begin{align} V (\ket{0} \otimes \ket{\phi}) &
= \ket{0} \otimes \ket{\phi} \\ \textrm{ e } V (\ket{1} \otimes \ket{\phi}) & = e^{i \phi} \ket{1} \otimes \ket{\phi}.
\end{align} Em seguida, por linearidade, \begin{align} V(\ket{+} \otimes \ket{\phi}) & = \frac{ (\ket{0} \otimes
\ket{\phi}) + e^{i \phi} (\ket{1} \otimes \ket{\phi}) }{\sqrt{2}}. \end{Align}, Podemos coletar termos para
descobrir que \begin{align} V(\ket{+} \otimes \ket{\phi}) & = \frac{\ket{0} + e^{i \phi} \ket{1}}{\sqrt{2}} \otimes
\ket{\phi} \\ & = (R_1(\phi) \ket{+}) \otimes \ket{\phi}, \end{align} em que $R_1$ é o unitário aplicado pela
operação R1 operation. Em outras palavras, o efeito da aplicação de $V$ é precisamente o mesmo que aplicar
$R_1$ com um ângulo desconhecido, mesmo que tenhamos acesso apenas ao $V$ como oracle. Portanto, para
o restante desta discussão, discutiremos a estimativa de fase em termos de $R_1(\phi)$, implementada usando
a chamada fase kickback.
Como o controle e o registro de destino permanecem separados após esse processo, podemos reutilizar
$\ket{\phi}$ como o destino de um aplicativo controlado de $U^$2 para preparar um segundo controle qubit
no estado $R_1(2 \phi) \ket{+}$. Continuando dessa forma, podemos obter um registro da forma begin{align}
\ket{\psi} & = \sum_{ j = 0}^n R_1(2^j \phi) \ket{+} \\ & \propto \bigotimes_{ j=0}^{n} \left(\ket{0} + \exp(i
2^{ j} \phi) \ket{1}\right) \\ & \propto \sum_{k = 0}^{2^n - 1} \exp(i \phi k) \ket{k} \end{align} em que $n$ é o
número de bits de precisão que exigimos, e onde usamos ${} \propto {}$ para indicar que suprimimos o fator de
normalização de $1 / \sqrt{2^n}$.
Se presumirmos que $\phi = 2 \pi p / 2^k$ para um inteiro $p$, reconheceremos isso como $ $\ket{\psi} =
\operatorname{QFT} \ket{p_0 p_1 \dots p_n}$, em que $p_j$ é o bit $j^{\textrm{th}}$ de $2 \pi \phi$. Ao aplicar
o adjunto da transformada de Fourier quântica, obtemos a representação binária da fase codificada como um
estado quântico.
Em Q#, isso é implementado pela operação QuantumPhaseEstimation operation, que usa um aplicativo de
implementação DiscreteOracle user defined typede $U^m$ como uma função de inteiros positivos $m$.
Diagnósticos
01/05/2021 • 8 minutes to read

Assim como no desenvolvimento clássico, é importante poder diagnosticar enganos e erros em programas do
Quantum. As bibliotecas padrão Q# fornecem várias maneiras diferentes de garantir a exatidão dos programas
do Quantum, conforme detalhado em Testar e depurar programas quantum. Em grande parte, esse suporte é
fornecido na forma de funções e operações que instruem o computador de destino a fornecer informações
adicionais de diagnóstico para o programa ou desenvolvedor do host, ou impõem a exatidão das condições e
invariáveis expressas pela função ou pela chamada de operação.

Diagnósticos do computador
O diagnóstico sobre valores clássicos pode ser obtido usando a funçãoMessage function para registrar uma
mensagem de forma dependente do computador. Por padrão, isso grava a cadeia de caracteres no painel. Usado
junto com cadeias de caracteres interpoladas, Message function facilita o relatório de informações de
diagnóstico sobre valores clássicos:

let angle = Microsoft.Quantum.Math.PI() * 2.0 / 3.0;


Message($"About to rotate by an angle of {angle}...");

NOTE
Message Ele tem assinatura (String -> Unit) , representando novamente que a emissão de uma mensagem de log de
depuração não pode ser observada de dentro do Q#.

O DumpMachine function e o chamadores DumpRegister function instruem os computadores de destino a


fornecer informações de diagnóstico sobre todos os qubits atualmente alocados ou sobre um registro específico
do qubits, respectivamente. Cada computador de destino varia de acordo com as informações de diagnóstico
fornecidas em resposta a uma instrução de despejo. A máquina de destino do simulador de estado completo,
por exemplo, fornece o programa de host com o vetor de estado que ele usa internamente para representar um
registro de qubits. Por comparação, a máquina de destino do Simulador Toffoli fornece um único bit clássico
para cada qubit.
Para saber mais sobre a saída do simulador de estado completo DumpMachine , dê uma olhada na seção de
funções de despejo do nosso Artigo de teste e depuração.

Fatos e declarações
Conforme discutido em teste e depuração, uma função ou operação com assinatura Unit -> Unit ou
Unit => Unit , respectivamente, pode ser marcada como um teste de unidade. Cada teste de unidade
geralmente consiste em um pequeno programa do Quantum, juntamente com uma ou mais condições que
verificam a exatidão desse programa. Essas condições podem vir na forma de fatos, que verificam os valores de
suas entradas ou declarações, que verificam os estados de um ou mais qubits passados como entrada.
Por exemplo, EqualityFactI(1 + 1, 2, "1 + 1 != 2") representa o fato matemático que $1 + 1 = $2, enquanto
AssertQubit(One, qubit) representa a condição que a medição qubit retornará um One com certeza. No
primeiro caso, podemos verificar a exatidão da condição, dado apenas seus valores, enquanto, no último,
devemos saber algo sobre o estado do qubit para avaliar a declaração.
As bibliotecas padrãoQ# fornecem várias funções diferentes para representar fatos, incluindo:
Fact function
EqualityWithinToleranceFact function
NearEqualityFactC function
EqualityFactI function
Testando Estados de qubit
Na prática, as declarações dependem do fato de que as simulações clássicas da mecânica quântica não precisam
obedecer à teorema no-Cloning, de modo que podemos fazer medidas e declarações não físicas ao usar um
simulador para o nosso computador de destino. Portanto, podemos testar operações individuais em um
simulador clássico antes de implantar em hardware. Em computadores de destino que não permitem a
avaliação de declarações, as chamadas para AssertMeasurement operationpodem ser ignoradas com segurança.
Em geral, a operação AssertMeasurement operation declara que medir o qubits fornecido na base Pauli
especificada sempre terá o resultado especificado. Se a asserção falhar, a execução terminará chamando fail
com a mensagem fornecida. Por padrão, essa operação não é implementada; os simuladores que podem
oferecer suporte a ele devem fornecer uma implementação que executa a verificação de tempo de execução.
AssertMeasurement Ele tem assinatura ((Pauli[], Qubit[], Result, String) -> ()) . Como é uma função
AssertMeasurement com uma tupla vazia como seu tipo de saída, nenhum efeito de tendo chamado
AssertMeasurement é observável em um programaQ#.

A função AssertMeasurementProbability operation de operação declara que medindo o determinado qubits na


base de Pauli especificada terá o resultado fornecido com a probabilidade determinada, dentro de certa
tolerância. A tolerância é aditiva (por exemplo, abs(expected-actual) < tol ). Se a asserção falhar, a execução
terminará chamando fail com a mensagem fornecida. Por padrão, essa operação não é implementada; os
simuladores que podem oferecer suporte a ele devem fornecer uma implementação que executa a verificação
de tempo de execução. AssertMeasurementProbability Ele tem assinatura
((Pauli[], Qubit[], Result, Double, String, Double) -> Unit) . O primeiro dos parâmetros Double fornece a
probabilidade desejada do resultado e o segundo a tolerância.
Podemos fazer mais do que declarar uma única medida, usando que as informações clássicas usadas por um
simulador para representar o estado interno de um qubit é receptivos para copiar, de modo que não precisamos
realmente executar uma medida para testar nossa declaração. Em particular, isso nos permite motivos sobre
medidas incompatíveis que seriam impossíveis em hardware real.
Suponha que P : Qubit => Unit seja uma operação destinada a preparar o estado $\ket{\psi} $ quando sua
entrada estiver no estado $\ket {0} $. Deixe que $ \ket{\psi '} $ seja o estado real preparado pelo P . Em seguida,
$ \ket{\psi} = \ket{\psi '} $ se e apenas se medindo $ \ket{\psi '} $ no eixo descrito por $ \ket{\psi} $ sempre
retorna Zero . Ou seja, \begin{align} \ket{\psi} = \ket{\psi '} \Text{If e only if} \braket{\psi | \psi '} = 1. \end{align}
usando as operações primitivas definidas no prelúdio, podemos executar diretamente uma medida que retorna
Zero se $ \ket{\psi} $ for um estado eigen de um dos operadores Pauli.

A operação AssertQubit operation fornece uma abreviação particularmente útil para fazer isso no caso em que
desejarmos testar a asserção $ \ket{\psi} = \ket{0}$. Isso é comum, por exemplo, quando não computamos para
retornar ancilla qubits a $ \ket{0}$ antes de liberá-los. A declaração em relação a $ \ket {0}$ também é útil
quando desejamos declarar que as operações de preparação de dois Estados preparam P e Q o mesmo estado
e quando o Q dá suporte ao Adjoint . Especialmente,
use register = Qubit();
P(register);
Adjoint Q(register);

AssertQubit(Zero, register);

No entanto, em geral, talvez não tenhamos acesso as declarações sobre estados que não coincidem com
estados eigen de operadores Pauli. Por exemplo, $ \ket{\psi} = (\ket{0} +e^ {i \pi/8} \ket{1})/\sqrt{2}$ não é um
estado eigen de nenhum operador Pauli, de modo que não podemos usar AssertMeasurementProbability
operation para determinar exclusivamente que um estado $ \ket{\psi '} $ é igual a $ \ket{\psi} $. Em vez disso,
devemos decompor a asserção $\ket{\psi '} = \ket{\psi} $ em suposições que podem ser diretamente testadas
usando os primitivos suportados pelo nosso simulador. Para fazer isso, deixe o \ket{\psi} = \alpha\ket{0} + \beta
\ket{1}$ para números complexos $ \alpha = _ r + a_i i$ e $ \beta $. Observe que essa expressão requer quatro
números reais ${a_r, a_i, b_r, b_i}$ para especificar, pois cada número complexo pode ser expresso como a soma
de uma parte real e imaginário. No entanto, devido à fase global, podemos escolher $a_ i = $0, de modo que
precisamos apenas de três números reais para especificar exclusivamente um estado de qubit único.
Portanto, precisamos especificar três declarações que são independentes umas das outras para declarar o
estado esperado. Fazemos isso encontrando a probabilidade de observar Zero cada medição de Pauli,
considerando $ \alpha$ e $\beta$, e declarando cada uma independentemente. Deixe $x$, $y$ e $z$ sejam
valores Result para Pauli $X$, $Y$ e $Z$ medições, respectivamente. Em seguida, usando a função
probabilidade para medições quânticas, \begin{align} \Pr(x = \texttt{Zero} | \alpha, \beta) & = \frac12 + a_r b_r
+ a_i b_i \\ \Pr(y = \texttt{Zero} | \alpha, \beta) & = \frac12 + a_r b_i - a_i b_r \\ \Pr(z = \texttt{Zero} | \alpha,
\beta) & = \frac12\left( 1 + a_r^2 + a_i^2 + b_r^2 + b_i^2 \right). \end{align}
A operaçãoAssertQubitIsInStateWithinTolerance operation implementa essas asserções dadas as representações
de $\alpha$ e $ \beta $ como valores do tipo Complex user defined type. Isso é útil quando o estado esperado
pode ser calculado matematicamente.
Declarando a igualdade de operações Quânticas
Até agora, estamos preocupados com as operações de teste que destinam-se a preparar estados específicos. No
entanto, muitas vezes estamos interessados em como uma operação atua para entradas arbitrárias em vez de
uma única entrada fixa. Por exemplo, suponha que implementamos uma operação
U : ((Double, Qubit[]) => () : Adjoint) correspondente a uma família de operadores unitários $U(t) $ e
fornecemos um adjoint bloco explícito em vez de usar adjoint auto . Talvez esteja interessado em afirmar que
$U^\dagger (t) = U(-t) $, conforme esperado se $t$ representa um tempo de evolução.
Em geral, há duas estratégias diferentes que podemos seguir para fazer a declaração de que duas operações U
e agir de forma V idêntica. Primeiro, podemos verificar se U(target); (Adjoint V)(target); preserva cada
estado em uma determinada base. Em segundo lugar, podemos verificar que U(target); (Adjoint V)(target);
agir na metade de um estado confusas preserva esse entrelaçamento. Essas estratégias são implementadas
pelas operações de Canon AssertOperationsEqualInPlace operation e AssertOperationsEqualReferenced
operation, respectivamente.

NOTE
A declaração referenciada mencionada acima funciona com base no Choi – Jamiłkowski isomorfismo, uma estrutura
matemática que relaciona as operações em $n$ qubits a confusas estados em $2n$ qubits. Em particular, a operação de
identidade em $n$ qubits é representada por $n$ as cópias do estado confusas $ \ket{\ beta_{00}} \mathrel{: =} (\ket {00}
+ \ket {11} )/\sqrt{2}$. A operação PrepareChoiState operation implementa esse isomorfismo, preparando um estado que
representa uma determinada operação.

Aproximadamente, essas estratégias são diferenciadas por uma compensação de espaço em tempo. A iteração
em cada estado de entrada leva mais tempo, ao passo que o uso de entrelaçamento como referência requer o
armazenamento de qubits adicionais. Nos casos em que uma operação implementa uma operação clássica
reversível, de modo que estamos interessados apenas em seu comportamento nos estados de base
computacional, AssertOperationsEqualInPlaceCompBasis operation testa a igualdade nesse conjunto restrito de
entradas.

TIP
A iteração sobre estados de entrada é tratada pelas operações de enumeração IterateThroughCartesianProduct operation
e IterateThroughCartesianPower operation. Essas operações são úteis mais geralmente para aplicar uma operação a cada
elemento do produto cartesiano entre dois ou mais conjuntos.

Mais importante, no entanto, as duas abordagens testam diferentes propriedades das operações em exame.
Como a declaração in-loco chama cada operação várias vezes, uma vez para cada estado de entrada, quaisquer
escolhas aleatórias e resultados de medidas podem ser alterados entre as chamadas. Por outro lado, a
declaração referenciada chama cada operação exatamente uma vez, de modo que ela verifica se as operações
são iguais em uma única captura. Esses dois testes são úteis para garantir a exatidão dos programas do
Quantum.

Leitura Adicional
Testar e depurar programas quantum
Microsoft.Quantum.Diagnostics namespace
Caracterização e estatísticas quânticas
15/05/2021 • 13 minutes to read

É fundamental conseguir caracterizar os efeitos das operações para desenvolver algoritmos quânticos úteis. Isso
é desafiador porque cada medida de um sistema quântico gera no máximo um bit de informação. Para aprender
um autovalor, independente de um estado quântico, os resultados de várias medidas devem ser unidos para que
o usuário possa obter os muitos bits de informações necessários para representar esses conceitos. Os estados
quânticos são especialmente complicados porque o teorema sem clonagem afirma que não há como aprender
um estado quântico arbitrário a partir de uma única cópia do estado, pois isso permite que você faça cópias do
estado. Essa ofuscação do estado quântico do usuário é refletida no fato de Q# não expor ou até mesmo definir
o que um estado é para os programas quânticos. Portanto, abordamos a caracterização quântica tratando as
operações e os estados como caixa preta. Essa abordagem tem muito em comum com a prática experimental de
QCVV (caracterização quântica, verificação e validação).
A caracterização é distinta de muitas das outras bibliotecas discutidas anteriormente. O objetivo aqui é menos
focado em aprender informações clássicas sobre o sistema, em vez de executar uma transformação unitária em
um vetor de estado. Portanto, essas bibliotecas devem misturar o processamento de informações clássicas e
quânticas.

Estimativa de fase iterativa


A exibição da programação quântica em termos de caracterização quântica sugere uma alternativa útil à
estimativa da fase quântica. Ou seja, em vez de preparar um registro de um qubit $n$ para conter uma
representação binária da fase como na estimativa da fase quântica, podemos exibir a estimativa de fase como o
processo pelo qual um agente clássico aprende as propriedades de um sistema quântico por meio de medidas.
Prosseguiremos como no caso quântico usando a fase kickback para transformar os aplicativos de uma
operação de caixa preta em rotações por um ângulo desconhecido, mas mediremos o qubit auxiliar que giramos
em cada etapa imediatamente após a rotação. A vantagem é que precisaremos de apenas um qubit único
adicional para executar a fase kickback descrita no caso quântico, ao aprendermos a fase dos resultados da
medida em cada etapa de maneira iterativa.
Cada um dos métodos propostos abaixo usa uma estratégia diferente para a criação de experimentos e
métodos de processamento de dados diferentes para aprender a fase. Cada um tem uma vantagem única, desde
limites de erro rigorosos até capacidade de incorporar informações anteriores, tolerar erros ou executar em
computadores clássicos com limite de memória.
Ao discutir a estimativa de fase iterativa, consideraremos um unitário $U$ fornecido como uma operação de
caixa preta. Conforme descrito na seção sobre oracles em estruturas de dados, o cânone de Q# modela as
operações pelo tipo definido pelo usuário DiscreteOracle user defined type, definidas pelo tipo de tupla
((Int, Qubit[]) => Unit : Adjoint, Controlled) . Concretamente, se U : DiscreteOracle , então U(m)
implementa $U^m$ para m : Int .
Com essa definição em vigor, cada etapa da estimativa de fase iterativa continua ao preparar um qubit auxiliar
no estado $\ket{+}$, juntamente com o estado inicial $\ket{\phi}$ que supomos que seja um autovetor de
$U(m)$, por exemplo, $U(m)\ket{\phi}= e^{im\phi}\ket{\phi}$.
Um aplicativo controlado do U(m) é usado, o que prepara o estado $\left(R_1(m \phi) \ket{+}\right)\ket{\phi}$.
Como no caso quântico, o efeito de um aplicativo controlado do oracle U(m) é precisamente o mesmo que o
efeito da aplicação de $R_1$ para a fase desconhecida em $\ket{+}$, de forma que possamos descrever os
efeitos de $U$ desta maneira mais simples. Opcionalmente, o algoritmo gira o qubit de controle aplicando
$R_1(-m\theta)$ para obter um estado $\ket{\psi}=\left(R_1(m [\phi-\theta]) \ket{+}\right)\ket{\phi}$$. O qubit
auxiliar usado como controle para U(m) é medido na base $X$ para obter um Result clássico único.
Neste ponto, a reconstrução da fase a partir dos valores Result obtidos por meio da estimativa de fase iterativa
é um problema de inferência de estatística clássica. Encontrar o valor de $m$ que maximize as informações
obtidas, considerando um método de inferência fixo, é simplesmente um problema nas estatísticas. Enfatizamos
isso descrevendo brevemente a estimativa de fase iterativa em um nível teórico no formalismo de estimativa de
parâmetros bayesianos antes de continuar a descrever os algoritmos estatísticos fornecidos no cânone de Q#
para resolver esse problema de inferência clássica.
Estimativa de fase iterativa sem autoestados
Se for fornecido um estado de entrada que não seja um autoestado, ou seja $U(m)\ket{\phi_j} = e^{im\phi_j}$, o
processo de estimativa de fase não determinística guiará o estado quântico em direção a um único autoestado
de energia. O autoestado que acabará sendo convergido será o autoestado com maior probabilidade de
produzir o Result observado.
Especificamente, uma única etapa de PE executa a seguinte transformação não unitária em um estado
\begin{align} \sum_j \sqrt{\Pr(\phi_j)} \ket{\phi_j} \mapsto
\sum_j\frac{\sqrt{\Pr(\phi_j)}\sqrt{\Pr(\text{Result}|\phi_j)}\ket{\phi_j}}{\sqrt{\Pr(\phi_j)\sum_j
\Pr(\text{Result}|\phi_j)}}. \end{align} Como esse processo é iterado em vários valores de Result , os
autoestados sem valores máximos de $\prod_k\Pr(\text{Result}_k|\phi_j)$ serão suprimidos exponencialmente.
Como resultado, o processo de inferência terá a tendência de convergir para estados com um autovalor único se
os experimentos forem escolhidos corretamente.
O teorema de Bayes sugere ainda que o estado resultante da estimativa de fase será gravado na forma
\begin{align} \frac{\sqrt{\Pr(\phi_j)}\sqrt{\Pr(\text{Result}|\phi_j)}\ket{\phi_j}}{\sqrt{\Pr(\phi_j)\sum_j
\Pr(\text{Result}|\phi_j)}}=\sum_j \sqrt{\Pr(\phi_j|\text{Result})} \ket{\phi_j}. \end{align} Aqui,
$\Pr(\phi_j|\text{Result})$ pode ser interpretado como a probabilidade de atribuição a cada hipótese sobre os
autoestados fornecidos:
1. conhecimento do estado quântico antes da medição,
2. conhecimento dos autoestados de $U$ e
3. conhecimento dos autovalores de $U$.
Descobrir essas três coisas é, muitas vezes, exponencialmente difícil em um computador clássico. A utilidade de
estimativa de fase surge, em grande medida, do fato de que ela pode executar tal tarefa de aprendizado
quântico sem conhecer nenhuma delas. Por esse motivo, a estimativa de fase aparece dentro de um número de
algoritmos quânticos que fornecem aumentos exponenciais.
Estimativa de fase bayesiana

TIP
Para obter mais detalhes sobre a estimativa de fase bayesiana na prática, confira a amostra PhaseEstimation .

A ideia da estimativa de fase bayesiana é simples. Você coleta estatísticas de medida do protocolo de estimativa
de fase, processa os resultados usando a inferência bayesiana e fornece uma estimativa do parâmetro. Esse
processamento fornece uma estimativa do autovalor, bem como a incerteza nessa estimativa. Com ele, você
também pode executar experimentos adaptáveis e utilizar informações anteriores. A principal desvantagem do
método é que ele é computacionalmente exigente.
Para entender como esse processo de inferência bayesiana funciona, considere o caso de processamento de um
resultado Zero único. Observe que $X = \ket{+}\bra{+} - \ket{-}\bra{-}$, de modo que $\ket{+}$ é o único
autoestado positivo de $X$ correspondente a Zero . A probabilidade de observar Zero para uma PauliX
medida no primeiro qubit que recebeu um estado de entrada $\ket{\psi}\ket{\phi}$ é, portanto, \begin{equation}
\Pr(\texttt{Zero} | \psi) = \left| \braket{+ | \psi} \right|^2. \end{Equation} No caso de estimativa de fase iterativa,
temos que $\ket{\psi} = R_1(m [\phi-\theta]) \ket{+}$, de forma que \begin{align} \Pr(\texttt{Zero} | \phi;
m,\theta) & = \left| \braket{+ | R_1(m [\phi-\theta]) | +} \right|^2 \\ & = \left| \frac12 \left( \bra{0} + \bra{1}
\right) \left( \ket{0} + e^{i m [\phi-\theta]} \ket{1} \right) \right|^2 \\ & = \left| \frac{1 + e^{i m [\phi-\theta]}}{2}
\right|^2 \\ & = \cos^2(m [\phi-\theta] / 2) \tag{★} \label{eq:phase-est-likelihood}. \end{Align} Ou seja, a
estimativa de fase iterativa consiste em descobrir a frequência de oscilação de uma função senoidal, dada a
capacidade de decidir na sorte com um desvio fornecido por esse senoide. Seguindo a terminologia clássica
tradicional, chamamos $\eqref{eq:phase-est-likelihood} de função de probabilidade para estimativa de fase
iterativa.
Após observar um Result da função de probabilidade de estimativa de fase iterativa, podemos usar a regra de
Bayes para prescrever o que acreditamos que seja a fase após essa observação. Concretamente,
\begin{equation} \Pr(\phi | d) = \frac{\Pr(d | \phi) \Pr(\phi)}{\int \Pr(d | \phi) \Pr(\phi){\mathrm d}\phi} \Pr(\phi),
\end{equation} em que $d \in \{\texttt{Zero}, \texttt{One}\}$ é um Result e em que $\Pr(\phi)$ descreve o que
acreditávamos sobre $\phi$. Isso então torna explícita a natureza iterativa da estimativa de fase iterativa, já que
a distribuição posterior $\Pr(\phi | d)$ descreve nossas crenças imediatamente antes da nossa observação do
próximo Result .
A qualquer momento durante esse procedimento, podemos relatar a fase $\hat{\phi}$ inferida pelo controlador
clássico como \begin{equation} \hat{\phi} \mathrel{:=} \expect[\phi | \text{data}] = \int \phi \Pr(\phi | \text{data})
{\mathrm d}\phi, \end{equation} em que $\text{data}$ significa todo o registro de todos os valores de Result
obtidos.
A inferência bayesiana exata é, na prática, intratável. Para isso, imagine que queremos descobrir uma variável
$x$ de $n$-bit. A distribuição anterior $\Pr(x)$ tem suporte acima de valores hipotéticos $2^n$ de $x$. Isso
significa que, se precisarmos de uma estimativa altamente precisa de $x$, a estimativa de fase bayesiana poderá
precisar de memória e tempo de processamento proibitivos. Enquanto que para alguns aplicativos, como a
simulação quântica, a precisão limitada necessária não impede esses métodos, outros aplicativos, como o
algoritmo de Shor, não podem usar a inferência bayesiana exata na etapa de estimativa de fase. Por isso,
também fornecemos implementações para métodos bayesianos aproximados, como RWPE (estimativa de fase
de movimentação aleatória), e abordagens não bayesianas, como estimativa de fase robusta.
Estimativa de fase robusta
Uma reconstrução máxima bayesiana a posteriori de uma fase estimada dos resultados da medida é
exponencialmente difícil no pior caso. Assim, os algoritmos de estimativa de fase mais práticos abrem mão de
qualidade na reconstrução em troca de uma quantidade de pós-processamento clássico que, por sua vez, escala
polinomialmente com o número de medidas feitas.
Um exemplo com uma etapa de pós-processamento clássico eficiente é o algoritmo de estimativa de fase
robusta, com a assinatura e as entradas mencionadas acima. Ele pressupõe que as caixas pretas de entrada
unitária $U$ são empacotadas como tipo DiscreteOracle e, portanto, consulta apenas as potências de inteiros
de $U$ controlado. Se o estado de entrada no registro Qubit[] for um autoestado
$U\ket{\psi}=e^{i\phi}\ket{\psi}$, o algoritmo de estimativa de fase robusta retornará uma estimativa
$\hat{\phi}\in[-\pi,\pi)$ de $\phi$ como um Double .
O recurso mais importante da estimativa de fase robusta, que é compartilhado com a maioria das variantes
úteis, é que a qualidade de reconstrução de $\hat{\phi}$ está de certa forma limitada a Heisenberg. Isso significa
que, se o desvio de $\hat{\phi}$ do valor verdadeiro for $\sigma$, então $\sigma$ terá um dimensionamento
inversamente proporcional ao número total de consultas $Q$ feitas ao $U$ controlado, por exemplo,
$\sigma=\mathcal{O}(1/Q)$. A definição de desvio varia entre diferentes algoritmos de estimativa. Em alguns
casos, pode significar que, com pelo menos a probabilidade $\mathcal{O}(1)$, o erro de estimativa $|\hat{\phi}-
\phi|_\circ\le \sigma$ em alguma medida circular é $\circ$. Para estimativa de fase robusta, o desvio será
precisamente a variância $\sigma^2 = \mathbb{E}_\hat{\phi}[(\mod_{2\pi}(\hat{\phi}-\phi +\pi)-\pi)^2]$ se
cancelarmos a quebra de linha de fases periódicas em um intervalo finito $(-\pi,\pi]$ único. Mais precisamente,
o desvio padrão na estimativa de fase robusta satisfaz as desigualdades $$ \begin{align} 2.0 \pi / Q \le \sigma
\le 2\pi / 2^{n} \le 10.7\pi / Q, \end{align} $$ em que o limite inferior é alcançado no limite de $Q$
assintoticamente grande e o limite superior é garantido até mesmo para tamanhos de amostra pequenos.
Observe que $n$ é selecionado pela entrada bitsPrecision , que define implicitamente $Q$.
Outros detalhes relevantes incluem a sobrecarga de espaço pequeno de apenas $1$ qubit auxiliar ou que o
procedimento não é adaptável, o que significa que a sequência necessária de experimentos quânticos é
independente dos resultados de medida intermediários. Neste e em futuros exemplos em que a escolha do
algoritmo de estimativa de fase é importante, deve-se consultar a documentação como
@"microsoft.quantum.characterization.robustphaseestimation" e as publicações referenciadas aqui para obter
mais informações e para a implementação.

TIP
Há muitos exemplos em que a estimativa de fase robusta é usada. Para estimativa de fase na extração da energia de
estado terrestre de vários sistemas físicos, confira o exemplo de simulação H2 , o exemplo de SimpleIsing e o
exemplo de modelo Hubbard .

Oracles contínuos
Também podemos generalizar a partir do modelo oracle usado acima para permitir oracles de tempo contínuos,
modelados pelo tipo cânone ContinuousOracle user defined type. Considere que, em vez de um operador
unitário $U$ único, temos uma família de operadores unitários $U(t)$ para $t \in \mathbb{R}$, de forma que
$U(t) U(s)$ = $U(t + s)$. É um demonstrativo mais fraco do que no caso discreto, já que podemos construir um
DiscreteOracle user defined type restringindo $t = m,\delta t$ para alguns $\delta t$ fixos. Pelo teorema de
Stone, $U(t) = \exp(i H t)$ para um operador $H$, em que $\exp$ é o exponencial de matriz, conforme descrito
em matrizes avançadas. Um autoestado $\ket{\phi}$ de $H$, de modo que $H \ket{\phi} = \phi \ket{\phi}$
também é um autoestado de $U(t)$ para todos os $t$, \begin{equation} U(t) \ket{\phi} = e^{i \phi t} \ket{\phi}.
\end{equation}
A mesma análise discutida para a estimativa de fase bayesiana pode ser aplicada e a função de probabilidade é
exatamente a mesma para esse modelo de oracle mais geral: $$ \Pr(\texttt{Zero} | \phi;
t,\theta)=\cos^2\left(\frac{t[\phi -\theta]}{2}\right). $$ Além disso, se $U$ for uma simulação de um gerador
dinâmico, como é o caso da simulação hamiltoniana, interpretamos $\phi$ como uma energia. Portanto, o uso
da estimativa de fase com consultas contínuas nos permite descobrir o espectro de energia de moléculas, os
materiais ou as teorias de campo simuladas sem precisar comprometer nossa escolha de experimentos ao exigir
que $t$ seja um inteiro.
Estimativa de fase de movimentação aleatória
Q# fornece uma aproximação útil de estimativa de fase bayesiana projetada para uso próximo de dispositivos
quânticos que operam ao condicionar uma movimentação aleatória no registro de dados obtido a partir da
estimativa de fase iterativa. Esse método é adaptativo e totalmente determinístico, permitindo o
dimensionamento quase ideal de erros na fase estimada $\hat{\phi}$ com sobrecargas de memória muito
baixas.
O protocolo usa um método de inferência bayesiana aproximado que assume que a distribuição anterior é
gaussiana. Essa suposição gaussiana nos permite usar uma fórmula analítica para o experimento que minimiza
a variância posterior. O algoritmo, com base no resultado desse experimento, muda a estimativa de $\phi$ para
a esquerda ou direita por uma quantidade predeterminada e reduz a variância por um valor predeterminado.
Essa média e variância fornecem todas as informações necessárias para especificar um gaussiano anterior em
$\phi$ para o próximo experimento. Esse método pode falhar quando há erros de medição inesperados ou caso
o resultado real esteja na parte final do início anterior. Ele se recupera de uma falha com a execução de
experimentos para testar se a média atual e o desvio padrão são apropriados para o sistema. Se eles não forem,
o algoritmo fará uma etapa inversa da movimentação e o processo continuará. A capacidade de retroceder
também permite que o algoritmo aprenda mesmo que o desvio padrão anterior inicial seja inadequadamente
pequeno.
Chamada para algoritmos de estimativa de fase
Cada operação de estimativa de fase fornecida com o cânone Q# usa um conjunto diferente de entradas
parametrizando a qualidade que solicitamos da estimativa final de $\hat{\phi}$. No entanto, essas várias
entradas compartilham muitas entradas em comum, de modo que o aplicativo parcial nos parâmetros de
qualidade resulta em uma assinatura comum. Por exemplo, a operação RobustPhaseEstimation operation
discutida na próxima seção tem a seguinte assinatura:

operation RobustPhaseEstimation(bitsPrecision : Int, oracle : DiscreteOracle, eigenstate : Qubit[]) :


Double

A entrada bitsPrecision é exclusiva para RobustPhaseEstimation , enquanto oracle e eigenstate são em


comum. Assim, como visto em H2Sample , uma operação pode aceitar um algoritmo de estimativa de fase
iterativa com uma entrada da forma (DiscreteOracle, Qubit[]) => Unit para permitir que um usuário
especifique algoritmos de estimativa de fase arbitrária:

operation H2EstimateEnergy(
idxBondLength : Int,
trotterStepSize : Double,
phaseEstAlgorithm : ((DiscreteOracle, Qubit[]) => Double))
: Double

Esses algoritmos de estimativa de inúmeras fases são otimizados para diferentes propriedades e parâmetros de
entrada, que devem ser compreendidos para fazer a melhor escolha para o aplicativo de destino. Por exemplo,
alguns algoritmos de estimativa de fase são adaptáveis, o que significa que as etapas futuras são controladas de
maneira clássica pelos resultados de medida das etapas anteriores. Alguns exigem a capacidade de exponenciar
o oracle unitário de caixa preta com potências reais arbitrárias e outros exigem apenas potências de inteiros,
mas só podem resolver um módulo de estimativa de fase $2\pi$. Alguns exigem muitos qubits auxiliares e
outros exigem apenas um.
Da mesma forma, o uso da estimativa de fase de movimentação aleatória continua praticamente da mesma
forma que para outros algoritmos fornecidos com o cânone:

operation ApplyExampleOracle(
eigenphase : Double,
time : Double,
register : Qubit[])
: Unit is Adj + Ctl {
Rz(2.0 * eigenphase * time, register[0]);
}

operation EstimateBayesianPhase(eigenphase : Double) : Double {


let oracle = ContinuousOracle(ApplyExampleOracle(eigenphase, _, _));
use eigenstate = Qubit();
X(eigenstate);
// The additional inputs here specify the mean and variance of the prior, the number of
// iterations to perform, how many iterations to perform as a maximum, and how many
// steps to roll back on an approximation failure.
let est = RandomWalkPhaseEstimation(0.0, 1.0, 61, 100000, 1, oracle, [eigenstate]);
Reset(eigenstate);
return est;

}
Correção de erro
30/04/2021 • 6 minutes to read

Introdução
Na computação clássica, se alguém quiser proteger um bit contra erros, muitas vezes poderá representar esse
bit por um bit lógico repetindo o bit de dados. Por exemplo, deixe que $\overline{0} = 000$ seja a codificação do
bit de dados 0, em que usamos uma linha acima do rótulo 0 para indicar que é uma codificação de um bit no
estado 0. Se, de forma semelhante, permitimos $\overline{1} = 111$, temos um código de repetição simples
que protege contra um erro de inversão de um bit. Ou seja, se qualquer um dos três bits for invertido, podemos
recuperar o estado do bit lógico assumindo um voto principal. Embora a correção de erro clássica seja um
assunto muito mais rico que esse exemplo específico (recomendamos a introdução à teoria da codificação de
Lint), o código de repetição acima já aponta para um possível problema na proteção de informações do
quantum. Ou seja, o teorema da não clonagem implica que, se medirmos cada qubit individual e pegarmos um
voto de maioria por analogia com o código clássico acima, perderemos exatamente as informações que
estamos tentando proteger.
Na configuração quantum, veremos que a medida é problemática. Ainda podemos implementar a codificação
acima. É útil fazer isso para ver como é possível generalizar a correção de erro para o caso do quantum.
Portanto, permita $\ket{\overline{0}} = \ket{000} = \ket{0} \otimes \ket{0} \otimes \ket{0}$ e permita
$\ket{\overline{1}} = \ket{111}$. Em seguida, por linearidade, definimos nosso código de repetição para todas as
entradas; por exemplo, $\ket{\overline{+}} = (\ket{\overline{0}} + \ket{\overline{1}}) / \sqrt{2} = (\ket{000} +
\ket{111}) / \sqrt{2}$. Em particular, deixando um erro de inversão $X_1$ agir no qubit do meio, vemos que a
correção necessária em ambas as ramificações é precisamente $X_1$: $$ \begin{align} X_1 \ket{\overline{+}} &
= \frac{1}{\sqrt{2}} \left( X_1 \ket{000} + X_1 \ket{111} \right) \\ & = \frac{1}{\sqrt{2}} \left( \ket{010} +
\ket{101} \right). \end{align} $$
Para ver como podemos identificar que esse é o caso sem medir o estado que estamos tentando proteger, é útil
anotar o que significa cada erro de inversão de bit diferente para nossos estados lógicos:

ERRO $E$ $E\ K ET { \ O VERL IN E{ 0} } $ $E\ K ET { \ O VERL IN E{ 1} } $

$\boldone$ $\ket{000}$ $\ket{111}$

$X_0$ $\ket{100}$ $\ket{011}$

$X_1$ $\ket{010}$ $\ket{101}$

$X_2$ $\ket{001}$ $\ket{110}$

Para proteger o estado que estamos codificando, precisamos conseguir distinguir os três erros uns dos outros e
da identidade $\boldone$ sem distinguir entre $\ket{\overline{0}}$ e $\ket{\overline{1}}$. Por exemplo, se
medirmos $Z_0$, obteremos um resultado diferente para $\ket{\overline{0}}$ e $\ket{\overline{1}}$ no caso
sem erro, de maneira que ele recolhe para o estado codificado. Por outro lado, considere medir $Z_0 Z_1$, a
paridade dos dois primeiros bits em cada estado de base computacional. Lembre-se de que cada medida de um
operador Pauli verifica a qual autovalor o estado que está sendo medido corresponde, portanto, para cada
estado $\ket{\psi}$ na tabela acima, podemos calcular $Z_0 Z_1 \ket{\psi}$ para ver se obtemos
$\pm\ket{\psi}$. Observe que $Z_0 Z_1 \ket{000} = \ket{000}$ e que $Z_0 Z_1 \ket{111} = \ket{111}$, de modo
que concluímos que essa medida faz o mesmo para ambos os estados codificados. Por outro lado, $Z_0 Z_1
\ket{100} = - \ket{100}$ e $Z_0 Z_1 \ket{011} = -\ket{011}$, portanto, o resultado da medição de $Z_0 Z_1$
revela informações úteis sobre o erro ocorrido.
Para enfatizar isso, repetimos a tabela acima, mas adicionamos os resultados da medição $Z_0 Z_1$ e $Z_1
Z_2$ em cada linha. Denotamos os resultados de cada medição pelo sinal do autovalor observado, $+$ ou $-$,
que corresponde aos valores Q# Result de Zero e One , respectivamente.

$E\ K ET { \ O VERL IN E{ 0} $E\ K ET { \ O VERL IN E{ 1} RESULTA DO DE $Z _0 RESULTA DO DE $Z _1


ERRO $E$ }$ }$ Z _1$ Z _2$

$\boldone$ $\ket{000}$ $\ket{111}$ $+$ $+$

$X_0$ $\ket{100}$ $\ket{011}$ $-$ $+$

$X_1$ $\ket{010}$ $\ket{101}$ $-$ $-$

$X_2$ $\ket{001}$ $\ket{110}$ $+$ $-$

Assim, os resultados das duas medições determinam exclusivamente qual erro de inversão de bit ocorreu, mas
sem revelar nenhuma informação sobre o estado que codificamos. Chamamos esses resultados de uma
síndrome e referimo-nos ao processo de mapeamento de uma síndrome ao erro que a causou como
recuperação. Em particular, enfatizamos que a recuperação é um procedimento de inferência clássico que usa
como entrada a síndrome que ocorreu e retorna uma receita para corrigir erros que possam ter ocorrido.

NOTE
O código de inversão de bits acima só pode corrigir em caso de erros de inversão de bits único; ou seja, uma operação X
agindo em um único qubit. X A aplicação de mais de um qubit mapeará $\ket{\overline{0}}$ para $\ket{\overline{1}}$
após a recuperação. Da mesma forma, a aplicação de uma operação de inversão de fase Z mapeará $\ket{\overline{1}}$
to $-\ket{\overline{1}}$ e, portanto, mapeará $\ket{\overline{+}}$ para $\ket{\overline{-}}$. Em geral, os códigos podem
ser criados para lidar com um número maior de erros e para manipular $Z$ erros, bem como $X$ erros.

A percepção de que podemos descrever as medidas na correção de erro de quantum que atuam da mesma
forma em todos os estados de código é a essência do formalismo estabilizador. O cânone Q# apresenta uma
estrutura para descrever a codificação e a decodificação de códigos de estabilizador e para descrever como é
possível se recuperar de erros. Nesta seção, descrevemos essa estrutura e sua aplicação com alguns códigos de
correção de erro de quantum simples.

TIP
Uma introdução completa ao formalismo estabilizador está além do escopo desta seção. Indicamos Gottesman 2009 aos
leitores interessados em aprender mais.

Representando códigos de correção de erro no Q#


Para ajudar a especificar códigos de correção de erro, o cânone Q# fornece vários tipos distintos definidos pelo
usuário:
LogicalRegister user defined type = Qubit[] : indica que um registro de qubits deve ser interpretado como o
bloco de código de um código de correção de erro.
Syndrome user defined type = Result[] : indica que uma matriz de resultados de medição deve ser
interpretada como a síndrome medida em um bloco de código.
RecoveryFn user defined type = (Syndrome -> Pauli[]) : indica que uma função clássica deve ser usada para
interpretar uma síndrome e retornar uma correção que deve ser aplicada.
EncodeOp user defined type = ((Qubit[], Qubit[]) => LogicalRegister) : indica que uma operação usa
qubits que representam dados junto com novos qubits auxiliares para produzir um bloco de código de um
código de correção de erro.
DecodeOp user defined type = (LogicalRegister => (Qubit[], Qubit[])) : indica que uma operação
decompõe um bloco de código de um erro corrigindo o código nos qubits de dados e nos qubits auxiliares
usados para representar informações sobre a síndrome.
SyndromeMeasOp user defined type = (LogicalRegister => Syndrome) : indica uma operação que deve ser
usada para extrair informações de síndrome de um bloco de código, sem perturbar o estado protegido pelo
código.
Por fim, o cânone fornece o tipo QECC user defined type para coletar os outros tipos necessários para definir um
código de correção de erro de quantum. Associado a cada código quantum do estabilizador está o tamanho do
código n$, o número $k$ de qubits lógicos e a distância mínima $d$, geralmente agrupados em conjunto na
notação $n$, $k$, $d$ . Por exemplo, a função BitFlipCode function define o código de inversão de bits 3, 1,
1 :

let encodeOp = EncodeOp(BitFlipEncoder);


let decodeOp = DecodeOp(BitFlipDecoder);
let syndMeasOp = SyndromeMeasOp(MeasureStabilizerGenerators([
[PauliZ, PauliZ, PauliI],
[PauliI, PauliZ, PauliZ]
], _, MeasureWithScratch));
let code = QECC(encodeOp, decodeOp, syndMeasOp);

Observe que o tipo QECC não inclui uma função de recuperação. Isso permite mudar a função de recuperação
que é usada na correção de erros sem alterar a definição do próprio código. Essa capacidade é particularmente
útil ao incorporar comentários de medidas de caracterização ao modelo determinado pela recuperação.
Depois que um código é definido dessa forma, podemos usar a operação Recover operation para a recuperação
de erros:

let code = BitFlipCode();


let fn = BitFlipRecoveryFn();
let X0 = ApplyPauli([PauliX, PauliI, PauliI], _);
use scratch = Qubit[nScratch];
let logicalRegister = encode(data, scratch);
// Cause an error.
X0(logicalRegister);
Recover(code, fn, logicalRegister);
let (decodedData, decodedScratch) = decode(logicalRegister);
ApplyToEach(Reset, decodedScratch);

Exploraremos isso mais detalhadamente no exemplo de código de inversão de bits.


Além do código de inversão de bits, o cânone Q# é fornecido com implementações do código perfeito de cinco
qubitse o código de sete qubits, que pode corrigir um erro arbitrário de qubit único.
Aplicativos
11/05/2021 • 14 minutes to read

Simulação hamiltoniana
A simulação de sistemas quânticos é umas aplicações mais empolgantes da computação quântica. Em um
computador clássico, a dificuldade de simular a mecânica quântica, em geral, é escalada com a dimensão $N$
da representação de vetor de estado. Como essa representação aumenta exponencialmente com o número de
$n$ qubits $N=2^n$, uma característica também conhecida como o mal da dimensionalidade, a simulação
quântica no hardware clássico é intratável.
No entanto, a situação pode ser muito diferente no hardware quântico. A variação mais comum da simulação
quântica é chamada de problema de simulação hamiltoniana independente de tempo. Nela, é fornecida uma
descrição do sistema hamiltoniano $H$, que é uma matriz hermitiana, e algum estado quântico inicial
$\ket{\psi(0)}$ codificado de acordo com qubits $n$ em um computador quântico. Como estados quânticos em
sistemas fechados evoluem na equação Schrödinger $$ \begin{align} i\frac{d \ket{\psi(t)}}{d t} & = H
\ket{\psi(t)}, \end{align} $$ o objetivo é implementar o operador de evolução de tempo unitário $U(t)=e^{-iHt}$
em um tempo fixo $t$, em que $\ket{\psi(t)}=U(t)\ket{\psi(0)}$ resolve a equação Schrödinger. De forma
semelhante, o problema de simulação hamiltoniana dependente de tempo resolve a mesma equação, mas com
$H(t)$ agora como uma função de tempo.
A simulação hamiltoniana é um componente importante de muitos outros problemas de simulação quântica. As
soluções para o problema da simulação hamiltoniana são algoritmos que descrevem uma sequência de portões
quânticos primitivos para sintetizar um unitário $\tilde{U}$ aproximado com o erro $\|\tilde{U} - U(t)\| \le
\epsilon$ na norma espectral. A complexidade desses algoritmos dependerá muito de como uma descrição da
hamiltoniana de interesse for disponibilizada por um computador quântico. Por exemplo, na pior das hipóteses,
se $H$ agindo em qubits $n$ for fornecida como uma lista de números $2^n \times 2^n$, uma para cada
elemento de matriz, a simples leitura dos dados já exigiria tempo exponencial. Na melhor das hipóteses, é
possível assumir o acesso a um unitário de caixa preta que $O\ket{t}\ket{\psi(0)}=\ket{t}U(t)\ket{\psi(0)}$
resolve o problema de forma trivial. Nenhum desses modelos de entrada é particularmente interessante – o
primeiro não é melhor do que as abordagens clássicas e, no último, a caixa preta oculta a complexidade do
portão primitivo de sua implementação, que poderia ser exponencial no número de qubits.
Descrições de hamiltonianas
Portanto, são necessárias suposições adicionais do formato da entrada. Um equilíbrio preciso deve ser
alcançado entre os modelos de entrada que são suficientemente descritivos para abranger hamiltonianas
interessantes, como aqueles para sistemas físicos realísticos ou problemas computacionais interessantes, e
modelos de entrada que são suficientemente restritivos para serem implementados de forma eficiente em um
computador quântico. Vários modelos de entrada não triviais podem ser encontrados na literatura e variam
entre quânticos e clássicos.
Como exemplos de modelos de entrada quânticos, a simulação hamiltoniana baseada em amostra assume o
acesso de caixa preta a operações quânticas que produzem cópias de uma matriz de densidade $\rho$, que são
consideradas como a hamiltoniana $H$. No modelo de acesso unitário, podemos pressupor que a hamiltoniana,
em vez disso, é decomposta em uma soma de unidades $$ \begin{align} H & = \sum^{d-1}_{ j=0} a_j \hat{U}_j,
\end{align} $$ em que $a_j>0$ são coeficientes e $\hat{U}_j$ são unitários. Em seguida, supõe-se que exista o
acesso de caixa preta ao oracle unitário $V=\sum^{d-1}_{ j=0}\ket{ j}\bra{ j}\otimes \hat{U}_j$ que seleciona o
$\hat{U}_j$ desejado e o oracle $A\ket{0}=\sum^{d-1}_{ j=0}\sqrt{a_j/\sum^{d-1}_{k=0}\alpha_j}\ket{ j}$ que
cria uma codificação de estado quântico com esses coeficientes. No caso da simulação hamiltoniana esparsa,
assume-se que a hamiltoniana é uma matriz esparsa com apenas elementos diferentes de zero $d=\mathcal{O}
(\text{polylog}(N))$ em cada linha. Além disso, assume-se a existência de circuitos quânticos eficientes que
geram o local desses elementos diferentes de zero, bem como os respectivos valores. A complexidade dos
algoritmos de simulação hamiltoniana é avaliada em termos do número de consultas a essas caixas pretas e a
complexidade do portão primitivo depende muito da dificuldade de implementar essas caixas pretas.

NOTE
A notação big-O é usada geralmente para descrever o dimensionamento da complexidade dos algoritmos. Considerando
duas funções reais $f,g$, a expressão $g(x)=\mathcal{O}(f(x))$ significa que existe uma constante positiva absoluta $x_0,
c>0$ de modo que $g(x) \le c f(x)$ para todos os $x\ge x_0$.

Na maioria das aplicações práticas a serem implementadas em um computador quântico, essas caixas pretas
devem ser implementadas de forma eficiente, ou seja, com os portões quânticos primitivos $\mathcal{O}
(\text{polylog}(N))$. Ou seja, hamiltonianas simuláveis de forma eficiente devem ter uma descrição clássica
suficientemente esparsa. Em uma dessas formulações, supõe-se que a hamiltoniana é decomposta em uma
soma de partes hermitianas $$ \begin{align} H & = \sum^{d-1}_{ j=0} H_j. \end{align} $$ Além disso, presume-
se que cada parte, uma hamiltoniana $H_j$, seja fácil de simular. Isso significa que o unitário $e^{-iH_j t}$ para
qualquer tempo $t$ pode ser implementado exatamente usando os portões quânticos primitivos $\mathcal{O}
(1)$. Por exemplo, isso se aplica no caso especial em que cada $H_j$ são operadores Pauli locais, o que significa
que eles são de produtos tensores de operadores Pauli sem identidade $\mathcal{O}(1)$ que atuam em qubits
de fechamento espacial. Esse modelo é particularmente aplicável a sistemas físicos com interação vinculada e
local, uma vez que o número de termos é $d=\mathcal{O}(\text{polylog}(N))$ e pode ser escrito claramente, ou
seja, descrito de forma clássica, em tempo polinomial.

TIP
Hamiltonianas decompostas em uma soma de partes podem ser descritas usando a biblioteca de Representação do
Gerador Dinâmico. Para obter mais informações, confira a seção Representação do Gerador Dinâmico em estruturas de
dados.

Algoritmos de simulação
Um algoritmo de simulação quântica converte uma determinada descrição de uma hamiltoniana em uma
sequência de portões quânticos primitivos que, como um todo, aproxima a evolução de tempo pela
hamiltoniana.
No caso especial em que a hamiltoniana é decomposta em uma soma de partes hermitianas, a decomposição
de Trotter-Suzuki é um algoritmo especialmente simples e intuitivo para simular hamiltonianas que se
decompõem em uma soma de componentes hermitianos. Por exemplo, um integrador de primeiro pedido desta
família aproxima $$ \begin{align} U(t) & = \left( e^{-iH_0 t / r} e^{-iH_1 t / r} \cdots e^{-iH_{d-1} t / r} \right)^{r}
+ \mathcal{O}(d^2 \max_j\|H_j\|^2 t^2/r), \end{align} $$ usando um produto de termos $rd$.

TIP
As aplicações do algoritmo de simulação Trotter-Suzuki são abordados nos exemplos. Para o modelo Ising usando apenas
as operações intrínsecas fornecidas por cada computador de destino, confira o exemplo SimpleIsing . Para o modelo Ising
usando a estrutura de controle de biblioteca Trotter-Suzuki, confira o exemplo IsingTrotter . Para hidrogênio molecular
usando a estrutura de controle de biblioteca Trotter-Suzuki, consulte o exemplo simulação H2 .

Em muitos casos, gostaríamos de implementar o algoritmo de simulação, mas não estamos interessados nos
detalhes da implementação. Por exemplo, o integrador de segunda ordem aproxima $$ \begin{align} U(t) & =
\left( e^{-iH_0 t / 2r} e^{-iH_1 t / 2r} \cdots e^{-iH_{d-1} t / 2r} e^{-iH_{d-1} t / 2r} \cdots e^{-iH_1 t / 2r} e^{-
iH_0 t / 2r} \right)^{r} + \mathcal{O}(d^3 \max_j\|H_j\|^3 t^3/r^2), \end{align} usando um produto de termos
$2rd$. Ordens maiores envolverão ainda mais termos e variantes otimizadas podem exigir ordenações
altamente não triviais em exponenciais. Outros algoritmos avançados também podem envolver o uso de qubits
auxiliares em etapas intermediárias. Portanto, empacotamos algoritmos de simulação no cânone como o tipo
definido pelo usuário

newtype SimulationAlgorithm = ((Double, EvolutionGenerator, Qubit[]) => Unit is Adj + Ctl);

O primeiro parâmetro Double é a hora da simulação, o segundo parâmetro EvolutionGenerator , abordado na


seção Representação do Gerador Dinâmico de estruturas de dados, é uma descrição clássica de uma
hamiltoniana independente de tempo com instruções sobre como cada termo na hamiltoniana pode ser
simulado por um circuito quântico. Tipos dessa forma aproximam a operação unitária $e^{-iHt}$ no terceiro
parâmetro Qubit[] , que é o registro que armazena o estado quântico do sistema simulado. Da mesma forma
para o caso dependente de tempo, definimos um tipo definido pelo usuário com um tipo EvolutionSchedule ,
que é uma descrição clássica de uma hamiltoniana dependente de tempo.

newtype TimeDependentSimulationAlgorithm = ((Double, EvolutionSchedule, Qubit[]) => Unit : Adjoint,


Controlled);

Por exemplo, a decomposição de Trotter-Suzuki pode ser chamada usando as seguintes funções de cânone, com
parâmetros trotterStepSize que modificam a duração da simulação em cada exponencial e trotterOrder para
a ordem do integrador desejado.

function TrotterSimulationAlgorithm(
trotterStepSize : Double,
trotterOrder : Int)
: SimulationAlgorithm {
...
}

function TimeDependentTrotterSimulationAlgorithm(
trotterStepSize : Double,
trotterOrder : Int)
: TimeDependentSimulationAlgorithm {
...
}

TIP
As aplicações da biblioteca de simulação são abordadas nos exemplos. Para estimativa de fase no modelo Ising usando
SimulationAlgorithm , confira o exemplo IsingPhaseEstimation . Para a preparação do estado adiabático no modelo
Ising usando TimeDependentSimulationAlgorithm , confira o exemplo AdiabaticIsing .

Preparação do estado adiabático e estimativa de fase


Uma aplicação da simulação hamiltoniana é a preparação do estado adiabático. Aqui, são fornecidos duas
hamiltonianas $H_{\text{start}}$ e $H_{\text{end}}$ e um estado quântico $\ket{\psi(0)}$ que é um estado
terrestre da hamiltoniana inicial $H_{\text{start}}$. Normalmente, $H_{\text{start}}$ é escolhido de modo que
$\ket{\psi(0)}$ seja fácil de se preparar de um estado de base computacional $\ket{0\cdots 0}$. Ao fazer a
interpolação entre essas hamiltonianas no problema de simulação dependente do tempo de forma
suficientemente lenta, é possível terminar, com alta probabilidade, em um estado terrestre da hamiltoniana final
$H_{\text{end}}$. Embora a preparação de uma boa aproximação para os estados terrestres hamiltonianos
possa continuar dessa maneira chamando os algoritmos de simulação hamiltoniana dependente de tempo
como uma sub-rotina, outras abordagens conceitualmente diferentes, como o eigensolver quântico de variação,
são possíveis.
Outra aplicação onipresente na química quântica é estimar a energia de estado terrestre de hamiltonianas que
representa as etapas intermediárias da reação química. Esse esquema poderia, por exemplo, basear-se na
preparação do estado adiabático para criar o estado terrestre e incorporar a simulação hamiltoniana
independente de tempo como uma sub-rotina na caracterização da estimativa de fase, para extrair essa energia
com um erro finito e a probabilidade de sucesso.
Abstrair algoritmos de simulação como os tipos definidos pelo usuário SimulationAlgorithm e
TimeDependentSimulationAlgorithm nos permite incorporar convenientemente a funcionalidade em algoritmos
quânticos mais sofisticados. Isso nos motiva a fazer o mesmo para essas sub-rotinas usadas com frequência.
Assim, definimos a função conveniente

function InterpolatedEvolution(
interpolationTime : Double,
evolutionGeneratorStart : EvolutionGenerator,
evolutionGeneratorEnd : EvolutionGenerator,
timeDependentSimulationAlgorithm : TimeDependentSimulationAlgorithm)
: (Qubit[] => Unit is Adj + Ctl) {
...
}

Isso retorna uma operação unitária que implementa todas as etapas da preparação do estado adiabático. O
primeiro parâmetro interpolatedTime define o tempo ao longo do qual interpolamos linearmente entre a
hamiltoniana inicial descrita pelo segundo parâmetro evolutionGeneratorStart e a hamiltoniana final descrita
pelo terceiro parâmetro evolutionGeneratorEnd . O quarto parâmetro timeDependentSimulationAlgorithm é onde
se pode escolher o algoritmo de simulação. Observe que se interpolatedTime for longo o suficiente, um estado
terrestre inicial permanecerá um estado terrestre instantâneo da hamiltoniana durante toda a duração da
simulação dependente de tempo e, portanto, terminará no estado terrestre da hamiltoniana final.
Também definimos uma operação útil que executa automaticamente todas as etapas de um experimento típico
de química quântica. Por exemplo, temos o seguinte, que retorna uma estimativa de energia do estado
produzido pela preparação do estado adiabático:

operation EstimateAdiabaticStateEnergy(
nQubits : Int,
statePrepUnitary : (Qubit[] => Unit),
adiabaticUnitary : (Qubit[] => Unit),
qpeUnitary: (Qubit[] => Unit is Adj + Ctl),
phaseEstAlgorithm : ((DiscreteOracle, Qubit[]) => Double))
: Double {
...
}

nQubits é o número de qubits usado para codificar o estado quântico inicial. statePrepUnitary prepara o
estado inicial da base computacional $\ket{0\cdots 0}$. adiabaticUnitary é a operação unitária que implementa
a preparação do estado adiabático, como produzido pela função InterpolatedEvolution . qpeUnitary é a
operação unitária usada para executar a estimativa de fase no estado quântico resultante. phaseEstAlgorithm é
nossa escolha de algoritmo de estimativa de fase.

TIP
Os aplicativos da preparação do estado adiabático são abordados nos exemplos. Para o modelo Ising usando uma
implementação manual da preparação do estado adiabático em comparação com o uso da função AdiabaticEvolution ,
confira o exemplo AdiabaticIsing . Para estimativa de fase e preparação do estado adiabático no modelo Ising, confira o
exemplo IsingPhaseEstimation .
TIP
A simulação de hidrogênio molecular é uma amostra interessante e breve. O modelo e os resultados experimentais
relatados em O'Malley et. al. exigem somente matrizes Pauli e usam a forma $\hat H =
g_{0}I_0I_1+g_1{Z_0}+g_2{Z_1}+g_3{Z_0}{Z_1}+g_4{Y_0}{Y_1}+g_5{X_0}{X_1}$. Essa é uma hamiltoniana eficaz que só
exige 2 qubits, em que as constantes $g$ são computadas a partir da distância $R$ entre os dois átomos de hidrogênio.
Usando as funções do cânone, as Paulis são convertidas em unitários e evoluídas por curtos períodos de tempo usando a
decomposição de Trotter-Suzuki. Uma boa aproximação para o estado terrestre $H_2$ pode ser criada sem usar a
preparação do estado adiabático e, portanto, a energia do estado terrestre pode ser encontrada diretamente ao usar a
estimativa de fase do cânone.

Algoritmo de Shor
O algoritmo de Shor continua sendo um dos desenvolvimentos mais significativos na computação quântica,
pois ele mostrou que os computadores quânticos podem ser usados para resolver problemas importantes e
classicamente intratáveis atualmente. O algoritmo de Shor fornece uma maneira rápida de fatorar números
grandes usando um computador quântico, um problema chamado de fatoração. A segurança de muitos
criptossistemas de hoje é baseada na suposição de que não existe um algoritmo rápido para fatoração. Assim, o
algoritmo de Shor teve um grande impacto na forma como pensamos na segurança em um mundo pós-
quântica.
O algoritmo de Shor pode ser considerado como um algoritmo híbrido. O computador quântico é usado para
executar uma tarefa de computação complicada conhecida como localização de períodos. Os resultados da
localização de período são então processados de forma clássica para estimar os fatores. Examinaremos essas
duas etapas abaixo.
Localização de período
Depois de ver como a transformada de Fourier quântica e a estimativa de fase funcionam (confira Algoritmos
quânticos), podemos usar essas ferramentas para resolver um problema computacional tradicionalmente
complicado, chamado localização de períodos. Na próxima seção, veremos como aplicar a localização de
período à fatoração.
Considerando dois inteiros $a$ e $N$, em que $a<N$, a meta da localização de período, também chamada de
localização de ordem, é encontrar a ordem $r$ de $a$ módulo $N$, em que $r$ é definido para ser o menor
inteiro positivo, de forma que $a^r \equiv 1 \text{ mod } N$.
Para localizar a ordem usando um computador quântico, podemos usar o algoritmo de estimativa de fase
aplicado ao operador unitário $U_a$ a seguir: $$ U_a\ket{x} \equiv \ket{(ax)\text{ mod }N} .$$ Os autovetores de
$U_a$ são para o inteiro $0\leq s \leq r - 1$, $$\ket{x_s} \equiv 1 / \sqrt{r} \sum_{k=0}^{r-1} e^{\frac{-2\pi i sk}
{r}} \ket{a^k\text{ mod }N},$$ são autoestados de $U_a$. Os autovalores de $U_a$ são $$ U_a \ket{x_s} =
e^{2\pi i s / r} \ket{x_s} . $$
Portanto, a estimativa de fase gera os autovalores $e^{2\pi i s / r}$, dos quais $r$ pode ser obtido com
eficiência usando as frações contínuas de $s / r$.
O diagrama de circuito para localização de período quântico é:

Aqui, os qubits $2n$ são inicializados para $\ket{0}$ e qubits $n$ são inicializados para $\ket{1}$. O leitor pode
se perguntar novamente por que o registro quântico para manter os autoestados é inicializado para $\ket{1}$.
Como não sabemos a ordem $r$ antecipadamente, não podemos realmente preparar os estados $\ket{x_s}$
diretamente. Felizmente, acontece que $1/\sqrt{r} \sum_{s=0}^{r-1} \ket{x_s} = \ket{1}$. Não precisamos
realmente preparar $\ket{x}$! Podemos apenas preparar um registro quântico de qubits $n$ no estado
$\ket{1}$.
O circuito contém o QFT e vários portões controlados. O portão QFT foi descrito anteriormente. O portão $U_a$
controlado mapeia $\ket{x}$ para $\ket{(ax)\text{ mod } N}$ se o controle qubit for $\ket{1}$ e mapeia $\ket{x}$
para $\ket{x}$ caso contrário.
Para obter $(a^nx)\text{ mod } N$, podemos simplesmente aplicar $U_{a^n}$ controlado, em que calculamos
$a^n \text{ mod } N$ de forma clássica para se conectar ao circuito quântico.
Os circuitos para obter essa aritmética modular foram descritos na documentação de aritmética quântica.
Especificamente, exigimos um circuito de exponenciação modular para implementar as operações controladas
por $U_{a^i}$.
Enquanto o circuito acima corresponde à Estimativa de Fase Quântica e habilita explicitamente a localização da
ordem, podemos reduzir o número de qubits necessário. Podemos seguir o método de Beauregard para a
localização de ordem, conforme descrito na página 8 de arXiv:quant-ph/0205095v3 ou usar uma das rotinas de
estimativa de fase disponível em Microsoft.Quantum.Characterization. Por exemplo, a Estimativa de Fase
Robusta também usa um qubit extra.
Fatoração
A meta da fatoração é determinar os dois fatores primos do inteiro $N$, em que $N$ é um número de $n$-bit.
A fatoração consiste nas etapas descritas abaixo. As etapas são divididas em três partes: uma rotina de pré-
processamento clássica (1-4); uma rotina de computação quântica para localizar a ordem de $a \text{ mod } N$
(5); e uma rotina de pós-processamento clássica para derivar os fatores primos da ordem (6-9).
A rotina de pré-processamento clássica consiste nas seguintes etapas:
1. Se $N$ for par, retorna o fator primo $2$.
2. Se $N=p^q$ para $p\geq1$, $q\geq2$, retorna o fator primo $p$. Esta etapa é executada de maneira
clássica.
3. Escolha um número $a$ aleatório, de modo que $1 < a < N-1$.
4. Se $\text{gcd}(a,N)>1$, returna o fator primo $\text{gcd}(a,N)$. Esta etapa é calculada usando o algoritmo de
Euclid. Se nenhum fator primo tiver sido retornado, continuamos para a rotina quântica:
5. Chame o algoritmo quântico de localização de período para calcular a ordem $r$ de $a \text{ mod } N$. Use
$r$ na rotina de pós-processamento clássica para determinar os fatores primos:
6. Se $r$ for ímpar, volte para a etapa de pré-processamento (3).
7. Se $r$ for par e $a^{r/2} = -1\text{ mod }N$, volte para a etapa de pré-processamento (3).
8. Se $\text{gcd}(a^{r/2}+1, N)$ for um fator não trivial de $N$, retorna $\text{gcd}(a^{r/2}+1, N)$.
9. Se $\text{gcd}(a^{r/2}-1, N)$ for um fator não trivial de $N$, retorna $\text{gcd}(a^{r/2}-1, N)$.
O algoritmo de fatoração é probabilístico: pode ser mostrado que, com probabilidade, pelo menos uma metade
de $r$ será par e $a^{r/2} \neq -1 \text{ mod }N$, produzindo assim um fator primo. (Confira o artigo original
de Shor para obter detalhes ou um dos textos de Computação quântica básica em Para obter mais informações).
Se um fator primo não for retornado, basta repetir o algoritmo da etapa (1). Após $n$ tentativas, a
probabilidade de que cada tentativa apresente falha é no máximo $2^{-n}$. Assim, depois de repetir o
algoritmo algumas poucas vezes, o sucesso está praticamente garantido.
Licenças
13/05/2021 • 2 minutes to read

O Quantum Development Kit é fornecido com uma ampla coleção de funções e operações de software livre,
licenciadas sob a licença MIT. As partes da biblioteca padrão, que são portáveis em computadores de destino,
podem ser obtidas no repositório Microsoft/QuantumLibraries no GitHub, juntamente com outras bibliotecas,
como a biblioteca de química do quantum.
O Quantum Development Kit da Microsoft também fornece funções e operações de biblioteca especializadas,
licenciadas sob uma licença do Microsoft Research. Elas podem ser obtidas no repositório Microsoft/Quantum-
NC no GitHub.
Também exitem muitos exemplos que explicam e ilustram o uso de funções e operações da biblioteca padrão e
de outras bibliotecas. Esses exemplos são licenciados sob a licença do MIT. Elas podem ser obtidas no
repositório Microsoft/Quantum no GitHub.

Contribuição
Este projeto aceita contribuições e sugestões. A maioria das contribuições exige que você concorde com um CLA
(Contrato de Licença do Colaborador) declarando que você tem o direito de nos conceder, e de fato concede, os
direitos de usar sua contribuição. Para obter detalhes, acesse CLA.
Quando você envia uma solicitação de pull, um bot do CLA determina automaticamente se você precisa
fornecer um CLA e preencher a PR corretamente (por exemplo, rótulo ou comentário). Basta seguir as
instruções fornecidas pelo bot. Você só precisará fazer isso uma vez em todos os repositórios que usam nosso
CLA.
Este projeto adotou o Código de Conduta de Software Livre da Microsoft. Para obter mais informações, confira
as Perguntas frequentes sobre o código de conduta ou entre em contato com opencode@microsoft.com para
enviar outras perguntas ou comentários.
Como usar Q# Bibliotecas adicionais
30/04/2021 • 2 minutes to read

O QDK (Quantum Development Kit) fornece funcionalidade adicional específica de domínio por meio de pacotes
NuGet que podem ser adicionados aos seus Q# projetos.

Q # B IB L IOT EC A PA C OT E N UGET O B SERVA Ç Õ ES

Q#biblioteca padrão Microsoft.Quantum.Standard Incluído por padrão

Biblioteca de química do Quantum Microsoft.Quantum.Chemistr y

Biblioteca de numéricos do Quantum Microsoft.Quantum.Numerics

Biblioteca de aprendizado de máquina Microsoft.Quantum.MachineLearn


quântico ing

Depois de instalar o kit de desenvolvimento Quantum para uso com seu ambiente e idioma de host
preferenciais, você pode adicionar facilmente bibliotecas a Q# projetos individuais sem nenhuma outra
instalação.

NOTE
Algumas bibliotecas Q# podem funcionar bem com ferramentas adicionais que funcionam com seus Q# programas, ou
que se integram aos seus aplicativos host. Por exemplo, as instruções de instalação da biblioteca de química descrevem
como usar o pacote Microsoft.Quantum.Chemistr y com a plataforma química computacional NWChem e como
instalar as qdk-chem ferramentas de linha de comando para trabalhar com dados de química do Quantum.

Q# aplicativos ou interoperabilidade do .NET


IQ# Notebooks
Interoperabilidade do Python

Prompt de comando ou Visual Studio Code: ao usar o prompt de comando por conta própria ou de dentro
do Visual Studio Code, você pode usar o dotnet comando para adicionar uma referência de pacote NuGet ao
seu projeto. Por exemplo, para adicionar o pacote Microsoft. Quantum.Numerics , execute o seguinte
comando:

dotnet add package Microsoft.Quantum.Numerics

Visual Studio: se você estiver usando o Visual Studio 2019 ou posterior, poderá adicionar Q# pacotes
adicionais usando o Gerenciador de pacotes NuGet. Para carregar um pacote:
1. Com um projeto aberto no Visual Studio, selecione Gerenciar pacotes NuGet... no menu Projeto .
2. Clique em Procurar , selecione Incluir pré-lançamento e procure o nome do pacote "Microsoft.
Quantum.Numerics". Isso listará os pacotes disponíveis para download.
3. Passe o mouse sobre Microsoft. Quantum. Numerics e clique na seta apontando para baixo à direita
do número de versão para instalar o pacote numérico.
Para obter mais detalhes, consulte o Guia de interface do usuário do Gerenciador de pacotes.
Como alternativa, você pode usar o console do gerenciador de pacotes para adicionar a biblioteca de numéricos
ao seu projeto por meio da interface de linha de comando.

No Console do Gerenciador de Pacotes, execute o seguinte comando:

Install-Package Microsoft.Quantum.Numerics

Para obter mais detalhes, consulte o guia Console do Gerenciador de Pacotes.


Introdução à Biblioteca de Química Quântica
23/04/2021 • 2 minutes to read

A simulação de sistemas físicos desempenha uma função central na computação quântica há bastante tempo.
Isso acontece porque acredita-se amplamente que a dinâmica quântica seja extremamente complicada de ser
simulada em computadores clássicos, o que significa que a complexidade de simular o sistema é dimensionada
exponencialmente com o tamanho do sistema quântico em questão. A simulação de problemas de química e da
ciência de materiais continua sendo a aplicação mais estimulante da computação quântica e nos permitiria
investigar mecanismos de reação química que estariam, de outra forma, além de nossa capacidade de medir ou
simular. Ela também nos permitiria simular materiais eletrônicos correlacionados, como supercondutores de alta
temperatura. A lista de aplicações neste espaço é grande.
A finalidade desta documentação é fornecer uma introdução concisa à simulação de problemas de estrutura
eletrônica em computadores quânticos para ajudar o leitor a entender a função de que muitos aspectos da
biblioteca de simulação Hamiltoniana desempenham no espaço. Começamos com uma breve introdução à
mecânica quântica e, em seguida, analisamos como os sistemas eletrônicos são modelados com base nela. Em
seguida, discutiremos como essa dinâmica quântica pode ser emulada usando um computador quântico. Depois
disso, discutiremos dois métodos usados para compilar a dinâmica quântica em sequências de etapas:
decomposições de Trotter-Suzuki e aplicação de qubits.
Instalação da biblioteca química
18/05/2021 • 5 minutes to read

O exemplo MolecularHydrogen usa os dados de entrada molecular configurados manualmente. Embora isso
seja bom para pequenos exemplos, a química quântica em escala exige hamiltonianos com milhões ou bilhões
de termos. Esses hamiltonianos, gerados por pacotes de química computacional escalonáveis, são muito
grandes para serem importados manualmente.
A biblioteca química do Quantum para o kit de desenvolvimento do Quantum foi projetada para funcionar bem
com pacotes de química computacional, mais notavelmente a plataforma de química computacional NWChem
desenvolvida pela EMSL (Environmental Molecular Sciences Laboratory) no Pacific Northwest National
Laboratory. Em particular, o pacote Microsoft.Quantum.Chemistr y fornece ferramentas para carregar
instâncias de problemas de simulação de química quântica representados no esquema de Broombridge,
também com suporte para exportação por versões recentes do NWChem.
A biblioteca química do kit de desenvolvimento do Quantum também fornece uma ferramenta de linha de
comando, qdk-chem , para a conversão entre formatos herdados e Broombridge.
Esta seção fornece detalhes sobre como usar o kit de desenvolvimento do Quantum com NWChem e
Broombridge, ou formatos herdados e o qdk-chem .

Usar o kit de desenvolvimento do Quantum com o NWChem


Para começar a usar o NWChem junto com o kit de desenvolvimento do Quantum, use um dos seguintes
métodos:
Comece usando os arquivos de Broombridge existentes fornecidos com os exemplos em IntegralData/YAML.
Use o EMSL Arrows Builder para kit de desenvolvimento do Quantum, que é um front-end baseado na Web
para NWChem, para gerar novos arquivos de entrada moleculares com formato Broombridge.
Use a imagem do Docker fornecida pelo PNNL para executar o NWChem ou
Compile o NWChem para sua plataforma.
Consulte Ponta a ponta com NWChem para obter mais informações sobre como trabalhar com o NWChem
para modelos químicos para analisar com a biblioteca química do kit de desenvolvimento do Quantum.
Introdução ao uso de arquivos Broombridge fornecidos com os exemplos
A pasta IntegralData/YAML no repositório de exemplos do kit de desenvolvimento do Quantum contém
arquivos de dados moleculares com formato Broombridge.
Como exemplo simples, use o exemplo de biblioteca, GetGateCount para carregar o hamiltoniano de um dos
arquivos Broombridge e executar estimativas de portão de algorigthms de simulação quântica:

cd Quantum/Chemistry/GetGateCount
dotnet run -- --path=../IntegralData/YAML/h2.yaml --format=YAML

Consulte Carregar um hamiltoniano do arquivo para obter mais informações sobre como inserir moléculas
representadas pelo esquema de Broombridge.
Confira Obter contagens de recursos para obter mais informações sobre a estimativa de recursos.
Introdução ao uso do EMSL Arrows Builder
O EMSL Arrows é uma ferramenta que usa bancos de dados computacionais químicos e do NWChem para
gerar dados moleculares. O EMSL Arrows Builder para kit de desenvolvimento do Quantum permite que você
insira seu modelo usando vários construtores de modelo molecular e gere o arquivo de configuração do
Broombridge a ser usado pelo kit de desenvolvimento do Quantum.
Na página do EMSL, clique na guia ['Instuctions'] e siga as instruções de ['Simple Examples'] para gerar arquivos
Broombridge. Em seguida, tente executar ['GetGateCount'] para ver as estimativas de recursos quânticos para
essas moléculas.
Instalar o NWChem a partir da origem
Instruções completas sobre como instalar o NWChem a partir da origem são fornecidas pelo PNNL.

TIP
Se você quiser usar o NWChem pelo Windows 10, o Subsistema do Windows para Linux é uma ótima opção. Depois de
ter instalado o Ubuntu 18.04 LTS para Windows, execute ubuntu18.04 do seu terminal favorito e siga as instruções
acima para instalar o NWChem a partir da origem.

Depois de compilar o NWChem a partir da origem, você pode executar o script yaml_driver fornecido com o
NWChem para produzir rapidamente instâncias de Broombridge pelos conjuntos de entrada do NWChem:

$NWCHEM_TOP/contrib/quasar/yaml_driver input.nw

Esse comando criará um novo arquivo input.yaml no formato Broombridge dentro do diretório atual.
Usar o NWChem com o Docker
As imagens predefinidas para usar o NWChem estão disponíveis em várias plataformas por meio do Docker
Hub. Para começar, siga as instruções de instalação do Docker para sua plataforma:
Instalar o Docker for Ubuntu
Instalar o Docker for macOS
Instalar o Docker for Windows 10

TIP
Se estiver usando Docker for Windows, você precisará compartilhar a unidade que contém seu diretório temporário
(normalmente, trata-se da unidade C:\ ) com o daemon do Docker. Confira a documentação do Docker para obter mais
detalhes.

Depois de instalar o Docker, você pode usar o módulo do PowerShell fornecido com os exemplos do kit de
desenvolvimento do Quantum para executar a imagem ou pode executar a imagem manualmente. Detalhamos
o uso do PowerShell aqui; se você quiser usar a imagem do Docker manualmente, consulte a documentação
fornecida com a imagem.
Executar o NWChem por meio do PowerShell Core
Para usar a imagem do Docker do NWChem com o kit de desenvolvimento do Quantum, fornecemos um
pequeno módulo do PowerShell que faz a movimentação de arquivos para dentro e fora do NWChem para
você. Se você ainda não tiver o PowerShell Core instalado, poderá baixá-lo em várias plataformas pelo
repositório do PowerShell no GitHub.
NOTE
O PowerShell Core também é usado para alguns exemplos para demonstrar a interoperabilidade com fluxos de trabalho
de ciência dos dados e análise de negócios.

Depois de instalar o PowerShell Core, importe InvokeNWChem.psm1 para tornar os comandos do NWChem
disponíveis na sessão atual:

cd Quantum/utilities/
Import-Module ./InvokeNWChem.psm1

Isso tornará o comando Convert-NWChemToBroombridge disponível em sua sessão atual do PowerShell. Para baixar
todas as imagens necessárias do Docker e usá-las para executar os conjuntos de entrada do NWChem e
capturar a saída para o Broombridge, execute o seguinte:

Convert-NWChemToBroombridge ./input.nw

Isso criará um arquivo Broombridge executando o NWChem em input.nw . Por padrão, a saída do Broombridge
terá o mesmo nome e caminho que o conjunto de entrada, com a extensão .nw substituída por .yaml . Isso
pode ser substituído com o uso do parâmetro -DestinationPath para Convert-NWChemToBroombridge . Mais
informações podem ser obtidas usando a funcionalidade interna de ajuda do PowerShell:

Convert-NWChemToBroombridge -?
Get-Help Convert-NWChemToBroombridge -Full

Usar o kit de desenvolvimento do Quantum com o qdk-chem


Para instalar qdk-chem , você pode usar o SDK do .NET Core na linha de comando:

dotnet tool install --global Microsoft.Quantum.Chemistry.Tools

Depois de instalar o qdk-chem , você pode usar a opção --help para obter mais detalhes sobre a funcionalidade
oferecida pela ferramenta qdk-chem .
Para converter de e para Broombridge, você pode usar o comando qdk-chem convert :

qdk-chem convert --from fcidump --to broombridge data.fcidump --out data.yml

O comando qdk-chem convert também pode aceitar dados da entrada padrão e pode gravar na saída padrão;
isso é especialmente útil em scripts e para a integração com ferramentas que exportam em formatos herdados.
Por exemplo, no Bash:

cat data.fcidump | qdk-convert --from fcidump --to broombridge - > data.yml


Dinâmica do Quantum
17/05/2021 • 4 minutes to read

A mecânica quantum é basicamente o estudo da dinâmica do quantum, que busca entender como um estado
inicial de quantum $ \ket{\psi (0)} $ varia ao longo do tempo (consulte os documentos conceituais de
computação quantum para obter mais informações sobre a notação de Dirac). Especificamente, considerando
essa condição inicial um estado de quantum, um tempo de evolução e uma especificação do sistema dinâmico
de quantum, um estado de quantum $ \ket{\psi (t)} $ é procurado. Antes de continuar a explicar a dinâmica do
quantum, é útil voltar um pouco e pensar na dinâmica clássica, pois ela fornece informações sobre como a
mecânica quantum não é tão diferente da dinâmica clássica.
Na dinâmica clássica, sabemos da segunda lei do Newton que a posição de uma partícula evolui de acordo com
$F(x, t)=ma =m\frac{\ dd ^ 2}{\dd t ^ 2}{x} (t)$, em que $F(x, t) $é a força, $m$ é a massa e $a$ é a aceleração.
Em seguida, dada uma posição inicial $x(0)$, tempo de evolução $t$ e descrição das forças que atuam na
partícula, podemos encontrar $x(t)$ resolvendo a equação diferencial fornecida pelas equações de Newton para
$x(t)$. Especificar as forças dessa maneira é um pouco problemático. Por isso, geralmente expressamos as
forças em termos da energia potencial do sistema, que nos dá $-\ partial_x V(x, t) = m\frac{\dd^2}{\dd t^2}{x}
(t)$. Portanto, para uma partícula, a dinâmica do sistema é especificada somente pela função de energia
potencial, pela massa de partículas e pelo tempo de evolução.
Uma linguagem mais ampla é geralmente introduzida para a dinâmica clássica que vai além de $F=ma $. Uma
formulação, que é particularmente útil na mecânica quantum, é a mecânica hamiltoniana. Na mecânica
hamiltoniana, a energia total de um sistema e as posições (generalizadas) e o momento de fornecem todas as
informações necessárias para descrever o movimento de um objeto clássico arbitrário. Especificamente, permita
que $f (x,p,t)$ seja alguma função das posições generalizadas $x$ e enquanto $p$ de um sistema e permita que
$H(x,p,t)$ seja a função hamiltoniana. Por exemplo, se levarmos $f(x,p,t)=x(t)$ e $H(x,p,t)=p^2(t)/2m - V(x,t)$,
recuperaremos o caso acima da dinâmica newtoniana. Em geral, nós temos então que \begin{align} \frac{d}{dt} f
&= \partial_t f- (\partial_x H\partial_p f + \partial_p H\partial_x f)\\ &\defeq \partial_t f + \{f,H\}. \end{align}
aqui $\{f, H\}$ é chamado de colchete de Poisson e aparece de forma onipresente na dinâmica clássica devido à
função central que ele desempenha na definição de dinâmica.
A dinâmica do quantum pode ser descrita usando exatamente o mesmo idioma. A hamiltoniana, ou o total de
energia, especifica completamente a dinãmica de qualquer sistema quantum fechado. No entanto, há algumas
diferenças significativas entre as duas teorias. Na mecânica clássica $x$ e $p$ são apenas números, enquanto
que na mecânica quantum eles não são. Na verdade, eles nem comutam: $xp \ne px$.
O conceito matemático certo para descrever esses objetos não comutáveis é um operador, que nos casos em
que $x$ e $p$ só pode pegar um conjunto discreto de valores coincide com o conceito de uma matriz. Portanto,
para simplificar, vamos pressupor que nosso sistema quantum é finito para que possa ser descrito usando-se
vetores e matrizes. Exigimos ainda que essas matrizes sejam hermitianas (o que significa que a transposição
conjugada da matriz é igual à matriz original). Isso garante que os autovalores das matrizes sejam de valor real;
uma condição que impõem para garantir que, quando medimos uma quantidade como posição, não vamos
fazer um número imaginário.
Assim como os análogos de posição e dinâmica na mecânica quantum precisam ser substituídos por
operadores, a função hamiltoniana precisa ser substituída da mesma forma por um operador. Por exemplo, para
uma partícula no espaço livre, temos esse $H(x,p) = p^2/2m$ enquanto na mecânica quantum o operador
hamiltoniano $\hat{H}$ é $\hat{H} = \hat{p}^2/2m$, em que $\hat{p}$ é o operador momentum. Dessa
perspectiva, ir da clássica para o dinâmica do quantum simplesmente envolve a substituição das variáveis
usadas na dinâmica comum com operadores. Depois de construirmos o operador hamiltoniano ao traduzir a
clássica hamiltoniana comum sobre a linguagem quantum, podemos expressar a dinâmica de uma quantidade
mecânica de quantum arbitrária (por exemplo, operador mecânico quantum) $\hat{f}(t)$ via
\begin{align}\frac{d}{dt}\hat{f} = \ partial_t \hat{f} + [\hat{f}, \hat{H}], \end{Align}, em que $[f, H] = fH-Hf$ é
conhecido como comutador. Essa expressão é exatamente como a expressão clássica fornecida acima com a
diferença de que o colchete de Poisson $\ {f, H\}$ está sendo substituído pelo comutador entre $f$ e $H$. Esse
processo de pegar uma clássica hamiltoniana e usá-la para encontrar um quantum hamiltoniano é conhecido
no jargão do quantum como quantificação canônica.
Em que operadores $f$ estamos mais interessados? A resposta para isso depende do problema que desejamos
resolver. Talvez a quantidade mais útil a ser encontrada seja o operador de estado quantum, que, como discutido
na documentação conceitual anterior, pode ser usado para extrair tudo o que gostaríamos de saber sobre a
dinâmica. Depois de fazer isso (e simplificar o resultado para o caso em que um tem um estado puro), a
equação Schrödinger para o estado de quantum é encontrada \begin{align} i\ partial_t\ket{\psi(t)} = \hat{H}
(t)\ket{\psi (t)}. \end{align}
Essa equação, embora talvez menos intuitiva do que aquela fornecida acima, produz, talvez, a expressão mais
simples para entender como simular a dinâmica de quantum em um computador quantum ou clássico. Isso
ocorre porque a solução para a equação diferencial pode ser expressa na seguinte forma (para o caso em que a
hamiltoniana é constante em $t$) \begin{align} \ket{\psi (t)} = e ^{-iHt} \ket{\psi (0)}. \end{align} aqui $e ^ {-iHt}
$ é uma matriz unitária. Isso significa que existe um circuito quantum que pode ser projetado para realizá-lo
porque os computadores quantum podem aproximar bastante qualquer matriz unitária. Esse ato de encontrar
um circuito quantum para implementar o operador de evolução de tempo quantum $e^{-iHt} $ é o que
costuma ser chamado de simulação de quantum ou em uma simulação de dinâmica de Quantum específica.
Modelos do Quantum para sistemas eletrônicos
18/05/2021 • 3 minutes to read

Para simular sistemas eletrônicos, precisamos primeiro começar especificando o hamiltoniano, que pode ser
encontrado pelo procedimento de quantificação canônica descrito acima. Especificamente, para $N_e$ elétrons
com momenta $p_i$ (em três dimensões) e massa $m_E$ e vetores de posição $x_i$ juntamente com núcleos
com cargas $Z_k e$ em posições $y_k$, o operador hamiltoniano lê \hat{H}= \sum_{i=1}^{N_e}
\frac{\hat{p}_i^2}{2m_e} + \frac{1}{2}\sum_{i\ne j} \frac{e^2}{|\hat{x}_i - \hat{x}_j|} -\sum_{i,k} \frac{Z_ke^2}
{|\hat{x}_i - {y}_k|}+\frac{1}{2} \sum_{k\ne k'} \frac{Z_kZ_{k'}e^2}{|y_k - y_k'|}. \label{eq:Ham} \end{equation} Os
operadores de momenta $\hat{p}_i^2$ podem ser exibidos em espaço real como operadores laplacianos, por
exemplo, $\hat{p}_i^2 = -\partial_{x_i}^2 - \partial_{y_i}^2 - \partial_{z_i}^2$. Aqui, fizemos a suposição
simplificadora de que os núcleos estão em repouso para a molécula. Isso é conhecido como aproximação de
Born-Oppenheimer e tende a ser válido para o espectro de baixa energia de $\hat{H}$, já que a massa de
$1/1836$ é a massa de um próton. Esse operador hamiltoniano pode ser facilmente encontrado escrevendo a
energia de um sistema de $N _e$ elétrons e aplicando o processo de compartimentalização canônica descrito
na Dinâmica do Quantum.
Para construir a representação de matriz de unitário para $e^{-i\hat{H}t}$, precisamos representar o operador
$\hat{H}$ como uma matriz. Para isso, precisamos escolher um sistema de coordenadas ou base na qual
representar o problema. Por exemplo, se $\psi_j$ são um conjunto de funções de base ortogonal para $N_e$
elétrons, podemos definir a matriz
\begin{equation} H_{i,j} = \int_{-\infty}^\infty\int_{-\infty}^\infty \psi^{*}_i(x_1) \hat{H} \psi_j(x_2) \dd^3x_1
\dd^3x_2.\label{eq:discreteHam} \end{equation}
Embora, em princípio, o operador $\hat{H}$ seja desassociado e não atue em um espaço dimensional finito, a
matriz com elementos $H_{i,j}$ o faz. Isso significa que os erros são incorridos por meio da escolha de um
conjunto de base muito pequeno; no entanto, escolher uma base grande pode tornar a simulação da dinâmica
química impraticável. Por esse motivo, escolher uma base que possa representar de forma concisa o problema é
vital para resolver o problema da estrutura eletrônica.
Há muitas bases apropriadas que podem ser usadas e a escolha de uma boa base para se adequar ao problema
é grande parte da arte da química quântica. Talvez os conjuntos de base mais simples sejam o STO (Orbitais do
tipo Slater), que são soluções (ortogonalizadas) para a equação de Schrödinger (por exemplo, autofunções de
$\hat{H}$) para átomos semelhantes ao hidrogênio. Outros conjuntos de base, como ondas planas ou orbitais
de espaço real, podem ser usados e, para obter mais detalhes, indicamos ao leitor curioso o texto padrão
'Molecular Electronic-Structure Theory' de Helgaker.
Embora os estados usados no modelo acima possam parecer arbitrários, as mecânica quântica retringe os
estados que podem ser encontrados na natureza. Em particular, todos os estados quânticos eletrônicos válidos
devem ser antissimétricos sob troca de marcadores de elétrons. Isso significa que, se $\psi(x_1,x_2)$ foi a função
de onda para o estado quântico conjunto de dois elétrons, devemos ter $$ \psi(x_1,x_2)= - \psi(x_2,x_1). $$ O
princípio de exclusão de Pauli que proíbe que dois elétrons estejam no mesmo estado quântico é,
fascinantemente, uma consequência direta dessa lei, pois pode ser deduzido pelo fato de que se trocarmos dois
elétrons localizados na mesma posição $\psi(x_1,x_1)\mapsto \psi(x_1,x_1) \ne -\psi(x_1,x_1)$ unless
$\psi(x_1,x_1)=0$. Portanto, os estados iniciais devem ser escolhidos para obedecer a essa propriedade de
antissimetria e, por sua vez, nunca ter dois elétrons no mesmo estado ao mesmo tempo. Isso é crucial para a
estrutura eletrônica porque proíbe que vários elétrons estejam no mesmo estado e, por sua vez, permite que os
computadores quânticos usem um único bit quântico para armazenar o número de elétrons em um
determinado estado quântico.
Embora a mecânica quântica possa ser simulada em um computador quântico por discretização desses estados,
a maior parte do trabalho no campo tem evitado essa abordagem porque ela requer muitos qubits para
armazenar os estados e precisa de um procedimento complicado de preparação de estado para preparar um
estado inicial antissimétrico. Felizmente, no entanto, esses problemas podem ser evitados exibindo o problema
de simulação em uma perspectiva diferente.
Segunda Quantização
11/05/2021 • 13 minutes to read

A segunda quantização analisa o problema da estrutura eletrônica por meio de uma lente diferente. Em vez de
atribuir cada um dos elétrons $N_e$ a um estado específico (ou orbital), a segunda quantização rastreia cada
orbital e armazena caso exista um elétron presente em cada uma delas e, ao mesmo tempo, garante
automaticamente as propriedades de simetria da função de onda correspondente. Isso é importante porque
permite que os modelos de química quântica sejam especificados sem a necessidade de se preocupar com a
não simetrização do estado de entrada (conforme necessário para férmions) e também porque a segunda
quantização permite que esses modelos sejam simulados usando pequenos computadores quânticos.
Como um exemplo da segunda quantização em ação, vamos supor que $\psi_0\cdots \psi_{N-1}$ seja um
conjunto ortonormal de orbitais espaciais. Esses orbitais são escolhidos para representar o sistema com a maior
precisão possível dentro do conjunto de base finito considerado. Um exemplo comum de tais orbitais são os
orbitais atômicos, que formam uma autobase para o átomo de hidrogênio. Como elétrons têm dois estados de
rotação, dois elétrons podem ser comprimidos em cada orbital espacial. Isso significa que os estados de base
válidos estão no formato $\psi_{0,\uparrow},\ldots,\psi_{N-1,\uparrow}, \psi_{0,\downarrow},\ldots,\psi_{N-
1,\downarrow}$ em que $\uparrow$ e $\downarrow$ são rótulos que especificam os dois autoestados do grau
de liberdade de rotação. Esse índice combinado de $( j,\sigma)$ para $\sigma \in {\uparrow,\downarrow}$ é
chamado de orbital de rotação porque armazena tanto a espacial quanto o grau de liberdade de rotação. Na
biblioteca de química, os orbitais de rotação são armazenados em uma estrutura de dados SpinOrbital e são
criados da maneira a seguir.

// First, we load the namespace containing spin-orbital objects.


using Microsoft.Quantum.Chemistry.OrbitalIntegrals;

// First, we assign an orbital index, say `5`. Note that we use 0-indexing,
// so this is the 6th orbital.
var orbitalIdx = 5;

// Second, we assign a spin index, say `Spin.u` for spin up or `Spin.d` for spin down.
var spin = Spin.d;

// the spin-orbital (5, ↓) is then


var spinOrbital = new SpinOrbital(orbitalIdx, spin);

// A tuple `(int, Spin)` is also implicitly recognized as a spin-orbital.


(int, Spin) tuple = (orbitalIdx, spin);

// We explicitly specify the type of `spinOrbital1` to demonstrate


// the implicit cast to `SpinOrbital`.
SpinOrbital spinOrbital1 = tuple;

Isso significa que podemos imaginar a base para a parte de rotação e espacial da função de onda como
$\psi_{0} \cdots \psi_{2N-1}$, onde cada um dos índices agora é uma enumeração de $( j,\sigma)$. Uma
enumeração possível é $g( j,\sigma) = j+N\sigma'$. Outra enumeração possível é $h( j,\sigma) = 2*j + \sigma$.
A biblioteca de química quântica pode usar essas convenções e os orbitais de rotação nessa codificação podem
ser instanciados da maneira a seguir.
// Let us use the spin orbital created in the previous snippet.
var spinOrbital = new SpinOrbital(5, Spin.d);

// Let us set the total number of orbitals to be say, `7`.


var nOrbitals = 7;

// This converts a spin-orbital index to a unique integer, in this case `12`,


// using the formula `g(j,σ)`.
var integerIndexHalfUp = spinOrbital.ToInt(IndexConvention.HalfUp);

// This converts a spin-orbital index to a unique integer, in this case `11`,


// using the formula `h(j,σ)`.
var integerIndexUpDown = spinOrbital.ToInt(IndexConvention.UpDown);

// The default conversion uses the formula `h(j,σ)`, in this case `11`.
var integerIndexDefault = spinOrbital.ToInt();

Para sistemas fermiônicos, o princípio de exclusão de Pauli impede que mais de um elétron esteja presente em
um orbital de rotação ao mesmo tempo. Isso significa que podemos escrever os dois estados legais para
$\psi_1$ como \begin{equation} \psi_1 \rightarrow \begin{cases} \ket{0}_1 & \text{if $\psi_1$ is not occupied,} \
\ket{1}_1 & \text{if $\psi_1$ is occupied.} \end{cases} \end{Equation} Essa codificação é excelente para
computadores quânticos porque significa que podemos armazenar a ocupação eletrônica como um bit quântico
único.
Os estados de ocupação para os orbitais de rotação $2N$ podem ser armazenados de forma semelhante em
qubits $2N$. Por exemplo, se $N=2$, então o estado $$ \ket{0} \ket{1} \ket{1} \ket{0}, $$
corresponderá aos orbitais de rotação $1$ e $2$ sendo ocupados com o restante vazio. Da mesma forma, o
estado $$ \ket{0} \equiv \ket{0} {0} \cdots \ket{0} {N-1}, $$
não tem elétrons e é conhecido como 'estado de vácuo'.
Um excelente efeito colateral da segunda quantização é que não precisamos mais rastrear explicitamente a
antissimetria do estado quântico. Isso ocorre porque, como veremos, a antissimetria do estado representa a si
mesma por meio das regras de anticomutação dos operadores que criam e destroem as ocupações eletrônicas
de um orbital de rotação.

Operadores fermiônicos
Os dois operadores fundamentais que atuam nos vetores de base de segunda quantização são conhecidos
como operadores de criação e destruição. Esses operadores inserem ou destroem elétrons em um local
específico. Eles são indicados como $a^\dagger_j$ e $a_j$, respectivamente.
Por exemplo, \begin{align} a^\dagger_1 \ket{0}_1 = \ket{1}_1,\quad a^\dagger_1 \ket{1}_1 = 0,\quad a_1
\ket{0}_1 = 0,\quad a_1 \ket{1}_1 = \ket{0}_1. \end{align} Observe que aqui $a^\dagger_1 \ket{1}_1=0$ e $a_1
\ket{0}_1$ produzem o vetor zero, não $\ket{0}_1$. Esses operadores, portanto, não são hermitianos nem
unitários. Representamos operadores de criação e destruição gerais usando o tipo LadderOperator<TIndex>.
Por exemplo, um operador de criação único é representado da seguinte maneira.
// We load the namespace containing ladder operator objects.
using Microsoft.Quantum.Chemistry.LadderOperators;

// Let us use the spin orbital created in the previous snippet.


var spinOrbitalInteger = new SpinOrbital(5, Spin.d).ToInt();

// We specify either a creation or annihilation operator using


// the enumerable type `RaisingLowering.u` or `RaisingLowering.d`
// respectively;
var creationEnum = RaisingLowering.u;

// The type representing a creation operator is then initialized


// as follows. Here, we index these operators with integers.
// Hence we initialize the generic ladder operator with an
// integer index type.
var ladderOperator0 = new LadderOperator<int>(creationEnum, spinOrbitalInteger);

// An alternate constructor for a LadderOperator instead uses


// a tuple.
var ladderOperator1 = new LadderOperator<int>((creationEnum, spinOrbitalInteger));

Usando esses operadores, também podemos expressar $$ \ket{0} \ket{1} \ket{1} \ket{0} = a^\dagger_1
a^\dagger_2 \ket{0}^{\otimes 4}. $$ Essa sequência de operadores seria construída na biblioteca de simulação
hamiltoniana usando um código C# semelhante ao caso de orbital de rotação única considerado acima:

// We load the namespace containing fermion-related objects.


using Microsoft.Quantum.Chemistry.Fermion;

// Let us initialize an array of tuples representing the


// desired sequence of creation operators.
var indices = new[] { (RaisingLowering.u, 1), (RaisingLowering.u, 2) };

// We can convert this array of tuples to a sequence of ladder


// operators using the `ToLadderSequence()` methods.
var ladderSequences = indices.ToLadderSequence();

// Sequences of ladder operators are quite general. For instance,


// they could be bosonic operators, instead of fermionic operators.
// We specialize them by constructing a `FermionTerm` representing
// a fermion creation operator on the index `2` followed by `1`.
var fermionTerm = new FermionTerm(ladderSequences);

Para um sistema de férmions $k$, na segunda quantização, a ação do operador de criação $a^\dagger_i$ é
fornecida por $$ a^\dagger_i \ket{n_1, n_2, \ldots, 0_i, \ldots, n_k } = (-1)^{S_i} \ket{n_1, n_2, \ldots, 1_i, \ldots,
n_k}, $$ e $$ a^\dagger_i \ket{n_1, n_2, \ldots, 1_i, \ldots, n_k } = 0, $$ em que $S_i = \sum_{ j<i} a^\dagger_j
a_j$ mede o número total de férmions que estão no estado de uma partícula única e que têm um índice $j < i$.
Um terceiro operador também é geralmente usado em representações de segunda quantização. Esse operador
é conhecido como o operador de número e é definido por \begin{equation} n_i = a^\dagger_i a_i.
\end{Equation} Esse operador conta a ocupação de um determinado orbital de rotação, o que significa que
\begin{align} n_i \ket{0}_i &= 0\nonumber\
n_i \ket{1}_i &= \ket{1}_i. \end{align} Semelhante aos exemplos FermionTerm acima, esse operador de número é
construído da seguinte maneira.
// Let us use a new method to compactly create a sequence of ladder
// operators. Note that we have omitted specifying whether the
// operators are raising or lowering. In this case, the first half
// will be raising operators, and the second half will be lowering
// operators.
var indices = new[] { 1, 1 }.ToLadderSequence();

// We now construct a `FermionTerm` representing an annihilation operator


// on the index 1 followed by the creation operator on the index 1.
var fermionTerm0 = new FermionTerm(indices);

Entretanto, há uma sutileza no uso de operadores de criação ou destruição em sistemas fermiônicos. É


necessário que todo estado quântico válido seja antissimétrico na troca de rótulos. Isso significa que $$
a^\dagger_2 a^\dagger_1 \ket{0} = -a^\dagger_1 a^\dagger_2 \ket{0}. $$ Esses operadores são considerados
como 'anticomutação' e, no geral, para qualquer $i, j$ temos que \begin{align} a^\dagger_i a^\dagger_j = -(1-
\delta_{i,j})a^\dagger_j a^\dagger_i,\quad a^\dagger_i a_j =\delta_{i,j} - a_j a^\dagger_i. \end{align} Assim, as
duas instâncias LadderSequence<TIndex> a seguir são consideradas não equivalentes

// Let us initialize an array of tuples representing the


// desired sequence of creation operators.
var indices = new[] { (RaisingLowering.u, 1), (RaisingLowering.u, 2) };

// We now construct a `LadderSequence` representing a creation operator


// on the index 1 followed by 2, then a term with the reverse ordering.
var laddderSeqeunce = indices.ToLadderSequence();
var laddderSeqeunceReversed = indices.Reverse().ToLadderSequence();

// The following Boolean is `false`.


var equal = laddderSeqeunce == laddderSeqeunceReversed;

O requisito de que cada um dos operadores de criação seja anticomutação significa que o uso de uma
representação de segunda quantização evita os desafios enfrentados pela antissimetria de Fermions. Em vez
disso, o desafio reaparecerá em nossa definição dos operadores de criação.
Usando as regras de anticomutação, algumas instâncias LadderSequence realmente correspondem à mesma
sequência de operadores fermiônicos, às vezes até no sinal de menos. Por exemplo, considere o hamiltoniano
$a_0^\dagger a_1^\dagger a_1 a_0 = - a_1^\dagger a_0^\dagger a_1 a_0$. Isso nos motiva a definir uma
ordenação canônica para cada FermionTerm . Todo FermionTerm é colocado automaticamente em ordem
canônica da seguinte maneira.

// We now construct two `FermionTerms` that are equivalent with respect to


// anti-commutation up to a sign change.
var fermionTerm0 = new FermionTerm(new[] { 0, 1, 1, 0 }.ToLadderSequence());
var fermionTerm1 = new FermionTerm(new[] { 1, 0, 1, 0 }.ToLadderSequence());

// The following Boolean is `true`.


var sequenceEqual = fermionTerm0 == fermionTerm1;

// The change in sign is not compared above, but is an internally tracked


// property of `FermionTerm`.
int sign0 = fermionTerm0.Coefficient;
var sign1 = fermionTerm1.Coefficient;

// The following Boolean is `false`.


var signEqual = sign0 == sign1;

Hamiltoniano Fermiônico de Segunda Quantização


Talvez não seja surpresa que o hamiltoniano em Modelos Quânticos para Sistemas Eletrônicos possa ser escrito
em termos de operadores de criação e destruição. Em particular, se $\psi_j$ corresponder aos orbitais de
rotação que formam a base, então
\begin{equation} \hat{H} = \sum_{pq} h_{pq}a^\dagger_p a_q + \frac{1}{2}\sum_{pqrs} h_{pqrs}a^\dagger_p
a^\dagger_q a_ra_s +h_{\textrm nuc},\label{eq:totalHam} \end{equation} em que $h_{\textrm nuc}$ é a energia
nuclear (que é uma constante na aproximação de Born-Oppenheimer) e
\begin{align} h_{pq} &= \int_{-\infty}^\infty \psi^*_p(x_1) \left(-\frac{\nabla^2}{2} +V(x_1)\right)
\psi_q(x_1)\mathrm{d}^3x_1, \end{align}
em que $V(x) $ é o potencial de campo médio e
\begin{align} h_{pqrs} &= \int_{-\infty}^\infty \int_{-\infty}^\infty\psi_p^*(x_1)\psi_q^*(x_2) \left(\frac{1}{|x_1-
x_2|} \right)\psi_r(x_2)\psi_s(x_1)\mathrm{d}^3x_1\mathrm{d}^3x_2.\label{eq:integrals} \end{align}
Os termos $h_{pq}$ são chamados de integrais de elétron único, pois todos esses termos envolvem apenas
elétrons únicos. Da mesma forma, $h_{pqrs}$ são os integrais de dois elétrons. Eles são chamados de integrais
porque a computação dos valores desses coeficientes requer um integral. Os termos de elétron único
descrevem a energia cinética do elétrons individuais e as interações com os campos elétricos dos núcleos. Por
outro lado, os integrais de dois elétrons descrevem as interações entre os elétrons.
Uma intuição do que esses termos significam pode ser obtida dos operadores de criação e destruição que
compõem cada um deles. Por exemplo, $h_{pq}a^\dagger_p a_q$ descreve os saltos de elétrons do orbital de
rotação $q$ para o orbital de rotação $p$. Da mesma forma, o termo $h_{pqrs} a^\dagger_p a^\dagger_q a_r
a_s$ (para $p,q,r,s$ distinto) descreve dois elétrons em orbitais de rotação $r$ e $s$ que se dispersam entre si e
acabam nos orbitais de rotação $p$ e $q$. Se $r=q$ e $p=s$, então $h_{prrp} a^\dagger_p a^\dagger_r a_r
a_p = h_{prrp} n_p n_r$ oferece a penalidade de energia associada aos dois elétrons próximos entre si, mas não
descreve um processo dinâmico.
Podemos representar esse hamiltoniano com a classe FermionHamiltonian , que é essencialmente uma lista que
contém todas as instâncias FermionTerm desejadas. Como hamiltonianos são hermitianos por definição,
indexamos os termos usando o tipo HermitianFermionTerm mais especializado que também usa a simetria
hermitiana ao verificar se os termos são equivalentes.
Vamos criar alguns exemplos ilustrativos. Considere o hamiltoniano $\hat{H} = a_0^\dagger a_1 +
a_1^\dagger a_0$.
// We create a `FermionHamiltonian` instance to store the fermion terms.
var hamiltonian = new FermionHamiltonian();

// We construct the terms to be added.


var fermionTerm0 = new FermionTerm(new[] { 1, 0 }.ToLadderSequence());
var fermionTerm1 = new FermionTerm(new[] { 0, 1 }.ToLadderSequence());

// These fermion terms are not equal. The following Boolean is `false`.
var sequenceEqual = fermionTerm0 == fermionTerm1;

// However, these terms are equal under Hermitian symmetry.


// We also take the opportunity to demonstrate equivalent constructors
// for hermitian fermion terms
var hermitianFermionTerm0 = new HermitianFermionTerm(fermionTerm0);
var hermitianFermionTerm1 = new HermitianFermionTerm(new[] { 0, 1 });

// These Hermitian fermion terms are equal. The following Boolean is `true`.
var hermitianSequenceEqual = hermitianFermionTerm0 == hermitianFermionTerm1;

// We add the terms to the Hamiltonian with the appropriate coefficient.


// Note that these terms are identical.
hamiltonian.Add(hermitianFermionTerm0, 1.0);
hamiltonian.Add(hermitianFermionTerm1, 1.0);

Podemos simplificar essa construção usando o fato de que operadores hamiltonianos são operadores
hermitianos. Ao adicionar termos ao hamiltoniano usando Add , todo termo não hermitiano, como
fermionTerm0 , será considerado emparelhado com o conjugado hermitiano. Portanto, o snippet a seguir
também representa o mesmo hamiltoniano:

// We create a `FermionHamiltonian` instance to store the fermion terms.


var hamiltonian = new FermionHamiltonian();

// We construct the term to be added -- note the doubled coefficient.


hamiltonian.Add(new HermitianFermionTerm(new[] { 1, 0 }), 2.0);

Usando as regras de anticomutação, algumas instâncias FermionTerm no hamiltoniano realmente correspondem


à mesma sequência de operadores fermiônicos, às vezes até no sinal de menos. Por exemplo, considere o
hamiltoniano $H=a_0^\dagger a_1^\dagger a_1 a_0 - a_1^\dagger a_0^\dagger a_1 a_0 =2a_0^\dagger
a_1^\dagger a_1 a_0$, que é uma soma dos termos construídos acima. Nem sempre estará claro para o usuário
que eles são termos equivalentes e podem ser adicionados ao hamiltoniano separadamente. Por outro lado,
você pode desejar modificar os termos já existentes no hamiltoniano. Nesses casos, podemos combinar os
termos equivalentes da seguinte maneira.

// We create a `FermionHamiltonian` instance to store the fermion terms.


var hamiltonian = new FermionHamiltonian();

// We now create two Hermitian fermion terms that are equivalent with respect to
// anti-commutation and Hermitian symmetry.
var terms = new[] {
(new[] { 0, 1, 1, 0 }, 1.0),
(new[] { 1, 0, 1, 0 }, 1.0) }
.Select(o => (new HermitianFermionTerm(o.Item1.ToLadderSequence()), o.Item2.ToDoubleCoeff()));

// Now add `terms` to the Hamiltonian.


hamiltonian.AddRange(terms);

// There is only one unique term. `nTerms == 1` is `true`.


var nTerms = hamiltonian.CountTerms();

Ao combinar coeficientes de termos equivalentes, reduzimos o número total de termos no hamiltoniano.


Posteriormente, isso reduzirá o número de portas quânticas necessárias para simular o hamiltoniano.
Representação interna
Um hamiltoniano fermiônico com interações de um e dois corpos é representado em uma notação de segunda
quantização como
$$ \begin{align} H=\sum_{pq}h_{pq}a^\dagger_{p}a_{q}+\frac{1}
{2}\sum_{pqrs}h_{pqrs}a^\dagger_{p}a^\dagger_{q}a_{r}a_{s}. \end{align} $$
Nessa notação, há no máximo $N^2+N^4$ coeficientes. No entanto, muitos desses coeficientes podem ser
coletados, pois correspondem ao mesmo operador. Por exemplo, no caso em que $p,q,r,s$ são índices distintos,
poderemos usar as regras de anticomutação para mostrar que: $$ a^\dagger_{p}a^\dagger_{q}a_{r}a_{s} = -
a^\dagger_{q}a^\dagger_{p}a_{r}a_{s} = -a^\dagger_{p}a^\dagger_{q}a_{s}a_{r} =
a^\dagger_{q}a^\dagger_{p}a_{s}a_{r}. $$
Além disso, como $H$ é hermitiano, todos os operadores fermiônicos não hermitianos, como
$h_{pqrs}a^\dagger_{p}a^\dagger_{q}a_{r}a_{s}$, tem um conjugado hermitiano que também é encontrado em
$H$. Para indexar exclusivamente grupos de termos caracterizados por essas simetrias, definimos uma
ordenação canônica nos índices $(i_1,\cdots,i_n,j_1,\cdots,j_m)$ de qualquer sequência de operadores
fermiônicos $n+m$ $a^\dagger_{i_1}\cdots a^\dagger_{i_n}a_{ j_1}\cdots a_{ j_m}$ da seguinte maneira:
Todos os operadores de criação $a^\dagger_{i_\cdot}$ são colocados antes de todos os operadores de
destruição $a_{ j_\cdot}$.
Todos os índices do operador de criação são classificados em ordem crescente: $i_1< i_2< \cdots < i_n$.
Todos os índices do operador de destruição são classificados em ordem decrescente: $j_1> j_2 \cdots > j_m$.
O índice mais à esquerda é menor ou igual ao índice mais à direita: $i_1\le j_m$.
Identificaremos esse conjunto de índices com ordenação canônica como $$ \begin{align}
(i_1,\cdots,i_n,j_1,\cdots,j_m) \in S_{n,m}. \end{align} $$
Com essa ordenação canônica, o hamiltoniano fermiônico pode ser expresso como $$ \begin{align}
H=\sum_{(p,q)\in S_{1,1}}h'_{pq}\frac{a^\dagger_{p}a_{q}+a^\dagger_{q}a_{p}}{2}+\sum_{(p,q,r,s)\in
S_{2,2}}h'_{pqrs}\frac{a^\dagger_{p}a^\dagger_{q}a_{r}a_{s}+a^\dagger_{s}a^\dagger_{r}a_{q}a_{p}}{2},
\end{align} $$ com integrais de um e dois elétrons $h'_{pq}$ e $h'_{pqrs}$, respectivamente, adaptados
adequadamente.
Simetrias de Integrais Moleculares
13/05/2021 • 3 minutes to read

A simetria inerente do hamiltoniano de Coulomb, que é o hamiltoniano fornecido nos Modelos Quantum para
Sistemas Eletrônicos, que descreve os elétrons interagindo eletricamente entre si e com os núcleos, leva a uma
série de simetrias que podem ser exploradas para comprimir os termos no hamiltoniano. Em geral, se não
forem feitas mais suposições sobre as funções base $\psi_j$, então temos apenas que \begin{equation}
h_{pqrs}= h_{qpsr},\tag{★}\label{eq:hpqrs} \end{equation}, que pode ser vista imediatamente a partir das
integrais de Modelos Quantum para Sistemas Eletrônicos, ao observar que seus valores permanecem idênticos
se $p,q$ e $r,s$ são trocados de anti-comutação.
Se presumirmos que os spins-órbitas são de valor real (como são para as bases de órbita gaussiana), então
temos mais que \begin{equation} h_{pqrs} = h_{qpsr} = h_{srqp} =
h_{rspq}=h_{rqps}=h_{psrq}=h_{spqr}=h_{qrsp}.\tag{★}\label{eq:hpqrsreal} \end{equation} Dadas tais
suposições, podemos usar as simetrias acima para reduzir os dados necessários para armazenar os elementos
da matriz do hamiltoniano por um fator de $8$. Embora isso torne a importação de dados de forma consistente
um pouco mais complicada. Felizmente, a biblioteca de simulação do hamiltoniano tem sub-rotinas que podem
ser usadas para importar arquivos inteiros de LIQUI$|\rangle$ ou diretamente do NWChem.
As integrais da órbita molecular como essas (por exemplo, os termos $h _{pq}$ e $h_{pqrs}$) são representados
usando o tipo OrbitalIntegral , que fornece várias funções úteis para expressar essa simetria.

// Load the namespace containing orbital integral objects.


using Microsoft.Quantum.Chemistry.OrbitalIntegrals;

// Create a `OrbitalIntegral` instance to store a one-electron molecular


// orbital integral data.
var oneElectronOrbitalIndices = new[] { 0, 1 };
var oneElectronCoefficient = 1.0;
var oneElectronIntegral = new OrbitalIntegral(oneElectronOrbitalIndices, oneElectronCoefficient);

// This enumerates all one-electron integrals with the same coefficient --


// an array of equivalent `OrbitalIntegral` instances is generated. In this
// case, there are two elements.
var oneElectronIntegrals = oneElectronIntegral.EnumerateOrbitalSymmetries();

// Create a `OrbitalIntegral` instance to store a two-electron molecular orbital integral data.


var twoElectronOrbitalIndices = new[] { 0, 1, 2, 3 };
var twoElectronCoefficient = 0.123;
var twoElectronIntegral = new OrbitalIntegral(twoElectronOrbitalIndices, twoElectronCoefficient);

// This enumerates all two-electron integrals with the same coefficient --


// an array of equivalent `OrbitalIntegral` instances is generated. In
// this case, there are 8 elements.
var twoElectronIntegrals = twoElectronIntegral.EnumerateOrbitalSymmetries();

Além de enumerar todas as integrais da órbita que são numericamente idênticas, uma lista de todos os índices
de spin-órbita contidos no hamiltoniano representado por um OrbitalIntegral pode ser gerada da seguinte
maneira.
// Create a `OrbitalIntegral` instance to store a two-electron molecular
// orbital integral data.
var twoElectronIntegral = new OrbitalIntegral(new[] { 0, 1, 2, 3 }, 0.123);

// This enumerates all spin-orbital indices of the `FermionTerm`s in the


// Hamiltonian represented by this integral -- this is an array of array
// of `SpinOrbital` instances.
var twoElectronSpinOrbitalIndices = twoElectronIntegral.EnumerateSpinOrbitals();

Construção de Hamiltonianos Fermiônicos a partir de Integrais


Moleculares
Em vez de construir um Hamiltoniano Fermiônico adicionando FermionTerm s, todos os termos correspondentes
a cada órbita integral podem ser adicionados automaticamente. Por exemplo, o código a seguir enumera
automaticamente todas as simetrias permutacionais e ordena os termos em ordem canônica:

// Load the namespace containing fermion objects. This


// example also uses LINQ queries.
using Microsoft.Quantum.Chemistry.Fermion;
using System.Linq;

// Create a `OrbitalIntegral` instance to store a two-electron molecular


// orbital integral data.
var orbitalIntegral = new OrbitalIntegral(new[] { 0, 1, 2, 3 }, 0.123);

// Create an `OrbitalIntegralHamiltonian` instance to store the orbital integral


// terms
var orbitalIntegralHamiltonian = new OrbitalIntegralHamiltonian();
orbitalIntegralHamiltonian.Add(orbitalIntegral);

// Convert the orbital integral representation to a fermion


// representation. This also requires choosing a convention for
// mapping spin orbital indices to integer indices.
var fermionHamiltonian = orbitalIntegralHamiltonian.ToFermionHamiltonian(IndexConvention.UpDown);

// Alternatively, one can add orbital integrals directly to a fermion Hamiltonian


// as follows. This automatically enumerates over all symmetries, and then
// orders the `HermitianFermionTerm` instances in canonical order. We will need to
// choose an indexing convention as well.
fermionHamiltonian.AddRange(orbitalIntegral
.ToHermitianFermionTerms(0, IndexConvention.UpDown)
.Select(o => (o.Item1, o.Item2.ToDoubleCoeff())));
Representação de Jordan-Wigner
13/05/2021 • 3 minutes to read

Embora os segundos hamiltonianos quantizados são convenientemente representados em termos de


$a^\dagger$ (criação) e $a$ (aniquilação), essas operações não são operações fundamentais em computadores
quantum. Como resultado, se quisermos implementá-los em um computador quantum, precisamos mapear os
operadores para matrizes unitárias que podem ser implementadas em um computador quantum. A
representação de Jordan–Wigner fornece um desses mapas. No entanto, existem outras representações, como a
Bravyi-Kitaev, que têm suas próprias vantagens e desvantagens relativas. A principal vantagem da
representação de Jordan-Wigner é sua simplicidade.
A representação de Jordan-Wigner é direta para derivar. Lembre-se de que um estado $\ket{0}_J$ implica que o
spin-órbita $j$ está vazio e $\ket{1}_J$ indica que está ocupado. Isso significa que o qubits pode naturalmente
armazenar a ocupação de um determinado spin-órbita. E temos que $a^\dagger_j\ket{0}_J = \ket{1}_J$ e
$a^\dagger_j\ket{1}_J = 0$. É fácil verificar que \begin{align} a^\dagger_j &= \begin{bmatrix}0 & 0 \\ 1 &0
\end{bmatrix}=\frac{X_j - iY_j}{2}, \nonumber\\ a_j &= \begin{bmatrix}0 & 1 \\ 0 &0 \end{bmatrix}=\frac{X_j +
iY_j}{2}, \end{align} onde $X_j$ e $Y_j$ são os operadores Pauli-$X$ e -$Y$ agindo no qubit $j$.

NOTE
No Q#, o estado $\ket{0}$ representa o autoestado + 1 do operador $Z$. Em algumas áreas da física, $\ket{0}$
representa o estado fundamental de baixa energia e, portanto, o autoestado -1 do operador $Z$. Sendo assim, algumas
fórmulas podem ser diferentes da literatura popular.

Na biblioteca de química usamos $\ket{0}$ para representar um spin-órbita não ocupado. Isso mostra que para
um único spin-órbita, é fácil representar os operadores de criação e aniquilação em termos de matrizes unitárias
que os computadores quantum entendem. Observe que embora $X$ e $Y$ sejam unitários, $a^\dagger$ e $a$
não são. Veremos mais adiante que isso não representa uma complicação para a simulação.
Um problema que permanece é que, embora a construção acima funcione para um único spin-órbita, ela falha
para sistemas com dois ou mais spins-órbitas. Como os férmions são antissimétricos, sabemos que
$a^\dagger_j a^\dagger_k = - a^\dagger_k a^\dagger_j$ para qualquer $j$ e $k$. No entanto, $$
\left(\frac{X_j - iY_j}{2}\right)\left(\frac{X_k - iY_k}{2}\right) = \left(\frac{X_k - iY_k}{2}\right) \left(\frac{X_j - iY_j}
{2}\right). $$ Em outras palavras, os dois operadores de criação não são anticomutadores conforme necessário.
Isso pode ser corrigido de forma direta, porém deselegante. A correção é observar que os operadores de Pauli
são naturalmente anticomutadores. Em especial, $XZ = -ZX$ e $YZ=-ZY$. Assim, intercalando operadores $Z$
na construção do operador, podemos emular a anticomutação correta. Abaixo, a construção completa:
\begin{align} a^\dagger_1 &= \left(\frac{X-iY}{2}\right)\otimes 1 \otimes 1 \otimes 1 \otimes \cdots \otimes
1,\\ a^\dagger_2 &= Z\otimes\left(\frac{X-iY}{2}\right)\otimes 1\otimes 1 \otimes \cdots \otimes 1,\\
a^\dagger_3 &= Z\otimes Z\otimes \left(\frac{X-iY}{2}\right)\otimes 1 \otimes \cdots \otimes 1,\\ &\vdots\\
a^\dagger_N &= Z\otimes Z\otimes Z\otimes Z \otimes \cdots \otimes Z\otimes \left(\frac{X-iY}{2}\right).
\label{eq:JW} \end{align}
Também é conveniente expressar os operadores numéricos, $n_j$, em termos de operadores Pauli. Felizmente,
as cadeias de caracteres dos operadores $Z$ (conhecidas como cadeias de caracteres Jordan-Wigner) se
cancelam depois que alguém faz essa substituição. Depois de fazer isso (lembrando que $X_jY_j=iZ_j$), temos
\begin{equation} n_j = a^\dagger_j a_j = \frac{(1-Z_j)}{2}. \end{equation}

Construção de hamiltonianos na representação de Jordan-Wigner


Depois que invocamos a representação de Jordan-Wigner, a tradução do hamiltoniano para uma soma de
operadores Pauli é direta. Basta substituir cada um dos operadores $a^\dagger$ e $a$ no hamiltoniano
Fermiônico pelas cadeias de caracteres dos operados Pauli fornecidos acima. Quando alguém executa essa
substituição, existem apenas cinco classes de termos no hamiltoniano. Essas cinco classes correspondem às
diferentes maneiras pelas quais podemos escolher o $p,q$ e o $p,q,r,s$ nos termos de um corpo e de dois
corpos no hamiltoniano. Essas cinco classes, para o caso em que $p>q>r>s$ e órbitas de valor real são
\begin{align} h_{pp}a_p^\dagger a_p &= \sum_p \frac{h_{pp}}{2}(1 - Z_p)\\ h_{pq}(a_p^\dagger a_q +
a^\dagger_q a_p) &= \frac{h_{pq}}{2}\left(\prod_{ j=q+1}^{p-1} Z_j \right)\left( X_pX_q + Y_pY_q\right)\\
h_{pqqp} n_p n_q &= \frac{h_{pqqp}}{4}\left(1-Z_p - Z_q +Z_pZ_q \right)\\ H_{pqqr} &= \frac{h_{pqqr}}
{2}\left(\prod_{ j=r+1}^{p-1} Z_j \right)\left( X_pX_r + Y_pY_r\right)\left(\frac{1-Z_q}{2}\right)\\ H_{pqrs} &=
\frac{h_{pqrs}}{8}\prod_{ j=s+1}^{r-1} Z_j\prod_{k=q+1}^{p-1} Z_k \Big(XXXX - XXYY+XYXY\nonumber\\
&\qquad\qquad\qquad\qquad\qquad+YXXY+YXYX-YYXX\nonumber\\
&\qquad\qquad\qquad\qquad\qquad+XYYX+YYYY\Big) \end{align}
Embora gerar esses hamiltonianos manualmente exija apenas a aplicação dessas regras de substituição, isso
seria inviável para grandes moléculas, que podem consistir em milhões de termos hamiltonianos. Como
alternativa, podemos construir automaticamente o JordanWignerEncoding dando uma representação
FermionHamiltonian do hamiltoniano.

// We load the namespace containing fermion and Pauli objects.


using Microsoft.Quantum.Chemistry.Fermion;
using Microsoft.Quantum.Chemistry.Pauli;

// We create an example `FermionHamiltonian` instance.


var fermionHamiltonian = new FermionHamiltonian();

// We convert this Fermion Hamiltonian to a Jordan-Wigner representation.


var jordanWignerEncoding = fermionHamiltonian.ToPauliHamiltonian(QubitEncoding.JordanWigner);

Depois que os hamiltonianos são construídos dessa forma, podemos usar um host de algoritmos de simulação
de quantum para compilar a dinâmica gerada pelo $e^{-iHt}$ em uma sequência de portas elementares (dentro
de uma tolerância de erros definidas pelo usuário). Discutimos os dois métodos mais populares para a
simulação de quantum, qubitização e fórmulas de Trotter-Suzuki, na documentação algorítmica. Fornecemos
implementações para ambos os métodos na biblioteca de simulação hamiltoniana.
Simular a dinâmica hamiltoniana
18/05/2021 • 9 minutes to read

Depois que o hamiltoniano tiver sido expresso como uma soma dos operadores elementares, a dinâmica
poderá então ser compilada em operações fundamentais de portão usando uma série de técnicas conhecidas.
Três abordagens eficientes incluem as fórmulas de Trotter-Suzuki, combinações lineares de unitários e
qubitização. Explicamos essas três abordagens abaixo e fornecemos exemplos Q# concretos de como
implementar esses métodos usando a biblioteca de simulação hamiltoniana.

Fórmulas de Trotter-Suzuki
A ideia por trás das fórmulas de Trotter-Suzuki é simples: expressar o hamiltoniano como uma soma de
hamiltonianos fáceis de simular e, em seguida, aproximar a evolução total como uma sequência dessas
evoluções mais simples. Em particular, permita que $H=\sum_{ j=1}^m H_j$ seja o hamiltoniano. Em seguida,
$$ e^{-i\sum_{ j=1}^m H_j t} =\prod_{ j=1}^m e^{-iH_j t} + O(m^2 t^2), $$, que significa que, se $t\ll 1$, o erro
nessa aproximação se tornará insignificante. Observe que se $e^{-i H t}$ fosse um exponencial comum, o erro
nessa aproximação não seria $O(m^2 t^2)$: ele seria zero. Esse erro ocorre porque $e^{-iHt}$ é um
exponencial de operador e, como resultado, há um erro incorrido ao usar essa fórmula devido ao fato de que os
termos $H_j$ não são deslocados (por exemplo, $H_j H_k \ne H_k H_j$ em geral).
Se $t$ for grande, as fórmulas de Trotter-Suzuki ainda poderão ser usadas para simular a dinâmica com
precisão, dividindo-o em uma sequência de etapas curtas. Deixe que $r$ seja o número de etapas executadas na
evolução do tempo, de modo que cada etapa seja executada por tempo $t/r$. Em seguida, temos o $$ e^{-
i\sum_{ j=1}^m H_j t} =\left(\prod_{ j=1}^m e^{-iH_j t/r}\right)^r + O(m^2 t^2/r), $$ que implica que se $r$
for dimensionado como $m^2 t^2/\epsilon$, o erro poderá ser feito no máximo $\epsilon$ para qualquer
$\epsilon>0$.
Aproximações mais precisas podem ser criadas com a construção de uma sequência de exponenciais de
operador, de modo que os termos de erro sejam cancelados. A fórmula mais simples, fórmula de Trotter-Suzuki
de segunda ordem, usa a forma $$ U_2(t) = \left(\prod_{ j=1}^{m} e^{-iH_j t/2r} \prod_{ j=m}^1 e^{-iH_j
t/2r}\right)^r = e^{-iHt} + O(m^3 t^3/r^2), $$ cujo erro pode ser inferior a $ \epsilon $ para qualquer $
\epsilon>$0, escolhendo $r$ para dimensionar como $m^{3/2}t^{3/2}/\sqrt{\epsilon}$.
As fórmulas de ordem mais alta, especificamente na ordem de ($2k$)° para $k>0$, podem ser construídas
recursivamente: $$ U_{2k}(t) = [U_{2k-2}(s_k~ t)]^2 U_{2k-2}([1-4s_k]t) [U_{2k-2}(s_k~ t)]^2 = e^{-iHt} + O((m
t)^{2k+1}/r^{2k}), $$ em que $s_k = (4-4^{1/(2k-1)})^{-1}$.
A mais simples é a seguinte fórmula ($k=$2) de quarta ordem, introduzida originalmente por Suzuki: $$ U_4(t)
= [U_2(s_2~ t)]^2 U_2([1-4s_2]t) [U_2(s_2~ t)]^2 = e^{-iHt} +O(m^5t^5/r^4), $$ em que $s_2 = (4-
4^{1/3})^{-1}$. Em geral, as fórmulas de ordem superior arbitrariamente podem ser construídas de forma
semelhante; no entanto, os custos incorridos com o uso de integradores mais complexos geralmente superam
os benefícios além da quarta ordem na maioria dos problemas práticos.
Para fazer com que as estratégias acima funcionem, precisamos ter um método para simular uma ampla classe
de $e^{-iH_j t}$. A família mais simples de hamiltonianos e, possivelmente, mais útil, que poderíamos usar aqui
são os operadores de Pauli. Os operadores de Pauli podem ser facilmente simulados porque podem ser
diagonalizados usando operações de Clifford (que são portões padrão na computação quântica). Além disso,
depois de terem sido diagonalizados, seus autovalores podem ser encontrados computando a paridade dos
qubits em que eles atuam.
Por exemplo, $$ e^{-iX\otimes X t}= (H\otimes H)e^{-iZ\otimes Z t}(H\otimes H), $$ em que $$ e^{-i Z \otimes
Z t} = \begin{bmatrix} e^{-it} & 0 & 0 & 0 \
0 & e^{i t} & 0 & 0 \
0 & 0 & e^{it} & 0 \
0 & 0 & 0 & e^{-it} \end{bmatrix}. $$ Aqui, $e^{-iHt} \ket{00} = e^{it} \ket{00}$ e $e^{-iHt} \ket{01} = e^{-it}
\ket{01}$, que pode ser visto diretamente como consequência do fato de que a paridade de $00$ é $0$
enquanto a paridade da cadeia de caracteres de bits $01$ é $1$.
Os exponenciais dos operadores de Pauli podem ser implementados diretamente no Q# usando a operação Exp
operation:

use qubits = Qubit[2];


let pauliString = [PauliX, PauliX];
let evolutionTime = 1.0;

// This applies ^{- ⊗ } to qubits 0 and 1.


Exp(pauliString, - evolutionTime, qubits);

Para hamiltonianos fermiônicos, a decomposição de Jordan-Wigner mapeia de forma conveniente o


hamiltoniano para uma soma de operadores de Pauli. Isso significa que a abordagem acima pode facilmente ser
adaptada para simular a química. Em vez de fazer um loop manual de todos os termos de Pauli na
representação de Jordan-Wigner, veja abaixo um exemplo simples de como seria a execução dessa simulação
dentro da química. Nosso ponto de partida é uma codificação Jordan-Wigner do hamiltoniano fermiônico,
expressa em código como uma instância da classe JordanWignerEncoding .

// This example uses the following namespaces:


// using Microsoft.Quantum.Chemistry.OrbitalIntegrals;
// using Microsoft.Quantum.Chemistry.Fermion;
// using Microsoft.Quantum.Chemistry.Pauli;
// using Microsoft.Quantum.Chemistry.QSharpFormat;

// We create an instance of the `FermionHamiltonian` objecclasst to store the terms.


var hamiltonian = new OrbitalIntegralHamiltonian(new[]
{
new OrbitalIntegral(new[] { 0, 1, 2, 3 }, 0.123),
new OrbitalIntegral(new[] { 0, 1 }, 0.456)
}).ToFermionHamiltonian(IndexConvention.UpDown);

// We convert this fermion Hamiltonian to a Jordan-Wigner representation.


var jordanWignerEncoding = hamiltonian.ToPauliHamiltonian(QubitEncoding.JordanWigner);

// We now convert this representation into a format consumable by Q#.


var qSharpData = jordanWignerEncoding.ToQSharpFormat();

Esse formato da representação de Jordan-Wigner que é consumível pelos algoritmos de simulação Q# é um


tipo definido pelo usuário JordanWignerEncodingData . No Q#, esse formato é passado para uma função de
conveniência TrotterStepOracle que retorna um operador aproximando a evolução do tempo usando o
integrador de Trotter-Suzuki, além de outros parâmetros necessários para sua execução.
// qSharpData passed from driver
let qSharpData = ...

// Choose the integrator step size


let stepSize = 1.0;

// Choose the order of the Trotter—Suzuki integrator.


let integratorOrder = 4;

// `oracle` is an operation that applies a single time-step of evolution for duration `stepSize`.
// `rescale` is just `1.0/stepSize` -- the number of steps required to simulate unit-time evolution.
// `nQubits` is the number of qubits that must be allocated to run the `oracle` operation.
let (nQubits, (rescale, oracle)) = TrotterStepOracle (qSharpData, stepSize, integratorOrder);

// Let us now apply a single time-step.


use qubits = Qubit[nQubits];
// Apply single step of time-evolution
oracle(qubits);
// Reset all qubits to the 0 state to be successfully released.
ResetAll(qubits);
}

De forma importante, essa implementação aplica algumas otimizações discutidas em Simulação de


hamiltonianos de estrutura eletrônica usando computadores quânticos e Aprimorando os algoritmos quânticos
para química quântica para minimizar o número de rotações de qubit único necessárias, bem como reduzir
erros de simulação.

Qubitização
A qubitização é uma abordagem alternativa para a simulação que usa ideias de passeios quânticos para simular
a dinâmica quântica. Embora a qubitização exija mais qubits do que as fórmulas de Trotter, o método promete o
dimensionamento ideal com o tempo de evolução e a tolerância a erros. Por esses motivos, ele se tornou um
método favorito para simular a dinâmica hamiltoniana em geral e para resolver o problema de estrutura
eletrônica em particular.
Em um alto nível, a qubitização realiza isso por meio das etapas a seguir. Primeiro, deixe $H=\sum_j h_j H_j$
para unitário e hermitiano $H_j$ e $h_j\ge 0$. Ao executar um par de reflexões, a qubitização implementa um
operador equivalente a $$W=e^{\pm i \cos^{-1}(H/|h|_1)},$$ em que $|h|_1 = \sum_j |h_j|$. A próxima etapa
envolve transformar os autovalores do operador de passeio de $e^{i\pm \cos^{-1}(E_k/|h|_1)}$, em que $E_k$
são os autovalores de $H$ to $e^{-iE_k t}$. Isso pode ser feito usando uma variedade de métodos de
transformação de valor singular quântico, incluindo processamento de sinal quântico.
Como alternativa, se apenas as quantidades estáticas forem desejadas (como a energia de estado fundamental
hamiltoniano), será suficiente aplicar a estimativa de fase diretamente a $W$ para estimar a energia de estado
fundamental do resultado, tirando o cosseno do resultado. Isso é significativo porque permite que a
transformação espectral seja executada de forma clássica em vez de usar um método de transformação de valor
singular quântico.
Em um nível mais detalhado, a implementação da qubitização requer duas sub-rotinas que fornecem as
interfaces para o hamiltoniano. Ao contrário dos métodos de Trotter-Suzuki, essas sub-rotinas são quânticas e
não clássicas e sua implementação precisará usar mais qubits de forma logarítmica do que seria necessário para
uma simulação baseada em Trotter.
A primeira sub-rotina quântica usada pela qubitização é chamada de $\operatorname{Select}$ e promete
produzir \begin{equation} \operatorname{Select} \ket{ j} \ket{\psi} = \ket{ j} em que cada $H_j$ é considerado
hermitiano e unitário. Embora isso pareça restritivo, lembre-se de que os operadores de Pauli são hermitianos e
unitários e, portanto, aplicativos como a simulação química quântica se enquadram naturalmente nessa
estrutura. A operação $\operatorname{Select}$, talvez surpreendentemente, na verdade é uma operação de
reflexão. Isso pode ser visto pelo fato de que $\operatorname{Select}^2\ket{ j} \ket{\psi} = \ket{ j} \ket{\psi}$
como cada $H_j$ é unitário e hermitiano e, portanto, tem autovalores $\pm $1.
A segunda sub-rotina é chamada $\operatorname{Prepare}$. Enquanto a operação de seleção fornece um meio
de acessar de forma coerente cada um dos termos hamiltonianos $H_j$, a sub-rotina de preparação fornece um
método para acessar os coeficientes $h_j$, \begin{equation} \operatorname{Prepare}\ket{0} = \sum_j
\sqrt{\frac{h_j}{|h|_1}}\ket{ j}. \end{Equation} Em seguida, usando um portão de fase controlada por
multiplicação, vemos que $$ \Lambda\ket{0}^{\otimes n} = \begin{cases} -\ket{x} & \text{if } x = 0 \
\ket{x} & \text{otherwise} \end{cases}. $$
A operação $\operatorname{Prepare}$ não é usada diretamente na qubitização, mas, em vez disso, é usada para
implementar uma reflexão sobre o estado em que $$ \begin{align} R & = 1 - 2\operatorname{Prepare}
\ket{0}\bra{0} \operatorname{Prepare}^{-1} \\ & = \operatorname{Prepare} \Lambda
\operatorname{Prepare}^{-1}. \end{align} $$
O operador de passeio, $W$, pode ser expresso em termos das operações $\operatorname{Select}$ e $R$
como $$ W = \operatorname{Select} R, $$, que pode ser visto novamente para implementar um operador
equivalente (até uma isometria) a $e^{\pm i \cos^{-1}(H/|h|_1)}$.
Essas sub-rotinas são fáceis de configurar no Q#. Como exemplo, considere o Ising hamiltoniano transversal de
qubit simples em que $H = X_1 + X_2 + Z_1 Z_2 $. Nesse caso, o código Q# que implementaria a operação
$\operatorname{Select}$ é invocado por MultiplexOperations operation, enquanto a operação
$\operatorname{Prepare}$ pode ser implementada usando PrepareArbitraryState operation. Um exemplo que
envolve a simulação do modelo Hubbard pode ser encontrado como um Q# exemplo.
A especificação manual dessas etapas para problemas de química arbitrários exigiria muito esforço, o que é
evitado com o uso da biblioteca de química. Da mesma forma que o algoritmo de simulação de Trotter-Suzuki
acima, o JordanWignerEncodingData é passado para a função de conveniência QubitizationOracle que retorna o
operador de passeio, além de outros parâmetros necessários para sua execução.

// qSharpData passed from driver


let qSharpData = ...

// `oracle` is an operation that applies a single time-step of evolution for duration `stepSize`.
// `rescale` is just `1.0/oneNorm`, where oneNorm is the sum of absolute values of all probabilities in
state prepared by `Prepare`.
// `nQubits` is the number of qubits that must be allocated to run the `oracle` operation.
let (nQubits, (rescale, oracle)) = QubitizationOracle (qSharpData, stepSize, integratorOrder);

// Let us now apply a single step of the quantum walk.


using qubits = Qubit[nQubits];
// Apply single step of quantum walk.
oracle(qubits);
// Reset all qubits to the 0 state to be successfully released.
ResetAll(qubits);

É importante que a implementação QubitizationOracle function seja aplicável a hamiltonianos arbitrários


especificadas como uma combinação linear de cadeias de caracteres de Pauli. Uma versão otimizada para
simulações de química é chamada usando OptimizedQubitizationOracle function. Esta versão é otimizada para
minimizar o número de portões T usando técnicas discutidas na Codificação espectros eletrônicos em circuitos
quânticos com complexidade de T linear.
Teoria Hartree-Fock
30/04/2021 • 3 minutes to read

Talvez a quantidade mais importante na simulação química do quantum seja o estado do solo, que é o autovalor
de energia mínimo da matriz hamiltoniana. Isso ocorre porque, para a maioria das moléculas em temperatura
ambiente, quantidades como taxas de reação são dominadas por diferenças de energia livre entre estados
quânticos que descrevem o início e o fim de uma etapa em um caminho de reação e em temperatura ambiente.
Esses estados intermediários são normalmente estados fundamentais. Embora o estado fundamental
normalmente seja muito difícil de aprender (mesmo com um computador quântico), porque é uma distribuição
em um número exponencialmente grande de configurações. Quantidades como energia de estado fundamental
podem ser aprendidas. Por exemplo, se $\ket{\psi}$ for qualquer estado quântico puro, então \bbegin{equation}
E = \bra{ \psi } \hat{H} \ket{\psi} \end{equation} proporcionará a energia média que o sistema tem nesse estado.
O estado fundamental é o estado que fornece o menor valor. Como resultado, escolher um estado que seja o
mais próximo possível do estado fundamental real é essencialmente importante para estimar a energia
diretamente (como é feito no eigensolvers de variação) ou por meio da estimativa de fase.
A teoria de Hartree-Fock oferece uma maneira simples de criar o estado inicial para sistemas quânticos. Ela
produz uma única aproximação de determinante de Slater para o estado fundamental de um sistema quântico.
Para esse fim, ela encontra uma rotação no espaço de Fock, que minimiza a energia do estado fundamental. Em
particular, para um sistema de N$ elétrons, o método executa a rotação \begin{equation} \prod_{ j=0}^{N-1}
a^\dagger_j \ket{0} \mapsto \prod_{ j=0}^{N-1} e^{u} a^\dagger_j e^{-u} \ket{0}\defeq\prod_{ j=0}^{N-1}
\widetilde{a}^\dagger_j \ket{0}, \end{equation} com uma matriz anti-hermitiana (por exemplo, $u= -
u^\dagger$) $u = \sum_{pq} u_{pq} a^\dagger_p a_q$. Deve-se observar que a matriz $u$ representa as
rotações orbitais, e $\widetilde{a}^\dagger_j$ e $\widetilde{a}_j$ representam os operadores de criação e
aniquilação para elétrons ocupando spin-orbitais moleculares de Hartree-Fock.
A matriz $u$ é então otimizada para minimizar a energia esperada $\bra{0} \prod_{ j=0}^{N-1} \widetilde{a}_j H
\prod_{k=0}^{N-1} \widetilde{a}^\dagger_k\ket{0}$. Embora esses problemas de otimização possam ser
genericamente difíceis, na prática, o algoritmo Hartree-Fock tende a convergir rapidamente para uma solução
quase ideal para o problema de otimização, principalmente para moléculas de shell fechado nas geometrias de
equilíbrio. Podemos especificar esses estados como uma instância do objeto FermionWavefunction . Por exemplo,
o estado $a^\dagger_{1}a^\dagger_{2}a^\dagger_{6}\ket{0}$ é instanciado na biblioteca química da seguinte
maneira.

// Create a list of integer indices of the creation operators


var indices = new[] { 1, 2, 6 };

// Convert the list of indices to a `FermionWavefunction` object.


// In this case, the indices are integers, so we use the `int`
// type specialization.
var wavefunction = new FermionWavefunction<int>(indices);

Também é possível indexar funções de onda com índices SpinOrbital e, em seguida, converter esses índices em
inteiros da seguinte maneira.
// Create a list of spin orbital indices of the creation operators
var indices = new[] { (0, Spin.d), (1,Spin.u), (3, Spin.u) };

// Convert the list of indices to a `FermionWavefunction` object.


var wavefunctionSpinOrbital = new FermionWavefunction<SpinOrbital>(indices.ToSpinOrbitals());

// Convert a wavefunction indexed by spin orbitals to


// one indexed by integers
var wavefunctionInt = wavefunctionSpinOrbital.ToIndexing(IndexConvention.UpDown);

O recurso mais surpreendente sobre a teoria Hartree-Fock é que ele produz um estado quântico que não tem
nenhum emaranhamento entre os elétrons. Isso significa que ele normalmente fornece uma descrição
qualitativa adequada das propriedades de sistemas molecular.
O estado de Hartree-Fock também pode ser reconstruído a partir de um FermionHamiltonian da seguinte
maneira.

// We initialize a fermion Hamiltonian.


var fermionHamiltonian = new FermionHamiltonian();

// Create a Hartree-Fock state from the Hamiltonian


// with, say, `4` occupied spin orbitals.
var wavefunction = fermionHamiltonian.CreateHartreeFockState(nElectrons: 4);

No entanto, obter resultados precisos, principalmente para sistemas altamente correlacionados, precisa de
estados quânticos que vão além da teoria Hartree-Fock.
Funções de onda correlacionadas
18/05/2021 • 5 minutes to read

Em muitos sistemas, especialmente aqueles próximos à geometria do equilíbrio, a teoria de Hartree-Fock


fornece uma descrição qualitativa das propriedades moleculares por meio de um estado de referência de um
único determinante. No entanto, para obter uma precisão quantitativa, também é necessário considerar os
efeitos de correlação.
Nesse contexto, é importante distinguir entre correlações dinâmicas e não dinâmicas. As correlações dinâmicas
surgem da tendência de os elétrons ficarem separados, como devido à repulsão intereletrônica. Esse efeito pode
ser modelado considerando as excitações dos elétrons fora do estado de referência. As correlações não
dinâmicas surgem quando a função de onda é dominada por duas ou mais configurações em ordem zero,
mesmo para obter apenas uma descrição qualitativa do sistema. Isso exige uma superposição dos
determinantes e é um exemplo de uma função de onda multirreferencial.
A biblioteca química fornece uma maneira de especificar uma função de onda de ordem zero para o problema
multirreferencial, como uma superposição de determinantes. Essa abordagem, que chamamos de funções de
onda multirreferencial esparsa, é eficaz quando apenas alguns componentes são suficientes para especificar a
superposição. A biblioteca também fornece um método para incluir correlações dinâmicas além de uma
referência de determinante único por meio do ansatz de cluster acoplado de unitário generalizado. Além disso,
também constrói circuitos quânticos que geram esses estados em um computador quântico. Esses estados
podem ser especificados no esquema de Broombridge e também fornecemos a funcionalidade de especificar
manualmente esses estados por meio da biblioteca química.

Função de onda multirreferencial esparsa


Um estado multirreferencial $\ket{\psi_{\rm {MCSCF}}}$ pode ser especificado explicitamente como uma
combinação linear de determinantes Slater de $N$-elétron. \begin{align} \ket{\psi_{\rm {MCSCF}}} \propto
\sum_{i_1 < i_2 < \cdots < i_N} \lambda_{i_1,i_2,\cdots,i_N} a^\dagger_{i_1}a^\dagger_{i_2}\cdots
a^\dagger_{i_N}\ket{0}. \end{Align} Por exemplo, o estado $\propto(0.1 a^\dagger_1a^\dagger_2a^\dagger_6
- 0.2 a^\dagger_2a^\dagger_1a^\dagger_5)\ket{0}$ pode ser especificado na biblioteca química da seguinte
maneira.

// Create a list of tuples where the first item of each


// tuple are indices to the creation operators acting on the
// vacuum state, and the second item is the coefficient
// of that basis state.
var superposition = new[] {
(new[] {1, 2, 6}, 0.1),
(new[] {2, 1, 5}, -0.2) };

// Create a fermion wavefunction object that represents the superposition.


var wavefunction = new FermionWavefunction<int>(superposition);

Essa representação explícita dos componentes de superposição é eficaz quando apenas alguns componentes
precisam ser especificados. É necessário evitar o uso dessa representação quando muitos componentes são
necessários para capturar com precisão o estado desejado. A razão para isso é o custo do portão do circuito
quântico que prepara esse estado em um computador quântico, que pode ser dimensionado pelo menos
linearmente com o número de componentes de superposição e, no máximo, de forma quadrática com a norma
única das amplitudes de superposição.
Função de onda de cluster acoplado de unitário
Também é possível especificar uma função de onda de cluster acoplado de unitário $\ket{\psi_{\rm {UCC}}}$
usando a biblioteca química. Nessa situação, temos um estado de referência de determinante único, digamos,
$\ket{\psi_{\rm{SCF}}}$. Os componentes da função de onda de cluster acoplado de unitário são então
especificados implicitamente por meio de um operador de unitário agindo em um estado de referência. Esse
operador de unitário normalmente é escrito como $e^{T-T^\dagger}$, em que $T-T^\dagger$ é o operador de
cluster anti-hermitiano. Portanto \begin{align} \ket{\psi_{\rm {UCC}}} = e^{T-T^\dagger}\ket{\psi_{\rm{SCF}}}.
\end{align}
Também é comum dividir o operador de cluster $T = T_1 + T_2 + \cdots$ em partes, em que cada parte $T_j$
contém os termos do corpo $j$. Na teoria de clusters agrupados generalizados, o operador de cluster de um
corpo (individual) está na forma \begin{align} T_1 = \sum_{pq}t^{p}_{q} a^\dagger_p a_q, \end{align}
e o operador de cluster de dois corpos (duplo) está na forma \begin{align} T_2 = \sum_{pqrs}t^{pq}_{rs}
a^\dagger_p a^\dagger_q a_r a_s. \end{align}
Os termos de ordem superior (triplos, quádruplos, etc.) são possíveis, mas não têm suporte na biblioteca
química no momento.
Por exemplo, permita $\ket{\psi_{\rm{SCF}}} = a^\dagger_1 a^\dagger_2\ket{0}$ e permita let $T= 0.123
a^\dagger_0 a_1 + 0.456 a^\dagger_0a^\dagger_3 a_1 a_2 - 0.789 a^\dagger_3a^\dagger_2 a_1 a_0$. Em
seguida, esse estado é instanciado na biblioteca química da seguinte maneira.

// Create a list of indices of the creation operators


// for the single-reference state
var reference = new[] { 1, 2 };

// Create a list describing the cluster operator


// The first half of each list of integers will be
// associated with the creation operators, and
// the second half with the annihilation operators.
var clusterOperator = new[]
{
(new [] {0, 1}, 0.123),
(new [] {0, 3, 1, 2}, 0.456),
(new [] {3, 2, 1, 0}, 0.789)
};

// Create a fermion wavefunction object that represents the


// unitary coupled-cluster wavefunction. It is assumed implicity
// that the exponent of the unitary coupled-cluster operator
// is the cluster operator minus its Hermitian conjugate.
var wavefunction = new FermionWavefunction<int>(reference, clusterOperator);

A conservação do spin pode ser explicitada, em vez disso, especificando índices SpinOrbital em vez de índices
de números inteiros. Por exemplo, permita que $\ket{\psi_{\rm{SCF}}} = a^\dagger_{1,\uparrow} a^\dagger_{2,
\downarrow}\ket{0}$ e permita que $T= 0.123 a^\dagger_{0, \uparrow} a_{1, \uparrow} + 0.456 a^\dagger_{0,
\uparrow} a^\dagger_{3, \downarrow} a_{1, \uparrow} a_{2, \downarrow} - 0.789 a^\dagger_{3,\uparrow}
a^\dagger_{2,\uparrow} a_{1,\uparrow} a_{0, \uparrow}$ conservem o spin. Em seguida, esse estado é
instanciado na biblioteca química da seguinte maneira.
// Create a list of indices of the creation operators
// for the single-reference state
var reference = new[] { (1, Spin.u), (2, Spin.d) }.ToSpinOrbitals();

// Create a list describing the cluster operator


// The first half of each list of integers will be
// associated with the creation operators, and
// the second half with the annihilation operators.
var clusterOperator = new[]
{
(new [] {(0, Spin.u), (1, Spin.u)}, 0.123),
(new [] {(0, Spin.u), (3, Spin.d), (1, Spin.u), (2, Spin.d)}, 0.456),
(new [] {(3, Spin.u), (2, Spin.u), (1, Spin.u), (0, Spin.u)}, 0.789)
}.Select(o => (o.Item1.ToSpinOrbitals(), o.Item2);

// Create a fermion wavefunction object that represents the


// unitary coupled-cluster wavefunction. It is assumed implicity
// that the exponent of the unitary coupled-cluster operator
// is the cluster operator minus its Hermitian conjugate.
var wavefunctionSpinOrbital = new FermionWavefunction<SpinOrbital>(reference, clusterOperator);

// Convert the wavefunction indexed by spin-orbitals to one indexed


// by integers
var wavefunctionInteger = wavefunctionSpinOrbital.ToIndexing(IndexConvention.UpDown);

Também fornecemos uma função conveniente que enumera em todos os operadores de clusters de
conservação do spin que eliminam apenas spin-orbitais ocupados e excitam apenas spin-orbitais desocupados.

// Create a list of indices of the creation operators


// for the single-reference state
var reference = new[] { (1, Spin.u), (2, Spin.d) }.ToSpinOrbitals();

// Generate all spin-conversing excitations from spin-orbitals


// occupied by the reference state to virtual orbitals.
var generatedExcitations = reference.CreateAllUCCSDSingletExcitations(nOrbitals: 3).Excitations;

// This is the list of expected spin-consvering excitations


var expectedExcitations = new[]
{
new []{ (0, Spin.u), (1,Spin.u)},
new []{ (2, Spin.u), (1,Spin.u)},
new []{ (0, Spin.d), (2,Spin.d)},
new []{ (1, Spin.d), (2,Spin.d)},
new []{ (0, Spin.u), (0, Spin.d), (2, Spin.d), (1,Spin.u)},
new []{ (0, Spin.u), (1, Spin.d), (2, Spin.d), (1,Spin.u)},
new []{ (0, Spin.d), (2, Spin.u), (2, Spin.d), (1,Spin.u)},
new []{ (1, Spin.d), (2, Spin.u), (2, Spin.d), (1,Spin.u)}
}.Select(o => new IndexOrderedSequence<SpinOrbital>(o.ToLadderSequence()));

// The following two assertions are true, and verify that the generated
// excitations exactly match the expected excitations.
var bool0 = generatedExcitations.Keys.All(expectedExcitations.Contains);
var bool1 = generatedExcitations.Count() == expectedExcitations.Count();
Exemplos de química quântica
15/04/2021 • 2 minutes to read

Nos conceitos de química quântica, criamos manualmente o exemplo de Hamiltonianos fermiônicos. Agora,
combinamos os algoritmos de simulação química descritos em Simulando a Dinâmica Hamiltoniana com a
estimativa de fase quântica na biblioteca de cânones. Essa combinação nos permite obter estimativas dos níveis
de energia na molécula representada, que é uma das principais aplicações da química quântica em um
computador quântico.
Em vez de especificar os termos do Hamiltoniano um por um, também trabalhamos com alguns exemplos que
nos permitem executar experimentos de química quântica em escala. Começamos com exemplos que carregam
um Hamiltoniano químico codificado no esquema de Broombridge.
Para moléculas grandes demais para serem simuladas no simulador de estado completo, experimentos
científicos interessantes ainda podem ser realizados. Por exemplo, os custos com recursos para executar
simulações químicas grandes ainda podem ser avaliados usando o simulador de rastreamento.
Agora, vamos ilustrar as aplicações interessantes da biblioteca de simulação química por meio de alguns dos
exemplos fornecidos.
Obter estimativas do nível de energia
19/05/2021 • 4 minutes to read

Estimar os valores dos níveis de energia é um dos principais aplicativos da química quântica. Este artigo
descreve como você pode executar isso para o exemplo canônico de hidrogênio molecular. O exemplo
referenciado nesta seção é MolecularHydrogen no repositório de exemplos de química. Um exemplo mais visual
que plota a saída é a demonstração MolecularHydrogenGUI .

Como estimar os valores de energia do hidrogênio molecular


A primeira etapa é criar o hamiltoniano que representa o hidrogênio molecular. Embora você possa criar isso
usando a ferramenta NWChem, para resumir, este exemplo adiciona os termos hamiltonianos manualmente.

// These orbital integrals are represented using the OrbitalIntegral


// data structure.
var energyOffset = 0.713776188; // This is the coulomb repulsion
var nElectrons = 2; // Molecular hydrogen has two electrons
var orbitalIntegrals = new OrbitalIntegral[]
{
new OrbitalIntegral(new[] { 0,0 }, -1.252477495),
new OrbitalIntegral(new[] { 1,1 }, -0.475934275),
new OrbitalIntegral(new[] { 0,0,0,0 }, 0.674493166),
new OrbitalIntegral(new[] { 0,1,0,1 }, 0.181287518),
new OrbitalIntegral(new[] { 0,1,1,0 }, 0.663472101),
new OrbitalIntegral(new[] { 1,1,1,1 }, 0.697398010),
// This line adds the identity term.
new OrbitalIntegral(new int[] { }, energyOffset)
};

// Initialize a fermion Hamiltonian data structure and add terms to it.


var fermionHamiltonian = new OrbitalIntegralHamiltonian(orbitalIntegrals).ToFermionHamiltonian();

A simulação do hamiltoniano requer a conversão dos operadores fermion em operadores qubit. Essa conversão
é executada por meio da codificação de Jordan-Wigner da seguinte maneira:

// The Jordan-Wigner encoding converts the fermion Hamiltonian,


// expressed in terms of fermionic operators, to a qubit Hamiltonian,
// expressed in terms of Pauli matrices. This is an essential step
// for simulating our constructed Hamiltonians on a qubit quantum
// computer.
var jordanWignerEncoding = fermionHamiltonian.ToPauliHamiltonian(Pauli.QubitEncoding.JordanWigner);

// You also need to create an input quantum state to this Hamiltonian.


// Use the Hartree-Fock state.
var fermionWavefunction = fermionHamiltonian.CreateHartreeFockState(nElectrons);

// This Jordan-Wigner data structure also contains a representation


// of the Hamiltonian and wavefunction made for consumption by the Q# operations.
var qSharpHamiltonianData = jordanWignerEncoding.ToQSharpFormat();
var qSharpWavefunctionData = fermionWavefunction.ToQSharpFormat();
var qSharpData = QSharpFormat.Convert.ToQSharpFormat(qSharpHamiltonianData, qSharpWavefunctionData);

Em seguida, passe qSharpData, que representa o hamiltoniano, para a função TrotterStepOracle .


TrotterStepOracle retorna uma operação quântica que aproxima a evolução em tempo real do hamiltoniano.
Para obter mais informações, confira Como simular a dinâmica hamiltoniana.
// qSharpData passed from driver
let qSharpData = ...

// Choose the integrator step size


let stepSize = 1.0;

// Choose the order of the Trotter—Suzuki integrator.


let integratorOrder = 4;

// `oracle` is an operation that applies a single time-step of evolution for duration `stepSize`.
// `rescale` is just `1.0/stepSize` -- the number of steps required to simulate unit-time evolution.
// `nQubits` is the number of qubits that must be allocated to run the `oracle` operation.
let (nQubits, (rescale, oracle)) = TrotterStepOracle (qSharpData, stepSize, integratorOrder);

Neste ponto, você pode usar os algoritmos de estimativa de fase da biblioteca padrão para aprender a energia
em estado fundamental usando a simulação anterior. Isso requer a preparação de uma boa aproximação para o
estado de aterramento do quantum. As sugestões para essas aproximações são fornecidas no esquema
Broombridge . No entanto, quando essas sugestões estão ausentes, a abordagem padrão adiciona um número de
elétrons hamiltonian.NElectrons para minimizar ambiciosamente as energias no termo de um elétron. As
funções e operações de estimativa de fase são fornecidas na notação de DocFX no namespace
Microsoft.Quantum.Caracterization.
O snippet a seguir mostra como a saída de evolução em tempo real da biblioteca de simulação de química se
integra à estimativa de fase quântica.

operation GetEnergyByTrotterization (
qSharpData : JordanWignerEncodingData,
nBitsPrecision : Int,
trotterStepSize : Double,
trotterOrder : Int) : (Double, Double) {

// The data describing the Hamiltonian for all these steps is contained in
// `qSharpData`
let (nSpinOrbitals, fermionTermData, statePrepData, energyOffset) = qSharpData!;

// Using a Product formula, also known as `Trotterization`, to


// simulate the Hamiltonian.
let (nQubits, (rescaleFactor, oracle)) =
TrotterStepOracle(qSharpData, trotterStepSize, trotterOrder);

// The operation that creates the trial state is defined here.


// By default, greedy filling of spin-orbitals is used.
let statePrep = PrepareTrialState(statePrepData, _);

// Using the Robust Phase Estimation algorithm


// of Kimmel, Low and Yoder.
let phaseEstAlgorithm = RobustPhaseEstimation(nBitsPrecision, _, _);

// This runs the quantum algorithm and returns a phase estimate.


let estPhase = EstimateEnergy(nQubits, statePrep, oracle, phaseEstAlgorithm);

// Now, obtain the energy estimate by rescaling the phase estimate


// with the trotterStepSize. We also add the constant energy offset
// to the estimated energy.
let estEnergy = estPhase * rescaleFactor + energyOffset;

// Return both the estimated phase and the estimated energy.


return (estPhase, estEnergy);
}

Agora você pode invocar o código Q# do programa host. O código C# a seguir cria um simulador de estado
completo e é executa GetEnergyByTrotterization para obter a energia em estado fundamental.
using (var qsim = new QuantumSimulator())
{
// Specify the bits of precision desired in the phase estimation
// algorithm
var bits = 7;

// Specify the step size of the simulated time evolution. The step size needs to
// be small enough to avoid aliasing of phases, and also to control the
// error of simulation.
var trotterStep = 0.4;

// Choose the Trotter integrator order


Int64 trotterOrder = 1;

// As the quantum algorithm is probabilistic, run a few trials.

// This may be compared to true value of


Console.WriteLine("Exact molecular hydrogen ground state energy: -1.137260278.\n");
Console.WriteLine("----- Performing quantum energy estimation by Trotter simulation algorithm");
for (int i = 0; i < 5; i++)
{
// EstimateEnergyByTrotterization
var (phaseEst, energyEst) = GetEnergyByTrotterization.Run(qsim, qSharpData, bits, trotterStep,
trotterOrder).Result;
}
}

A operação retorna dois parâmetros:


energyEst é a estimativa da energia em estado fundamental e deve estar perto de -1.137 em média.
phaseEst é a fase bruta retornada pelo algoritmo de estimativa de fase. Quando ocorre devido a um valor
de trotterStep grande demais, isso é útil para diagnosticar aliases.
Carregar um Hamiltonian do arquivo
19/05/2021 • 2 minutes to read

Anteriormente, criávamos hamiltonianos adicionando termos individuais a ele. Isso é bom para exemplos
pequenos, mas a química quântica em escala exige hamiltonianos com milhões ou bilhões de termos. Esses
hamiltonianos, gerados por pacotes de química como o NWChem, são muito grandes para importação manual.
Neste exemplo, ilustramos como uma instância FermionHamiltonian pode ser gerada automaticamente de um
molécula representada pelo esquema Broombridge. Para referência, é possível inspecionar o exemplo
LithiumHydrideGUI fornecido ou o exemplo RunSimulation . O suporte limitado também está disponível para
importação do formato consumido por LIQUi|>.
Vamos considerar o exemplo da molécula de nitrogênio, que é fornecido na pasta IntegralData/YAML do
repositório de exemplos. O método para carregar o esquema Broombridge é simples.

// This is the name of the file we want to load


var filename = @"n2_1_00Re_sto3g.nw.out.yaml";
// This is the directory containing the file
var root = @"IntegralData\YAML";

// This deserializes a Broombridge file, given its filename.


var broombridge = Broombridge.Deserializers.DeserializeBroombridge($@"{root}\{filename}");

// Note that the deserializer returns a list of `ProblemDescription` instances


// as the file might describe multiple Hamiltonians. In this example, there is
// only one Hamiltonian. So we use `.First()`, which selects the first element of the list.
var problem = broombridge.ProblemDescriptions.First();

// This extracts the `OrbitalIntegralHamiltonian` from Broombridge format,


// then converts it to a fermion Hamiltonian, then to a Jordan-Wigner
// representation.
var orbitalIntegralHamiltonian = problem.OrbitalIntegralHamiltonian;
var fermionHamiltonian = orbitalIntegralHamiltonian.ToFermionHamiltonian(IndexConvention.UpDown);
var jordanWignerEncoding = fermionHamiltonian.ToPauliHamiltonian(Pauli.QubitEncoding.JordanWigner);

O esquema Broombridge também contém sugestões para que o estado inicial seja preparado. Os rótulos (por
exemplo, "|G " ou "|E1 " ) para esses estados podem ser vistos inspecionando o arquivo. Para preparar esses
estados iniciais, o qSharpData consumido pelos Q# algoritmos quânticos é obtido de maneira semelhante à
seção anterior, mas com um parâmetro adicional selecionando o estado inicial desejado. Por exemplo,

// The desired initial state, assuming that a description of it is present in the


// Broombridge schema.
var state = "|E1>";
var wavefunction = problem.Wavefunctions[state].ToIndexing(IndexConvention.UpDown);

// This creates the qSharpData consumable by the Q# chemistry library algorithms.


var qSharpHamiltonianData = jordanWignerEncoding.ToQSharpFormat();
var qSharpWavefunctionData = wavefunction.ToQSharpFormat();
var qSharpData = QSharpFormat.Convert.ToQSharpFormat(qSharpHamiltonianData, qSharpWavefunctionData);

Todas as etapas acima podem ser abreviadas usando métodos de conveniência fornecidos a seguir.
// This is the name of the file we want to load
var filename = "...";

// This deserializes a Broombridge file, given its filename.


var broombridge = Broombridge.Deserializers.DeserializeBroombridge(filename);

// Note that the deserializer returns a list of `ProblemDescription` instances


// as the file might describe multiple Hamiltonians. In this example, there is
// only one Hamiltonian. So we use `.Single()`, which selects the only element of the list.
var problem = broombridge.ProblemDescriptions.Single();

// This is a data structure representing the Jordan-Wigner encoding


// of the Hamiltonian that we may pass to a Q# algorithm.
// If no state is specified, the Hartree-Fock state is used by default.
var qSharpData = problem.ToQSharpFormat("|E1>");

Também poderemos carregar um hamiltoniano no formato LIQUi|>, usando uma sintaxe muito semelhante.

// This is the name of the file we want to load


var filename = @"fe2s2_sto3g.dat"; // This is Ferrodoxin.
// This is the directory containing the file
var root = @"IntegralData\Liquid";

// Deserialize the LiQuiD format.


var problem = LiQuiD.Deserialize($@"{root}\{filename}").First();

// This is a data structure representing the Jordan-Wigner encoding


// of the Hamiltonian that we may pass to a Q# algorithm.
var qSharpData = problem.ToQSharpFormat();

Seguindo esse processo de qualquer instância de Broombridge, ou qualquer etapa intermediária, os algoritmos
quânticos, como a estimativa de fase quântica, podem ser executados no problema de estrutura eletrônica
especificado.
Obter contagens de recursos
19/05/2021 • 2 minutes to read

O custo da simulação de $n$ qubits em computadores clássicos é dimensionado exponencialmente com $n$.
Isso limita bastante o tamanho de uma simulação de química quântica que pode ser executada com o simulador
de estado completo. Para grandes instâncias de química, poderemos, no entanto, obter informações úteis. Aqui,
examinamos como os custos de recursos, como o número de portões T ou portões CNOT, para simular a
química podem ser obtidos de maneira automatizada usando o simulador de rastreamento. Essas informações
informam quando os computadores quânticos podem ser grandes o suficiente para executar esses algoritmos
de química quântica. Para referência, veja o exemplo GetGateCount fornecido.
Vamos supor que já temos uma instância FermionHamiltonian , digamos, carregada do esquema Broombridge,
conforme discutido no exemplo de carregar do arquivo.

// Filename of Hamiltonian to be loaded.


var filename = "...";

// This deserializes Broombridge.


var problem = Broombridge.Deserializers.DeserializeBroombridge(filename).ProblemDescriptions.First();

// This is a data structure representing the Jordan-Wigner encoding


// of the Hamiltonian that we may pass to a Q# algorithm.
var qSharpData = problem.ToQSharpFormat();

A sintaxe para obter estimativas de recursos é quase idêntica à execução do algoritmo no simulador de estado
completo. Simplesmente escolhemos um computador de destino diferente. Para fins de estimativas de recursos,
é suficiente avaliar o custo de uma só etapa Trotter ou uma movimentação quântica criada pela técnica de
Qubitization. O padrão para invocar esses algoritmos é como segue.
//////////////////////////////////////////////////////////////////////////
// Using Trotterization //////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/// # Summary
/// This allocates qubits and applies a single Trotter step.
operation RunTrotterStep (qSharpData: JordanWignerEncodingData) : Unit {

// The data describing the Hamiltonian for all these steps is contained in
// `qSharpData`
// We use a Product formula, also known as `Trotterization` to
// simulate the Hamiltonian.
// The integrator step size does not affect the gate cost of a single step.
let trotterStepSize = 1.0;

// Order of integrator
let trotterOrder = 1;
let (nQubits, (rescaleFactor, oracle)) = TrotterStepOracle(qSharpData, trotterStepSize, trotterOrder);

// We not allocate qubits an run a single step.


use qubits = Qubit[nQubits]);
oracle(qubits);
ResetAll(qubits);
}

//////////////////////////////////////////////////////////////////////////
// Using Qubitization ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

/// # Summary
/// This allocates qubits and applies a single qubitization step.
operation RunQubitizationStep (qSharpData: JordanWignerEncodingData) : Double {

// The data describing the Hamiltonian for all these steps is contained in
// `qSharpData`
let (nQubits, (l1Norm, oracle)) = QubitizationOracle(qSharpData);

// We now allocate qubits and run a single step.


using (qubits = Qubit[nQubits]) {
oracle(qubits);
ResetAll(qubits);
}

return l1Norm;
}

Agora, vamos configurar o simulador de rastreamento para acompanhar os recursos nos quais estamos
interessados. Nesse caso, contamos as operações quânticas primitivas configurando o sinalizador
usePrimitiveOperationsCounter como true . Um detalhe técnico throwOnUnconstraintMeasurement é definido
como false para evitar exceções em casos em que o código Q# não declara corretamente a probabilidade de
resultados de medida, se algum for executado.
private static QCTraceSimulator CreateAndConfigureTraceSim()
{
// Create and configure Trace Simulator
var config = new QCTraceSimulatorConfiguration()
{
usePrimitiveOperationsCounter = true,
throwOnUnconstraintMeasurement = false
};

return new QCTraceSimulator(config);


}

Agora, executamos o algoritmo quântico do programa de driver como segue.

// Instantiate a trace simulator instance


QCTraceSimulator sim = CreateAndConfigureTraceSim();

// Run the quantum algorithm on the trace simulator.


RunQubitizationStep.Run(sim, qSharpData);

// Print all resource counts to file.


var gateStats = sim.ToCSV();
foreach (var x in gateStats)
{
System.IO.File.WriteAllLines($"QubitizationGateCountEstimates.{x.Key}.csv", new string[] { x.Value });
}
Ponta a ponta com NWChem
19/05/2021 • 6 minutes to read

Neste artigo, você examinará um exemplo de como obter contagens de portão para a simulação de química
quântica, começando com um conjunto de entrada NWChem. Antes de continuar com este exemplo, verifique se
você instalou o Docker, seguindo o guia de instalação e validação.
Para mais informações:
Estrutura de conjuntos de entrada NWChem
Comandos de conjunto de entrada para uso com o Quantum development kit
Como instalar a biblioteca e as dependências de química
Contagem de recursos

NOTE
Este exemplo requer que o Windows PowerShell Core seja executado. Baixe o PowerShell Core para Windows, macOS ou
Linux em https://github.com/PowerShell/PowerShell.

Como importar os módulos do PowerShell necessários


Se você ainda não fez isso, clone o repositório Microsoft/Quantum, que contém exemplos e utilitários para
trabalhar com o Quantum development kit:

git clone https://github.com/Microsoft/Quantum

Depois de clonar Microsoft/Quantum , execute cd na pasta utilities/ e importe o módulo do PowerShell para
trabalhar com Docker e NWChem:

cd utilities
Import-Module InvokeNWChem.psm1

NOTE
Por padrão, o Windows impede a execução de qualquer script ou módulo como uma medida de segurança. Para permitir
que módulos como o Invoke-NWChem.psm1 sejam executados no Windows, talvez seja necessário alterar a política. Para
fazer isso, execute o script Set-ExecutionPolicy :

Set-ExecutionPolicy RemoteSigned -Scope Process

A política será revertida quando você sair do PowerShell. Se você quiser salvar a política, use um valor diferente para
-Scope :

Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

Agora você deve ter o comando Convert-NWChemToBroombridge disponível:


Get-Command -Module InvokeNWChem

Em seguida, importaremos o comando Get-GateCount fornecido com o exemplo GetGateCount . Para obter
detalhes completos, confira as instruções fornecidas com o exemplo. Em seguida, execute o seguinte,
substituindo <runtime> por um win10-x64 , osx-x64 ou linux-x64 , dependendo do seu sistema operacional:

cd ../Chemistry/GetGateCount
dotnet publish --self-contained -r <runtime>
Import-Module ./bin/Debug/netcoreapp2.1/<runtime>/publish/get-gatecount.dll

Agora você deve ter o comando Get-GateCount disponível:

Get-Command Get-GateCount

Conjuntos de entrada
O pacote NWChem pega um arquivo de texto chamado de conjunto de entrada, que especifica um problema de
química quântica a ser resolvido, juntamente com outros parâmetros, como configurações de alocação de
memória. Para este exemplo, vamos usar um dos conjuntos de entrada predefinidos que vem com o NWChem.
Primeiro, clone o repositório nwchemgit/nwchem:

NOTE
Como esse é um repositório muito grande, podemos fazer um clone superficial para poupar alguma largura de banda e
espaço em disco, usando o argumento --depth 1 . Porém, isso é opcional. A clonagem funcionará bem sem --depth 1
.

git clone https://github.com/nwchemgit/nwchem --depth 1

O repositório nwchemgit/nwchem vem com uma variedade de conjuntos de entrada destinados ao uso com o
Quantum development kit, listado na pasta QA/chem_library_tests . Para este exemplo, usaremos o conjunto de
entrada H4 :

cd nwchem/QA/chem_library_tests/H4
Get-Content h4_sto6g_0.000.nw

O molécula em questão é um sistema de quatro átomos de hidrogênio organizados em uma determinada


geometria que depende de um ângulo, o parâmetro alpha conforme indicado no nome h4_sto6g_alpha.nw do
conjunto. H4 é um parâmetro de benchmark molecular conhecido para química computacional desde os anos
1970. O parâmetro sto6g indica que o conjunto implementa uma representação em relação a um orbital do
tipo Slater, especificamente, uma representação com relação a um conjunto de base STO-nG com seis funções
de base gaussianas. Esse conjunto de entrada contém várias instruções para o TCE (mecanismo de contração de
tensor) NWChem que direciona o NWChem para produzir as informações necessárias para interoperar com o
Quantum development kit:
...
set tce:print_integrals T
set tce:qorb 18
set tce:qela 9
set tce:qelb 9

Como produzir e consumir a saída de Broombridge de NWChem


Agora você tem tudo de que precisa para produzir e consumir documentos Broombridge. Para executar o
NWChem e produzir um documento Broombridge para o conjunto de entrada h4_sto6g_0.000.nw , execute
Convert-NWChemToBroombridge :

NOTE
Na primeira vez que você executar esse comando, o Docker baixará a imagem nwchemorg/nwchem-qc para você. Isso
pode demorar um pouco, dependendo da velocidade da sua conexão, possivelmente dando a oportunidade de pegar
uma xícara de café.

Convert-NWChemToBroombridge h4_sto6g_0.000.nw

Isso produzirá um documento Broombridge chamado h4_sto6g_0.000.yaml que você pode usar com
Get-GateCount :

Get-GateCount -Format YAML h4_sto6g_0.000.yaml

Agora você deve ver a saída do console que contém a estimativa de recurso, como contagem de T, contagem de
rotações, contagem de CNOT etc. para vários métodos de simulação quântica:

IntegralDataPath : C:\Users\martinro\REPOS\nwchem\qa\chem_library_tests\h4\h4_sto6g_0.000.yaml
HamiltonianName : hamiltonian_0
SpinOrbitals : 8
Method : Trotter
TCount : 0
RotationsCount : 92
CNOTCount : 520
ElapsedMilliseconds : 327

IntegralDataPath : C:\Users\martinro\REPOS\nwchem\qa\chem_library_tests\h4\h4_sto6g_0.000.yaml
HamiltonianName : hamiltonian_0
SpinOrbitals : 8
Method : Qubitization
TCount : 438
RotationsCount : 516
CNOTCount : 2150
ElapsedMilliseconds : 528

IntegralDataPath : C:\Users\martinro\REPOS\nwchem\qa\chem_library_tests\h4\h4_sto6g_0.000.yaml
HamiltonianName : hamiltonian_0
SpinOrbitals : 8
Method : Optimized Qubitization
TCount : 3540
RotationsCount : 18
CNOTCount : 7932
ElapsedMilliseconds : 721

Há muitas coisas a serem passadas aqui:


Experimente diferentes conjuntos de entrada predefinidos, por exemplo, variando o parâmetro alpha em
h4_sto6g_alpha.nw ,
Tente modificar os conjuntos editando os conjuntos NWChems diretamente, por exemplo, explorando
modelos STO-nG para várias opções de n,
Tente outros conjuntos de entrada NWChem predefinidos disponíveis em nwchem/qa/chem_library_tests ,
Experimente um conjunto de benchmarks predefinidos de YAML de Broombridge que foram gerados do
NWChem e estão disponíveis como parte do repositório Microsoft/Quantum. Esses parâmetros de
comparação incluem:
moléculas pequenas, como hidrogênio molecular (H2), berílio (Be), hidreto de lítio (LiH),
moléculas maiores, como ozônio (O3), betacaroteno, citosina e muitas outras.
Experimente as Setas de EMSL do front-end gráfico que apresenta uma interface para o Microsoft Quantum
development kit.

Como gerar a saída Broombridge de setas EMSL


Para começar a usar as setas EMSL do front-end baseadas na Web, navegue até um navegador aqui.

NOTE
A execução de setas EMSL em um navegador da Web requer que o JavaScript esteja habilitado. Veja estas instruções
sobre como habilitar o JavaScript em seu navegador.

Primeiro, insira um molécula na caixa de consulta e Insira um esmiles, uma reação esmiles ou outra
entrada de Setas, então pressione o botão "Executar Setas" .
Você pode inserir muitas moléculas por seu nome de uso coloquial, como "cafeína", em vez de "1,3,7-
Trimetilxantina".
Em seguida, clique em theor y{qsharp_chem} . Isso preencherá a caixa de consulta mais detalhadamente com
uma instrução que dirá a execução para exportar a saída no formato YAML de Broombridge.
Agora, clique em Executar Setas . Dependendo do tamanho da entrada, isso pode levar algum tempo. Ou, caso
o modelo específico já tenha sido computado antes, isso poderá ser feito de modo extremamente rápido, pois
exigirá apenas uma pesquisa em um banco de dados. Em ambos os casos, você será levado para uma nova
página que contém uma grande quantidade de informações sobre a execução específica de NWChem em
relação ao conjunto especificado por sua entrada.
Você pode baixar e salvar o arquivo YAML de Broombridge da seção que começa com o seguinte cabeçalho:

+==================================================+
|| Molecular Calculation ||
+==================================================+

Id = 48443

NWOutput = Link to NWChem Output (download)

Datafiles:
qsharp_chem.yaml-2018-10-23-14:37:42 (download)
...

Clique em Baixar , que salva uma cópia local com um nome de arquivo exclusivo, por exemplo
qsharp_chem48443.yaml (o nome específico será diferente para cada execução). Em seguida, você pode
processar esse arquivo como acima, por exemplo, com
Get-GateCount -Format YAML qsharp_chem48443.yaml

para obter contagens de recursos.


Você pode desfrutar do construtor de molécula 3D que pode ser acessado na guia Entrada de Setas –
Construtor 3D na página inicial das setas EMSL. Clicar na imagem 3D JSMol do molécula mostrada permitirá
que você a edite. Você pode mover os átomos, arrastar átomos para que suas distâncias intermoleculares sejam
alteradas, adicionar/remover átomos etc. Para cada uma dessas opções, depois de adicionar
theor y{qsharp_chem} na caixa de consulta, você pode gerar uma instância do esquema YAML de
Broombridge e explorá-la ainda mais usando a biblioteca química do Quantum.
Esquema do Broombridge Quantum de química
01/05/2021 • 2 minutes to read

Um software robusto de química computacional, como o NWChem, permite modelar uma ampla gama de
problemas de química do mundo real. Para acessar modelos do NWChem molecular com a biblioteca de
química Quantum da Microsoft, use um esquema baseado em YAMLchamado Broombridge . O nome foi
escolhido em referência a um marco que em alguns círculos, é celebrado como local de nascimento dos
hamiltonianos.
O NWChem é um projeto de software livre licenciado sob a licença Comunitária Educacional permissiva (ECL)
2.0. O esquema do Broombridge Quantum química ) é um esquema de software livre que inclui uma definição
após RFC 2119 e um script de validador licenciado sob a licença MIT.
Sendo baseado em YAML, o Broombridge é uma maneira estruturada, legível e humana, de representar
problemas de estrutura eletrônica. Em particular, os dados a seguir podem ser representados:
Hamiltonianos fermiônicos podem ser representado usando integrais de um e dois.
Os estados terrestre e excitados podem ser apresentados usando sequências de criação.
Os limites superiores e inferiores dos níveis de energia podem ser especificados.
Os dados podem ser gerados a partir de NWChem usando vários métodos, como o uso de uma instalação
completa do NWChem para executar deques de química (por exemplo, aqueles fornecidos na biblioteca
NWChem que geram Broombridge como parte da execução) ou uma imagem do Docker de NWChem que
também pode ser usada para gerar Broombridge de deques de química. Para começar com o química
computacional rapidamente sem precisar instalar nenhum software de química, você pode usar a interface
visual para NWChem fornecida pelas setas EMSL.
Em um alto nível, a interação entre NWChem e o kit de desenvolvimento Quantum da Microsoft pode ser
visualizada da seguinte maneira:
a caixa sombreada azul representa o esquema Broombridge, as várias caixas sombreadas de cinza representam
outras representações de dados internas que foram escolhidas para representar e processar os algoritmos do
Quantum para a química computacional com base nos problemas de química do mundo real.
A pasta integral/YAML no repositório de exemplos do kit de desenvolvimento Quantum contém várias
representações químicas definidas usando o esquema Broombridge.
Especificação Broombridge ver 0.2
01/05/2021 • 12 minutes to read

As palavras-chave "DEVE", "NÃO DEVE", "OBRIGATÓRIO", "PODE", "NÃO PODE", "DEVERIA", "NÃO DEVERIA",
"RECOMENDADO" "PODE" e "OPCIONAL" neste documento devem ser interpretadas conforme descritas na
RFC 2119.
Qualquer barra lateral com os títulos "OBSERVAÇÃO", "INFORMAÇÃO" ou "AVISO" é informativa.

Introdução
Esta seção é informativa.
Os documentos do Broombridge destinam-se a comunicar instâncias de problemas de simulação em química
do Quantum para processamento usando simulação de Quantum e programação cadeias.

Serialização
Esta seção é normativa.
Um documento Broombridge deve ser serializado como um documento YAML 1.2 que representa um objeto
JSON, conforme descrito em RFC 4627 seção 2.2. O objeto serializado para YAML deve ter uma propriedade
"$schema" cujo valor é
"https://raw.githubusercontent.com/Microsoft/Quantum/master/Chemistry/Schema/qchem-0.2.schema.json" e deve
ser válido de acordo com as especificações do rascunho do esquema JSON-06 [1, 2].
Para o restante desta especificação, "o objeto Broombridge" fará referência ao objeto JSON desserializado de
um documento Broombridge YAML.
A menos que indicado de outra forma explicitamente, os objetos não devem ter propriedades adicionais além
daquelas especificadas explicitamente neste documento.

Definições adicionais
Esta seção é normativa.
Quantidade de objetos
Esta seção é normativa.
Um objeto de quantidade é um objeto JSON e deve ter uma propriedade units cujo valor seja um dos valores
permitidos listados na tabela 1.
Um objeto de quantidade é um objeto de quantidade simples se tiver uma única propriedade, value além de
sua propriedade units . O valor da propriedade " value " deve ser um número.
Um objeto de quantidade é um objeto de quantidade vinculada se ele tiver as propriedades lower e, upper
além de sua propriedade units . Os valores das lower Propriedades e upper devem ser números. Um objeto
de quantidade associada pode ter uma propriedade value cujo valor é um número.
Um objeto de quantidade é um objeto de quantidade de matriz esparsa se ele tiver a propriedade format e
uma propriedade além de values sua units propriedade. O valor de format deve ser a cadeia de caracteres
sparse . O valor da propriedade " values " deve ser uma matriz. Cada elemento de values deve ser uma matriz
que representa os índices e o valor da quantidade de matriz esparsa.
Os índices para cada elemento de um objeto de quantidade de matriz esparsa devem ser exclusivos em todo o
objeto de quantidade de matriz esparsa. Se um índice estiver presente com um valor de 0 , um analisador deve
tratar o objeto de quantidade de matriz esparsa de forma idêntica a um objeto de quantidade de matriz esparsa
no qual esse índice não está presente.
Um objeto de quantidade deve ser
um objeto de quantidade simples,
um objeto de quantidade limitada, ou
um objeto de quantidade de matriz esparsa.
Exemplos
Esta seção é informativa.
Uma quantidade simples representando $1.9844146837 , \mathrm{ha} $:

coulomb_repulsion:
value: 1.9844146837
units: hartree

Uma quantidade limitada que representa uma distribuição uniforme sobre o intervalo $[-7,-6] , \mathrm{ha}$:

fci_energy:
upper: -6
lower: -7
units: hartree

A seguir está uma quantidade de matriz esparsa com um elemento [1, 2] igual a hello e um elemento
[3, 4] igual a world :

sparse_example:
format: sparse
units: hartree
values:
- [1, 2, "hello"]
- [3, 4, "world"]

Seção de formato
Esta seção é normativa.
O objeto Broombridge deve ter uma propriedade format cujo valor é um objeto JSON com uma propriedade
chamada version . A propriedade version deve ter o valor "0.2" .
Exemplo
Esta seção é informativa.

format: # required
version: "0.2" # must match exactly

Seção de descrição do problema


Esta seção é normativa.
O objeto Broombridge deve ter uma propriedade problem_description cujo valor é uma matriz JSON. Cada
item no valor da propriedade problem_description deve ser um objeto JSON que descreve um conjunto de
integrais, conforme descrito no restante desta seção. No restante desta seção, o termo "objeto de descrição do
problema" fará referência a um item no valor da propriedade problem_description do objeto Broombridge.
Cada objeto de descrição do problema deve ter uma propriedade metadata cujo valor é um objeto JSON. O
valor de metadata pode ser o objeto JSON vazio (ou seja, {} ) ou pode conter propriedades adicionais
definidas pelo implementador.
Seção Hamiltonian
Visão geral
Esta seção é informativa.
A propriedade hamiltonian de cada objeto de descrição do problema descreve o Hamiltonian para um
problema de química do Quantum específico, listando seus termos de um e dois corpo como matrizes esparsas
de números reais. Os operadores de Hamiltonian descritos por cada objeto de descrição de problema assumem
o formulário
$ $ H = \sum _ { i, j } \sum _ {\sigma\in \ {\uparrow, \downarrow \ }} H _ { IJ } a ^ { \dagger } _ {i, \sigma} a _ { j,
\sigma} + \frac {1} {2} \sum _ { i, j, k, l } \sum _ {\sigma, \rho\in \ {\uparrow, \downarrow \ }} H _ {ijkl} a ^ \dagger
_ {i, \sigma} a ^ \dagger _ {k, \rho} a _ {l, \rho} a _ { j, \sigma}, $ $
Aqui $h _ {ijkl} = (IJ | KL) $ na Convenção Mulliken.
Para maior clareza, o termo de um elétron é
$$ h_{ij} = \int {\mathrm d}x \psi^*_i(x) \left(\frac{1}{2}\nabla^2 + \sum_{A}\frac{Z_A}{|x-x_A|} \right) \psi_j(x),
$$
e o termo de dois elétrons é
$$ h_{ijkl} = \iint {\mathrm d}x^2 \psi^{*}_i(x_1)\psi_j(x_1) \frac{1}{|x_1 -x_2|}\psi_k^{*}(x_2) \psi_l(x_2). $$
Conforme observado em nossa descrição da basis_set Propriedade de cada elemento da propriedade
integral_sets , supomos que as funções de base usadas sejam de valor real. Isso nos permite usar as seguintes
simetrias entre os termos para compactar a representação do Hamiltonian.
$$ h_{ijkl} = h_{ijlk}=h_{ jikl}=h_{ jilk}=h_{klij}=h_{klji}=h_{lkij}=h_{lkji}. $$

NOTE
O termo $h _ {ijkl} = (IJ | KL)$ segue a convenção de índice de Mulliken, também conhecida como notação de químicos. A
representação usada pelo .NET e modelos de dados Q# segue a Dirac ou notação dos físicos, em que
$$ h_{ijkl} = \iint {\mathrm d}x^2 \psi^{*}_i(x_1)\psi^{*}_j(x_2) \frac{1}{|x_1 -x_2|}\psi_k (x_1) \psi_l(x_2)$$.
Atualmente, o esquema Broombridge dá suporte apenas à indexação Mulliken, de modo que o desserializador converte
entre as duas convenções ao carregar dados.

Sumário
Esta seção é normativa.
Cada objeto de descrição do problema deve ter uma propriedade hamiltonian cujo valor é um objeto JSON. O
valor da propriedade hamiltonian é conhecido como um objeto hamiltoniano e deve ter as propriedades e,
one_electron_integrals two_electron_integrals conforme descrito no restante desta seção.

Cada objeto de descrição do problema deve ter uma propriedade coulomb_repulsion cujo valor é um objeto de
quantidade simples. Cada objeto de descrição do problema deve ter uma propriedade energy_offet cujo valor
é um objeto de quantidade simples.

NOTE
Os valores de coulomb_repulsion e energy_offet adicionados em conjunto capturam o termo de identidade do
Hamiltonian.

O b j e t o d e O n e - El e c t r o n i n t e g r a i s

Esta seção é normativa.


A propriedade one_electron_integrals do objeto hamiltoniano deve ser uma quantidade de matriz esparsa
cujos índices são dois inteiros e cujos valores são números. Cada termo deve ter índices [i, j] onde i >= j .

NOTE
Isso reflete a simetria de que $h _ {IJ} = h_ {ji} $, que é uma consequência do fato de que o Hamiltonian é hermitiano.

E x e mp l o

Esta seção é informativa.


A quantidade de matriz esparsa a seguir representa o Hamiltonian $ $ H = \left (-5,0 (a ^ { \dagger } _ {1,
\uparrow} a _ {1, \uparrow} + a ^ { \dagger } _ {1, \downarrow} a _ {1, \downarrow}) + 0,17 (a ^ { \dagger } _ {2,
\uparrow} a _ {1, \uparrow} + a ^ { \dagger } _ {1, \uparrow} a _ {2, \uparrow} + a ^ { \dagger } _ {2,
\downarrow} a _ {1, \downarrow} + a ^ { \dagger } _ {1, \downarrow} a _ {2, \downarrow}) \right) \ ,
\mathrm{ha}. $$

one_electron_integrals: # required
units: hartree # required
format: sparse # required
values: # required
# i j f(i,j)
- [1, 1, -5.0]
- [2, 1, 0.17]

NOTE
Broombridge usa indexação com base em 1.

O b j et o In t eg r ai s d e d o i s el ét r o n s

Esta seção é normativa.


A two_electron_integrals Propriedade do objeto hamiltoniano deve ser uma quantidade de matriz esparsa com
uma propriedade adicional chamada index_convention . Cada elemento do valor de two_electron_integrals
deve ter quatro índices.
Cada propriedade deve ter uma propriedade index_convention . O valor da propriedade
two_electron_integrals
index_convention deve ser um dos valores permitidos listados na tabela 1. Se o valor de index_convention for
mulliken , para cada elemento da quantidade de two_electron_integrals matriz esparsa, um analisador que
carrega um documento Broombridge deve criar uma instância de um termo Hamiltonian igual ao operador de
dois bits $h _ {i, j, k, l} a ^ \ dagger_i um ^ \ dagger_j a_k A_L $, em que $i $, $j $, $k $ e $l $ devem ser inteiros
de valor pelo menos 1 e em que $h _ {i, j, k, l} $ é o elemento [i, j, k, l, h(i, j, k, l)] da quantidade de
matriz esparsa.
Si me t ri a s

Esta seção é normativa.


Se a propriedade index_convention de um objeto two_electron_integrals for igual a mulliken , se um
elemento com índices [i, j, k, l] estiver presente, os seguintes índices não deverão estar presentes, a menos
que sejam iguais a [i, j, k, l] :
[i, j, l, k]
[j, i, k, l]
[j, i, l, k]
[k, l, i, j]
[k, l, j, i]
[l, k, j, i]

NOTE
Como a propriedade index_convention é um objeto de quantidade esparsa, nenhum índice pode ser repetido em
elementos diferentes. Em particular, se um elemento com índices [i, j, k, l] estiver presente, nenhum outro
elemento poderá ter esses índices.

E x e mp l o

Esta seção é informativa.


O objeto a seguir especifica o Hamiltonian
$ $ H = \frac12 \sum _ {\sigma, \rho\in \ {\uparrow, \downarrow \ }} \Biggr (1,6 a ^ {\dagger} _ {1, \sigma} a ^
{\dagger} _ {1, \rho} a _ {1, \rho} a _ {1, \sigma}-0,1 a ^ {\dagger} _ {6, \sigma} a ^ {\dagger} { _ 1, \rho} a _ {3,
\rho} a _ {2, \sigma}-0,1 a ^ {\dagger} _ {6, \sigma} a ^ {\dagger} _ {1, \rho} a _ {2, \rho} a _ {3, \sigma}-0,1 a ^
{\dagger} _ {1, \sigma} a ^ {\dagger} _ {6, \rho} a _ {3, \rho} a _ {2, \sigma}-0,1 a ^ {\dagger} _ {1, \sigma} a ^
{\dagger} _ {6, \rho} a _ {2, \rho} a _ {3, \sigma} $ $ $ $-0,1 a ^ {\dagger} _ {3, \sigma} a ^ {\dagger} _ {2, \rho} a _
{6, \rho} a _ {1, \sigma}-0,1 a ^ {\dagger} _ {3, \sigma} a ^ {\dagger} _ {2, \rho} a _ {1, \rho} a _ {6, \sigma}-0,1 a ^
{\dagger} _ {2, \sigma} a ^ {\dagger} _ {3 , \rho} a _ {6, \rho} a _ {1, \sigma}-0,1 a ^ {\dagger} _ {2, \sigma} a ^
{\dagger} _ {3, \rho} a _ {1, \Rho} a _ {6, \sigma}\Biggr) \ , \textrm{ha}. $$

two_electron_integrals:
index_convention: mulliken
units: hartree
format: sparse
values:
- [1, 1, 1, 1, 1.6]
- [6, 1, 3, 2, -0.1]

Seção de estado inicial


Esta seção é normativa.
O initial_state_suggestion objeto cujo valor é uma matriz JSON especifica os estados quânticos iniciais de
interesse para o Hamiltonian especificado. Cada item no valor da propriedade initial_state_suggestion deve
ser um objeto JSON que descreve um estados quântico, conforme descrito no restante desta seção. No restante
desta seção, o termo "objeto de estado" fará referência a um item no valor da propriedade
initial_state_suggestion do objeto Broombridge.

Objeto de estado
Esta seção é normativa.
Cada objeto de Estado deve ter uma propriedade label que contém uma cadeia de caracteres. Cada objeto de
Estado deve ter uma propriedade method . O valor da propriedade method deve ser um dos valores permitidos
listados na tabela 3. Cada objeto de estado pode ter uma propriedade energy cujo valor deve ser um objeto de
quantidade simples.
Se o valor da propriedade method for sparse_multi_configurational , o objeto de estado deverá ter uma
propriedade superposition que contém uma matriz de Estados de base e suas amplitudes não normalizadas.
Por exemplo, os Estados iniciais $ $ \ket{G0} = \ket{G1} = \ket{G2} = (a ^ {\dagger} _ {1, \uparrow}a ^ {\dagger}
_ {2, \uparrow}a ^ {\dagger} _ {2, \downarrow}) \ket {0} $ $ $ $ \ket{E} = \frac{0.1 (a ^ {\dagger} _ {1, \uparrow}a
^ {\dagger} _ {2, \uparrow}a ^ {\dagger} _ {2, \downarrow}) + 0.2 (um ^ {\dagger} _ {1, \uparrow}a ^ {\dagger} _
{3, \uparrow}a ^ {\dagger} _ {2, \downarrow})} {\sqrt{| 0,1 | ^ 2 + | 0.2 | ^ 2}} \ket {0} , $ $, em que $ \ket{E} $
tem energia $0.987 \textrm{ha} $, são representados por

initial_state_suggestions: # optional. If not provided, spin-orbitals will be filled to minimize one-body


diagonal term energies.
- label: "|G0>"
method: sparse_multi_configurational
superposition:
- [1.0, "(1a)+","(2a)+","(2b)+","|vacuum>"]
- label: "|G1>"
method: sparse_multi_configurational
superposition:
- [-1.0, "(2a)+","(1a)+","(2b)+","|vacuum>"]
- label: "|G2>"
method: sparse_multi_configurational
superposition:
- [1.0, "(3a)","(1a)+","(2a)+","(3a)+","(2b)+","|vacuum>"]
- label: "|E>"
energy: {units: hartree, value: 0.987}
method: sparse_multi_configurational
superposition:
- [0.1, "(1a)+","(2a)+","(2b)+","|vacuum>"]
- [0.2, "(1a)+","(3a)+","(2b)+","|vacuum>"]

Se o valor da propriedade method for unitary_coupled_cluster , o objeto de estado deverá ter uma propriedade
cluster_operator cujo valor é um objeto JSON. O objeto JSON deve ter uma propriedade reference_state cujo
valor é um estado base. O objeto JSON pode ter uma propriedade one_body_amplitudes cujo valor é uma matriz
de operadores de cluster de um corpo e suas amplitudes. O objeto JSON pode ter uma propriedade
two_body_amplitudes cujo valor é uma matriz de operadores de cluster de dois corpos e suas amplitudes. que
contém uma matriz de estados de base e suas amplitudes não normalizadas.
Por exemplo, o estado $$ \ket{\Text{Reference}} = (um ^ {\dagger} _ {1, \uparrow}a ^ {\dagger} _ {2, \uparrow}a
^ {\dagger} _ {2, \downarrow}) \ket {0}, $$
$$ \ket{\text{UCCSD}}=e^{T-T^\dagger}\ket{\text{reference}}, $$
$ $ T = 0,1 a ^ {\dagger} _ {3, \uparrow}a _ {2, \downarrow} + 0,2 a ^ {\dagger} _ {2, \uparrow}a _ {2,
\downarrow}-0,3 a ^ {\dagger} _ {1, \uparrow}a ^ {\dagger} _ {3, \downarrow}a { _ 3, \uparrow}a _ {2,
\downarrow} $ $ é representado por

initial_state_suggestions: # optional. If not provided, spin-orbitals will be filled to minimize one-body


diagonal term energies.
- label: "UCCSD"
method: unitary_coupled_cluster
cluster_operator: # Initial state that cluster operator is applied to.
reference_state:
[1.0, "(1a)+", "(2a)+", "(2b)+", '|vacuum>']
one_body_amplitudes: # A one-body cluster term is t^{q}_{p} a^\dag_p a_q
- [0.1, "(3a)+", "(2b)"]
- [-0.2, "(2a)+", "(2b)"]
two_body_amplitudes: # A two-body unitary cluster term is t^{rs}_{pq} a^\dag_p a^\dag_q a_r a_s
- [-0.3, "(1a)+", "(3b)+", "(3a)", "(2b)"]

Objeto de conjunto de base


Esta seção é normativa.
Cada objeto de descrição do problema pode ter uma propriedade basis_set . Se houver, o valor da propriedade
basis_set deverá ser um objeto com duas propriedades type e name .

As funções base identificadas pelo valor da propriedade basis_set devem ser de valor real.

NOTE
A suposição de que todas as funções de base tenham valor real pode ser reduzida em versões futuras desta especificação.

Tabelas e Listas
Tabela 1. Unidades físicas permitidas
Esta seção é normativa.
Qualquer cadeia de caracteres especificando uma unidade deve ser uma das seguintes:
hartree
ev

Analisadores e produtores devem tratar os seguintes objetos de quantidade simples como equivalentes:

- {"units": "hartree", "value": 1}


- {"units": "ev", "value": 27.2113831301723}

Tabela 2. Convenções de índice permitidas


Esta seção é normativa.
Qualquer cadeia de caracteres especificando uma Convenção de índice deve ser uma das seguintes:
mulliken

Esta seção é informativa.


As convenções de índice adicionais podem ser introduzidas em versões futuras desta especificação.
Interpretação de convenções de índice
Esta seção é informativa.
Tabela 3. Métodos de estado permitidos
Esta seção é normativa.
Qualquer string especificando um método de estado DEVE ser uma das seguintes:
sparse_multi_configurational
unitary_coupled_cluster

Esta seção é informativa.


Métodos de estado adicionais podem ser introduzidos em versões futuras desta especificação.
Especificação Broombridge ver 2.1
01/05/2021 • 10 minutes to read

As palavras-chave "DEVE", "NÃO DEVE", "OBRIGATÓRIO", "PODE", "NÃO PODE", "DEVERIA", "NÃO DEVERIA",
"RECOMENDADO" "PODE" e "OPCIONAL" neste documento devem ser interpretadas conforme descritas na
RFC 2119.
Qualquer barra lateral com os títulos "OBSERVAÇÃO", "INFORMAÇÃO" ou "AVISO" é informativa.

Introdução
Esta seção é informativa.
Os documentos do Broombridge destinam-se a comunicar instâncias de problemas de simulação na química do
Quantum para processamento usando simulação de Quantum e programação cadeias.

Serialização
Esta seção é normativa.
Um documento Broombridge DEVE ser serializado como um documento YAML 1.2 que representa um objeto
JSON, conforme descrito em RFC 4627 seção 2.2. O objeto serializado para YAML DEVE ter uma propriedade
"$schema" cujo valor é
"https://raw.githubusercontent.com/Microsoft/Quantum/master/Chemistry/Schema/qchem-0.1.schema.json" e DEVE
ser válido de acordo com as especificações do rascunho do esquema JSON-06 [1, 2].
Para o restante desta especificação, "o objeto Broombridge" fará referência ao objeto JSON desserializado de
um documento Broombridge YAML.
A menos que indicado de outra forma explicitamente, os objetos NÃO DEVEM ter propriedades adicionais além
daquelas especificadas neste documento.

Definições adicionais
Esta seção é normativa.
Objetos de quantidade
Esta seção é normativa.
Um objeto de quantidade é um objeto JSON e DEVE ter uma propriedade units cujo valor seja um dos valores
permitidos listados na tabela 1.
Um objeto de quantidade é um objeto de quantidade simples se tiver uma única propriedade, value além de
sua própria units . O valor da propriedade value DEVE ser um número.
Um objeto de quantidade é um objeto de quantidade limitada se ele tiver propriedades lower upper além de
sua propriedade units . Os valores da lower e propriedades upper DEVEM ser números. Um objeto de
quantidade limitada PODE ter uma propriedade value cujo valor é um número.
Um objeto de quantidade é um objeto de quantidade de matriz esparsa se ele tiver uma propriedade format e
outra propriedade values além de sua própria units . O valor de format DEVE ser a cadeia de caracteres
sparse . O valor da propriedade values DEVE ser uma matriz. Cada elemento de values DEVE ser uma matriz
que representa os índices e o valor da quantidade da matriz esparsa.
Os índices para cada elemento de um objeto de quantidade de matriz esparsa DEVEM ser exclusivos em todo o
objeto de quantidade de matriz esparsa. Se um índice estiver presente com um valor de 0 , um analisador DEVE
tratar o objeto de quantidade de matriz esparsa de forma idêntica a um objeto de quantidade de matriz esparsa
no qual esse índice não está presente.
Um objeto de quantidade DEVE ser:
um objeto de quantidade simples,
um objeto de quantidade limitada, ou
um objeto de quantidade de matriz esparsa.
Exemplos
Esta seção é informativa.
Uma quantidade simples representando $1.9844146837 , \mathrm{Ha}$:

coulomb_repulsion:
value: 1.9844146837
units: hartree

Uma quantidade limitada que representa uma distribuição uniforme sobre o intervalo $[-7,-6] ,\mathrm{ha}$:

fci_energy:
upper: -6
lower: -7
units: hartree

A seguir está uma quantidade de matriz esparsa com um elemento [1, 2] igual a hello e um elemento
[3, 4] igual a world :

sparse_example:
format: sparse
units: hartree
values:
- [1, 2, "hello"]
- [3, 4, "world"]

Seção de formato
Esta seção é normativa.
O objeto Broombridge DEVE ter uma propriedade format cujo valor é um objeto JSON com uma propriedade
chamada version . A propriedade version DEVE ter o valor "0.1" .
Exemplo
Esta seção é informativa.

format: # required
version: "0.1" # must match exactly

Seção de conjuntos integrais


Esta seção é normativa.
O objeto Broombridge DEVE ter uma propriedade integral_sets cujo valor é uma matriz JSON. Cada item no
valor da propriedade integral_sets DEVE ser um objeto JSON que descreve um conjunto de integrais,
conforme descrito no restante desta seção. No restante desta seção, o termo "objeto de conjunto integral" fará
referência a um item no valor da propriedade integral_sets do objeto Broombridge.
Cada objeto de conjunto integral DEVE ter uma propriedade metadata cujo valor é um objeto JSON. O valor de
metadata pode ser o objeto JSON vazio (ou seja, {} ) ou pode conter propriedades adicionais definidas pelo
implementador.
Seção Hamiltonian
Visão geral
Esta seção é informativa.
A propriedade hamiltonian de cada objeto de conjunto integral descreve o hamiltoniano para um problema de
química do Quantum específico, listando seus termos de um e dois corpos como matrizes esparsas de números
reais. Os operadores hamiltonianos descritos por cada objeto de conjunto integral assumem o formulário
$$ H = \sum_{i,j}\sum_{\sigma\in\{\uparrow,\downarrow\}} h_{ij} a^{\dagger}_{i,\sigma} a_{ j,\sigma} + \frac{1}
{2}\sum_{i,j,k,l}\sum_{\sigma,\rho\in\{\uparrow,\downarrow\}} h_{ijkl} a^\dagger_{i,\sigma} a^\dagger_{k,\rho}
a_{l,\rho} a_{ j,\sigma}, $$
Aqui $h _ {ijkl} = (IJ | KL) $ na convenção Mulliken.
Para maior clareza, o termo de um elétron é
$$ h_{ij} = \int {\mathrm d}x \psi^*_i(x) \left(\frac{1}{2}\nabla^2 + \sum_{A}\frac{Z_A}{|x-x_A|} \right) \psi_j(x),
$$
e o termo de dois elétrons é
$$ h_{ijkl} = \iint {\mathrm d}x^2 \psi^{*}_i(x_1)\psi_j(x_1) \frac{1}{|x_1 -x_2|}\psi_k^{*}(x_2) \psi_l(x_2). $$
Conforme observado em nossa descrição da basis_set Propriedade de cada elemento da propriedade
integral_sets , supomos que as funções de base usadas sejam de valor real. Isso nos permite usar as seguintes
simetrias entre os termos para compactar a representação do Hamiltonian.
$$ h_{ijkl} = h_{ijlk}=h_{ jikl}=h_{ jilk}=h_{klij}=h_{klji}=h_{lkij}=h_{lkji}. $$
Sumário
Esta seção é normativa.
Cada objeto de conjunto integral DEVE ter uma propriedade hamiltonian cujo valor é um objeto JSON. O valor
da propriedade hamiltonian é conhecido como um objeto hamiltoniano e DEVE ter as propriedades,
one_electron_integrals two_electron_integrals conforme descrito no restante desta seção. Um objeto
hamiltoniano também pode ter uma propriedade particle_hole_representation . Se presente, o valor de
particle_hole_representation DEVE seguir o formato descrito no restante desta seção.

O b j et o In t eg r ai s d e u m el ét r o n

Esta seção é normativa.


A propriedade one_electron_integrals do objeto hamiltoniano DEVE ser uma quantidade de matriz esparsa
cujos índices são dois integrais e cujos valores são números. Cada termo DEVE ter índices [i, j] onde i >= j .

Isso reflete a simetria de que $h _ {IJ} = h_ { ji} $, que é uma consequência do fato de que o hamiltoniano é
hermitiano.

E x e mp l o

Esta seção é informativa.


A quantidade de matriz esparsa a seguir representa o hamiltoniano $$ H = \left(-5.0 (a^{\dagger}_{1,\uparrow}
a_{1,\uparrow}+a^{\dagger}_{1,\downarrow} a_{1,\downarrow})+ 0.17 (a^{\dagger}_{2,\uparrow}
a_{1,\uparrow}+ a^{\dagger}_{1,\uparrow} a_{2,\uparrow}+a^{\dagger}_{2,\downarrow} a_{1,\downarrow}+
a^{\dagger}_{1,\downarrow} a_{2,\downarrow})\right)\,\mathrm{Ha}. $$

one_electron_integrals: # required
units: hartree # required
format: sparse # required
values: # required
# i j f(i,j)
- [1, 1, -5.0]
- [2, 1, 0.17]

NOTE
Broombridge usa indexação com base em 1.

O b j et o s In t eg r ai s d e d o i s el ét r o n s

Esta seção é normativa.


A two_electron_integrals Propriedade do objeto hamiltoniano DEVE ser uma quantidade de matriz esparsa
com uma propriedade adicional chamada index_convention . Cada elemento do valor de
two_electron_integrals DEVE ter quatro índices.

Cada propriedade two_electron_integrals DEVE ter uma propriedade index_convention . O valor da


propriedade index_convention DEVE ser um dos valores permitidos listados na tabela 1. Se o valor de
index_convention for mulliken , para cada elemento da quantidade de matriz esparsa two_electron_integrals ,
um analisador que carrega um documento Broombridge DEVE criar uma instância de um termo hamiltoniano
igual ao operador de dois elétrons $h _ {i, j, k, l} a ^ \ dagger_i um ^ \ dagger_j a_k A_L $, em que $i $, $j $, $k $
e $l$ DEVEM ser inteiros no intervalo inclusivo de 1 ao número de elétrons especificado pela propriedade
n_electrons do objeto de conjunto integral, e onde $ h_ {i, j, k,l}$ é o elemento [i, j, k, l, h(i, j, k, l)] da
quantidade da matriz esparsa.
Si me t ri a s

Esta seção é normativa.


Se a propriedade index_convention de um objeto two_electron_integrals for igual a mulliken , se um
elemento com índices [i, j, k, l] estiver presente, os seguintes índices NÃO DEVERÃO estar presentes, a
menos que sejam iguais a [i, j, k, l] :
[i, j, l, k]
[j, i, k, l]
[j, i, l, k]
[k, l, i, j]
[k, l, j, i]
[l, k, j, i]

NOTE
Como a propriedade index_convention é um objeto de quantidade esparsa, nenhum índice pode ser repetido em
elementos diferentes. Em particular, se um elemento com índices [i, j, k, l] estiver presente, nenhum outro
elemento poderá ter esses índices.

E x e mp l o

Esta seção é informativa.


O objeto a seguir especifica o hamiltoniano
$ $ H = \frac12 \sum _ {\sigma, \rho\in \ {\uparrow, \downarrow \ }} \Biggr (1,6 a ^ {\dagger} _ {1, \sigma} a ^
{\dagger} _ {1, \rho} a _ {1, \rho} a _ {1, \sigma}-0,1 a ^ {\dagger} _ {6, \sigma} a ^ {\dagger} { _ 1, \rho} a _ {3,
\rho} a _ {2, \sigma}-0,1 a ^ {\dagger} _ {6, \sigma} a ^ {\dagger} _ {1, \rho} a _ {2, \rho} a _ {3, \sigma}-0,1 a ^
{\dagger} _ {1, \sigma} a ^ {\dagger} _ {6, \rho} a _ {3, \rho} a _ {2, \sigma}-0,1 a ^ {\dagger} _ {1, \sigma} a ^
{\dagger} _ {6, \rho} a _ {2, \rho} a _ {3, \sigma} $ $ $ $-0,1 a ^ {\dagger} _ {3, \sigma} a ^ {\dagger} _ {2, \rho} a _
{6, \rho} a _ {1, \sigma}-0,1 a ^ {\dagger} _ {3, \sigma} a ^ {\dagger} _ {2, \rho} a _ {1, \rho} a _ {6, \sigma}-0,1 a ^
{\dagger} _ {2, \sigma} a ^ {\dagger} _ {3 , \rho} a _ {6, \rho} a _ {1, \sigma}-0,1 a ^ {\dagger} _ {2, \sigma} a ^
{\dagger} _ {3, \rho} a _ {1, \Rho} a _ {6, \sigma}\Biggr) \, \textrm{ha}. $$

two_electron_integrals:
index_convention: mulliken
units: hartree
format: sparse
values:
- [1, 1, 1, 1, 1.6]
- [6, 1, 3, 2, -0.1]

P a r t í c u l a - o r i fí c i o : o b j e t o d e r e p r e se n t a ç ã o

Esta seção é normativa.


O objeto de representação partícula-orifício especifica que os integrais armazenados são definidos de acordo
com a representação de partículas-orifícios, em que os operadores de criação e aniquilação descrevem
excitations fora do estado de referência usado, como um estado Hartree-Fock. O objeto é OPCIONAL Se o
objeto não for especificado, o hamiltoniano será interpretado como não fornecido na representação de
partículas-orifício. Se presente, o valor de particle_hole_representation DEVE ser um objeto de quantidade de
matriz esparsa cujos índices são quatro inteiros e cujos valores são um número e uma cadeia de caracteres. A
parte da cadeia de caracteres do valor de cada elemento DEVE conter somente os caracteres '+' e '-' . Esses
caracteres especificam se um determinado fator é um operador de criação ou aniquilação na representação
partícula-orifício Por exemplo, "-+++" corresponde a um orifício que está sendo criado no local $i$ e as
partículas que estão sendo criadas nos locais $j,k$ e $l$.

NOTE
Como o valor de particle_hole_representation é um objeto de quantidade de matriz esparsa, as propriedades unit
e format DEVEM ser especificadas. As unidades aceitáveis incluem as listadas na tabela 1. A propriedade format é
necessária e indica se os coeficientes hamiltonianos são especificados como uma matriz densa ou esparsa. Na versão atual,
somente as matrizes esparsas têm suporte, com a interpretação de que todos os elementos não especificados são $0$,
mas as versões futuras podem adicionar suporte para valores adicionais da propriedade format .

Seção de estado inicial


O objeto initial_state_suggestion especifica os estados quânticos inicial de interesse para o hamiltoniano
especificado. Esse objeto DEVE ser uma matriz de objetos state JSON.
Objeto de estado
Cada estado representa uma superposição de órbitas ocupadas. Cada objeto de estado DEVE ter uma
propriedade label que contém uma cadeia de caracteres. Cada objeto de estado DEVERÁ ter uma propriedade
superposition que contém uma matriz de estados de base e suas amplitudes não normalizadas.

Por exemplo, os estados iniciais $$ \ket{G0}=\ket{G1}=\ket{G2}=


(a^{\dagger}_{1,\uparrow}a^{\dagger}_{2,\uparrow}a^{\dagger}_{2,\downarrow})\ket{0} $$ $$ \ket{E}=\frac{0.1
(a^{\dagger}_{1,\uparrow}a^{\dagger}_{2,\uparrow}a^{\dagger}_{2,\downarrow})+0.2
(a^{\dagger}_{1,\uparrow}a^{\dagger}_{3,\uparrow}a^{\dagger}_{2,\downarrow})}
{\sqrt{|0.1|^2+|0.2|^2}}\ket{0} $$, são representados por

initial_state_suggestions: # optional. If not provided, spin-orbitals will be filled to minimize one-body


diagonal term energies.
- state:
label: "|G0>"
superposition:
- [1.0, "(1a)+","(2a)+","(2b)+","|vacuum>"]
- state:
label: "|G1>"
superposition:
- [-1.0, "(2a)+","(1a)+","(2b)+","|vacuum>"]
- state:
label: "|G2>"
superposition:
- [1.0, "(3a)","(1a)+","(2a)+","(3a)+","(2b)+","|vacuum>"]
- state:
label: "|E>"
superposition:
- [0.1, "(1a)+","(2a)+","(2b)+","|vacuum>"]
- [0.2, "(1a)+","(3a)+","(2b)+","|vacuum>"]

Objeto de conjunto de base


Esta seção é normativa.
Cada objeto de conjunto integral pode ter uma basis_set propriedade. Se houver, o valor da propriedade
basis_set DEVERÁ ser um objeto com duas propriedades type e name .

As funções base identificadas pelo valor da propriedade basis_set DEVEM ser de valor real.

NOTE
A suposição de que todas as funções de base tenham valor real pode ser reduzida em versões futuras desta especificação.

Tabelas e Listas
Tabela 1. Unidades físicas permitidas
Esta seção é normativa.
Qualquer cadeia de caracteres especificando uma unidade DEVE ser uma das seguintes:
hartree
ev

Analisadores e produtores DEVEM tratar os seguintes objetos de quantidade simples como equivalentes:

- {"units": "hartree", "value": 1}


- {"units": "ev", "value": 27.2113831301723}

Tabela 2. Convenções de índice permitidas


Esta seção é normativa.
Qualquer cadeia de caracteres especificando uma convenção de índice DEVE ser uma das seguintes:
mulliken

Esta seção é informativa.


As convenções de índice adicionais podem ser introduzidas em versões futuras desta especificação.
Interpretação de convenções de índice
Esta seção é informativa.
Introdução à Biblioteca de Aprendizado de
Máquina Quântico
15/04/2021 • 2 minutes to read

A Biblioteca de Machine Learning do Quantum é uma API escrita em Q# que permite executar experimentos de
machine learning quântico/clássico híbrido. A biblioteca possibilita:
Carregar seus próprios dados para classificação com simuladores quânticos
Usar exemplos e tutoriais para ter uma introdução ao campo do aprendizado de máquina quântico
Você pode esperar um baixo desempenho em comparação com as estruturas clássicas de aprendizado de
máquina atuais (lembre-se de que tudo é executado além da simulação de um dispositivo quântico que já
consome muitos recursos de computação).
A finalidade desta documentação é:
Fazer uma apresentação concisa das ferramentas de aprendizado de máquina (escritas em Q#) para
aprendizado quântico/clássico híbrido.
Apresentar conceitos de aprendizado de máquina e, especificamente, sua realização em classificadores
centrados no circuito quântico (também conhecidos como classificadores sequenciais quânticos).
Fornecer um conjunto de tutoriais com as noções básicas para começar a usar as ferramentas fornecidas
pela biblioteca.
Discutir os métodos de treinamento e validação desses classificadores e como eles são convertidos nas
operações em Q# específicas fornecidas pela biblioteca.
O modelo implementado nesta biblioteca é baseado no esquema de treinamento quântico clássico apresentado
em Classificadores quânticos centrados no circuito
Introdução ao machine learning do Quantum
19/05/2021 • 5 minutes to read

Estrutura e metas
A codificação e o processamento de informações quantum são uma alternativa poderosa a classificadores
clássicos de machine learning do Quantum. Em particular, ele nos permite codificar dados em registros
quantum que são concisos em relação ao número de recursos, empregando sistematicamente o entrelaçamento
quantum como recurso computacional e empregando a medição quantum para a inferência de classes. O
classificador quantum centrado em circuito é uma solução quantum relativamente simples que combina a
codificação de dados com um circuito quantum de entrelaçamento/desentrelaçamento rápido seguido por
medida para inferir rótulos de classe de exemplos de dados. A meta é garantir a caracterização clássica e o
armazenamento de circuitos de entidades, bem como treinamento híbrido/clássico quantum dos parâmetros de
circuito, mesmo para espaços de recursos muito grandes.

Arquitetura do classificador
A classificação é uma tarefa de machine learning supervisionada, em que a meta é inferir rótulos de classe
${y_1,y_2,\ldots,y_d}$ de determinadas amostras de dados. O "conjunto de dados de treinamento" é uma
coleção de exemplos $\mathcal{D}={(x,y)}$ com rótulos previamente atribuídos conhecidos. Aqui, $x$ é um
exemplo de dados e $y$ é seu rótulo conhecido chamado "rótulo de treinamento". De um modo semelhante aos
métodos tradicionais, a classificação quantum consiste em três etapas:
codificação de dados
preparação de um estado de classificador
medição Devido à natureza probabilística da medição, essas três etapas devem ser repetidas várias vezes. A
codificação e a computação do estado do classificador são feitas por meio de circuitos quantum. Embora o
circuito de codificação seja geralmente controlado por dados e sem parâmetros, o circuito do classificador
contém um conjunto suficiente de parâmetros de aprendizado.
Na solução proposta, o circuito do classificador é composto por rotações de qubit único e rotações controladas
de dois qubit. Os parâmetros que mais podem ser aprendidos aqui são os ângulos de rotação. A rotação e os
portões de rotação controlada são conhecidos como universais para computação quântica, o que significa que
qualquer matriz de peso unitário pode ser decomposta em um circuito suficientemente longo que consiste
nesses portões.
Na versão proposta, há suporte para apenas um circuito seguido por uma só estimativa de frequência. Portanto,
a solução é uma analogia de quantum de um computador de vetor de suporte com um kernel polinomial de
baixo grau.
Um design de classificador quantum simples pode ser comparado a uma solução de SVM (computador de vetor
de suporte) tradicional. A inferência para um exemplo de dados $x$ no caso de SVM é feita usando uma forma
de kernel ideal $\sum \alpha_j k(x_j,x)$, em que $k$ é uma determinada função de kernel.
Por outro lado, um classificador quantum usa o preditor $p(y│x,U(\theta))= U(\theta)x|M|U(\theta)x $, que é
semelhante em espírito, mas tecnicamente muito diferente. Assim, quando uma codificação de amplitude
simples é usada, $p(y│x,U(\theta))$ é uma forma quadrática nas amplitudes de $x$, mas os coeficientes dessa
forma não são mais aprendidos de maneira independente. Em vez disso, eles são agregados dos elementos de
matriz do circuito $U(\theta)$, que normalmente tem um número significativamente menor de parâmetros que
podem ser aprendidos $\theta$ que a dimensão do vetor $x$. O grau polinomial de $p(y│x,U(\theta))$ nos
recursos originais pode ser aumentado para US$2^l$ usando uma codificação de produto quantum em $l$
cópias de $x$.
Nossa arquitetura explora circuitos relativamente superficiais, que, portanto, devem ser rapidamente
entrelaçados para capturar todas as correlações entre os recursos de dados em todos os intervalos. Um
exemplo do componente de circuito de entrelaçamento rápido mais útil é mostrado na figura abaixo. Embora
um circuito com essa geometria seja composto por apenas $3 n+1$ portões, a matriz de peso unitário que ele
calcula garante uma conversa cruzada significativa entre os recursos $2^n$.

O circuito no exemplo acima consiste em seis portões de qubit único $(G_1,\ldots,G_5; G_{16})$ e dez portões
de dois qubits $(G_6,\ldots,G_{15})$. Supondo que cada um dos portões seja definido com um parâmetro que
pode ser aprendível, temos 16 parâmetros de aprendíveis, enquanto a dimensão do espaço Hilbert de 5 qubits é
32. Essa geometria de circuito pode ser facilmente generalizada para qualquer registro $n$-qubit, quando $n$ é
ímpar, produzindo circuitos com $3 n+1$ parâmetros para o espaço de recurso de $2^n$ dimensional.

Treinamento de classificador como uma tarefa de aprendizado


supervisionada
O treinamento de um modelo classificador envolve encontrar valores ideais de seus parâmetros operacionais,
de modo que eles maximizem a probabilidade média de inferir os rótulos de treinamento corretos em todos os
exemplos de treinamento. Aqui, nos preocupamos apenas com a classificação de dois níveis, por exemplo, o
caso de $d=2$ e apenas duas classes com os rótulos $y_1, y_2$.

NOTE
Uma forma de generalizar nossos métodos para o número arbitrário de classes é substituir qubits por qudits, que são
unidades quantum com estados $d$, e a medida de duas vias pela medida de $d$ vias.

Probabilidade da meta de treinamento


Dado um circuito quantum que pode ser aprendido $U(\theta)$, em que $\theta$ é um vetor de parâmetros, e
denotando a medição final por $M$, a probabilidade média da inferência de rótulo correta é $$ \begin{align}
\mathcal{L}(\theta)=\frac{1}{|\mathcal{D}|} \left( \sum_{(x,y_1)\in\mathcal{D}} P(M=y_1|U(\theta) x) +
\sum_{(x,y_2)\in\mathcal{D}} P(M=y_2|U(\theta) x)\right) \end{align} $$ em que $P(M=y|z)$ é a probabilidade
de medir $y$ no estado quantum $z$. Aqui, é suficiente entender que a função de probabilidade $\mathcal{L}
(\theta)$ é adequada em $\theta$ e seu derivativo em qualquer $\theta_j$ pode ser computado, essencialmente,
pelo mesmo protocolo quantum usado para computar a própria função de probabilidade. Isso permite otimizar
os descendentes de $\mathcal{L}(\theta)$ por gradiente.
Tendência do classificador e pontuação de treinamento
Dado alguns valores intermediários (ou finais) dos parâmetros em $\theta$, precisamos identificar um só valor
real $b$ conhecido como desvio do classificador para fazer a inferência. A regra de inferência de rótulo funciona
da seguinte maneira:
Uma amostra $x$ recebe o rótulo $y_2$ se, e apenas se, $P(M=y_2|U(\theta) x) + b > 0.5$ (RULE1) (caso
contrário, receberá o rótulo $y_1$)
Claramente, $b$ deve estar no intervalo $(-0,5,+0,5)$ para ser significativo.
Um caso de treinamento $(x,y) em \mathcal{D} $ é considerado uma classificação inadequada, considerando o
desvio $b$ se o rótulo inferido para $x$ de acordo com RULE1 for, na verdade, diferente de $y$. O número total
de classificações incorretas é a pontuação de treinamento do classificador dado o desvio o $b$. O desvio do
classificador ideal $b$ minimiza a pontuação de treinamento. É fácil ver que, considerando as estimativas de
probabilidade pré-computadas ${ P(M=y_2 U(\theta) x)   (x,*)\in\mathcal{D} }$, o desvio do classificador ideal
pode ser encontrado pela pesquisa binária no intervalo $(-0,5,+0,5)$ fazendo no máximo $\log_2( 
\mathcal{D} )$ etapas.
Referência
Essas informações devem ser suficientes para começar a experimentar com o código. No entanto, se você quiser
saber mais sobre esse modelo, leia a proposta original: 'Classificadores quantum centrados em circuitos', Maria
Schuld, Alex Bocharov, Krysta Svore e Nathan Wiebe
Além do exemplo de código que você verá nas próximas etapas, você também pode começar a explorar a
classificação quantum neste tutorial
Classificação básica: classificar dados com o QDK
28/04/2021 • 7 minutes to read

Neste Início rápido, você saberá como executar um classificador sequencial quântico escrito em Q# usando a
biblioteca Quantum Machine Learning do QDK.
Neste guia, usaremos o conjunto de dados de meia lua, usando uma estrutura de classificador definida em Q#.

Pré-requisitos
O Microsoft Quantum development kit.
A criação de um projeto Q# com um programa de host em Python ou com um programa de host em C#.

Programa de host
O programa de host consiste em três partes:
Carregar o conjunto de dados e escolher um conjunto de parâmetros iniciais para seu modelo.
Executar o treinamento para determinar os parâmetros e o desvio do modelo.
Validar o modelo para determinar sua exatidão
Python com Visual Studio Code ou a linha de comando
C# com Visual Studio Code ou a linha de comando
C# com Visual Studio 2019

Para executar o classificador Q# em Python, salve o seguinte código como host.py . Lembre-se de que
você também precisa do Q# arquivo Training.qs explicado posteriormente neste tutorial.
import json

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
plt.style.use('ggplot')

import qsharp

from Microsoft.Quantum.Samples import (


TrainHalfMoonModel, ValidateHalfMoonModel, ClassifyHalfMoonModel
)

if __name__ == "__main__":
with open('data.json') as f:
data = json.load(f)
parameter_starting_points = [
[0.060057, 3.00522, 2.03083, 0.63527, 1.03771, 1.27881, 4.10186, 5.34396],
[0.586514, 3.371623, 0.860791, 2.92517, 1.14616, 2.99776, 2.26505, 5.62137],
[1.69704, 1.13912, 2.3595, 4.037552, 1.63698, 1.27549, 0.328671, 0.302282],
[5.21662, 6.04363, 0.224184, 1.53913, 1.64524, 4.79508, 1.49742, 1.545]
]

(parameters, bias) = TrainHalfMoonModel.simulate(


trainingVectors=data['TrainingData']['Features'],
trainingLabels=data['TrainingData']['Labels'],
initialParameters=parameter_starting_points
)

miss_rate = ValidateHalfMoonModel.simulate(
validationVectors=data['ValidationData']['Features'],
validationLabels=data['ValidationData']['Labels'],
parameters=parameters, bias=bias
)

print(f"Miss rate: {miss_rate:0.2%}")

# Classify the validation so that we can plot it.

Em seguida, execute o programa host do Python na linha de comando:

$ python host.py
Preparing Q# environment...
[...]
Observed X.XX% misclassifications.

Código do classificador Q#
Agora, vejamos como as operações invocadas pelo programa de host estão definidas em Q#. Salvamos o
seguinte código em um arquivo chamado Training.qs .

namespace Microsoft.Quantum.Samples {
open Microsoft.Quantum.Convert;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Arrays;
open Microsoft.Quantum.MachineLearning;
open Microsoft.Quantum.Math;

function WithProductKernel(scale : Double, sample : Double[]) : Double[] {


return sample + [scale * Fold(TimesD, 1.0, sample)];
return sample + [scale * Fold(TimesD, 1.0, sample)];
}

function Preprocessed(samples : Double[][]) : Double[][] {


let scale = 1.0;

return Mapped(
WithProductKernel(scale, _),
samples
);
}

function DefaultSchedule(samples : Double[][]) : SamplingSchedule {


return SamplingSchedule([
0..Length(samples) - 1
]);
}

function ClassifierStructure() : ControlledRotation[] {


return [
ControlledRotation((0, new Int[0]), PauliX, 4),
ControlledRotation((0, new Int[0]), PauliZ, 5),
ControlledRotation((1, new Int[0]), PauliX, 6),
ControlledRotation((1, new Int[0]), PauliZ, 7),
ControlledRotation((0, [1]), PauliX, 0),
ControlledRotation((1, [0]), PauliX, 1),
ControlledRotation((1, new Int[0]), PauliZ, 2),
ControlledRotation((1, new Int[0]), PauliX, 3)
];
}

operation TrainHalfMoonModel(
trainingVectors : Double[][],
trainingLabels : Int[],
initialParameters : Double[][]
) : (Double[], Double) {
let samples = Mapped(
LabeledSample,
Zipped(Preprocessed(trainingVectors), trainingLabels)
);
Message("Ready to train.");
let (optimizedModel, nMisses) = TrainSequentialClassifier(
Mapped(
SequentialModel(ClassifierStructure(), _, 0.0),
initialParameters
),
samples,
DefaultTrainingOptions()
w/ LearningRate <- 0.1
w/ MinibatchSize <- 15
w/ Tolerance <- 0.005
w/ NMeasurements <- 10000
w/ MaxEpochs <- 16
w/ VerboseMessage <- Message,
DefaultSchedule(trainingVectors),
DefaultSchedule(trainingVectors)
);
Message($"Training complete, found optimal parameters: {optimizedModel::Parameters}");
return (optimizedModel::Parameters, optimizedModel::Bias);
}

operation ValidateHalfMoonModel(
validationVectors : Double[][],
validationLabels : Int[],
parameters : Double[],
bias : Double
) : Double {
let samples = Mapped(
LabeledSample,
Zipped(Preprocessed(validationVectors), validationLabels)
Zipped(Preprocessed(validationVectors), validationLabels)
);
let tolerance = 0.005;
let nMeasurements = 10000;
let results = ValidateSequentialClassifier(
SequentialModel(ClassifierStructure(), parameters, bias),
samples,
tolerance,
nMeasurements,
DefaultSchedule(validationVectors)
);
return IntAsDouble(results::NMisclassifications) / IntAsDouble(Length(samples));
}

operation ClassifyHalfMoonModel(
samples : Double[][],
parameters : Double[],
bias : Double,
tolerance : Double,
nMeasurements : Int
)
: Int[] {
let model = Default<SequentialModel>()
w/ Structure <- ClassifierStructure()
w/ Parameters <- parameters
w/ Bias <- bias;
let features = Preprocessed(samples);
let probabilities = EstimateClassificationProbabilities(
tolerance, model,
features, nMeasurements
);
return InferredLabels(model::Bias, probabilities);
}

As funções e operações mais importantes definidas no código acima são:


ClassifierStructure() : ControlledRotation[] : nessa função, definimos a estrutura do nosso modelo de
circuito adicionando as camadas dos portões controlados que consideramos. Esta etapa é análoga à
declaração de camadas de neurônios em um modelo de aprendizado profundo sequencial.
TrainHalfMoonModel() : (Double[], Double) : esta operação é a parte principal do código e define o
treinamento. Aqui, carregamos os exemplos do conjunto de conjuntos incluído na biblioteca, definimos os
parâmetros hyper e os parâmetros iniciais para o treinamento e iniciamos o treinamento chamando a
operação TrainSequentialClassifier incluída na biblioteca. Ele gera os parâmetros e o desvio que
determinam o classificador.
ValidateHalfMoonModel(parameters : Double[], bias : Double) : Int : esta operação define o processo de
validação para avaliar o modelo. Aqui, carregamos os exemplos para validação, o número de medições por
amostra e a tolerância. Ele gera o número de classificações incorretas no lote escolhido de amostras para
validação.

Próximas etapas
Primeiro, você pode mexer no código e tentar alterar alguns parâmetros para ver como afeta o treinamento. Em
seguida, no próximo tutorial, Crie seu próprio classificador, você aprenderá a definir a estrutura do classificador.
Criar seu classificador
18/05/2021 • 2 minutes to read

Neste guia, você aprenderá os conceitos básicos por trás do design de modelos de circuito para o classificador
centrado no circuito quântico.
Como no aprendizado profundo clássico, não há nenhuma regra geral para escolher uma arquitetura específica.
Dependendo do problema, algumas arquiteturas terão um desempenho melhor do que outras. Mas há alguns
conceitos que podem ser úteis ao criar o circuito:
Um grande número de parâmetros implica um modelo mais flexível, que pode ser adequado para
desenhar limites de classificação complicados, mas que também podem ser mais suscetíveis a
sobreajuste.
Portões Entangling entre qubits são essenciais para capturar as correlações entre os recursos quânticos.

Como criar um classificador com Q#


Para criar um classificador, vamos concatenar as rotações controladas parametrizadas em nosso modelo de
circuito. Para fazer isso, podemos usar o tipo ControlledRotation definido na biblioteca de Machine Learning do
Quantum. Esse tipo aceita quatro argumentos que determinam: o índice do qubit de destino, a matriz de índices
dos qubits de controle, o eixo de rotação e o índice do parâmetro associado na matriz de parâmetros que
definem o modelo.
Vejamos um exemplo de classificador. No exemplo de meia lua, podemos encontrar o classificador a seguir
definido no arquivo Training.qs .

function ClassifierStructure() : ControlledRotation[] {


return [
ControlledRotation((0, new Int[0]), PauliX, 4),
ControlledRotation((0, new Int[0]), PauliZ, 5),
ControlledRotation((1, new Int[0]), PauliX, 6),
ControlledRotation((1, new Int[0]), PauliZ, 7),
ControlledRotation((0, [1]), PauliX, 0),
ControlledRotation((1, [0]), PauliX, 1),
ControlledRotation((1, new Int[0]), PauliZ, 2),
ControlledRotation((1, new Int[0]), PauliX, 3)
];
}

O que estamos definindo aqui é uma função que retorna uma matriz de ControlledRotation elementos, que
junto com uma matriz de parâmetros e uma tendência definirão nosso SequentialModel . Esse tipo é
fundamental na biblioteca de Machine Learning do Quantum e define o classificador. O circuito definido na
função acima faz parte de um classificador no qual cada amostra do conjunto de dados contém dois recursos.
Portanto, precisamos apenas de dois qubits. A representação gráfica do circuito é:
Observe que, por padrão, as operações da biblioteca de Machine Learning do Quantum medem o último qubit
do registro a estimar as probabilidades de classificação. Você deve ter em mente esse fato ao projetar seu
circuito.

Usar as funções de biblioteca para gravar camadas de portões


Suponha que tenhamos um conjunto de dados com 784 recursos por instância, por exemplo, imagens de
28×28 pixels, como o conjunto de dados do MNIST. Nesse caso, a largura do circuito se torna grande o
suficiente para que a gravação manual de cada portão se torne uma tarefa possível, mas impraticável. É por isso
que a biblioteca de Machine Learning do Quantum fornece um conjunto de ferramentas para gerar
automaticamente camadas de rotações parametrizadas. Por exemplo, a função LocalRotationsLayer retorna
uma matriz de rotações de qubit simples não controladas ao longo de um determinado eixo, com uma rotação
para cada qubit no registro, cada uma parametrizada por um parâmetro de modelo diferente. Por exemplo,
LocalRotationsLayer(4, X) retorna o seguinte conjunto de portões:

Recomendamos que você explore a referência de API da biblioteca de Machine Learning do Quantum para
descobrir todas as ferramentas disponíveis para simplificar o design do circuito.

Próximas etapas
Experimente estruturas diferentes nos exemplos fornecidos. Você vê as alterações no desempenho do modelo?
No próximo tutorial, Load your own datasets , você aprenderá a carregar conjuntos de dados para experimentar
e explorar novas arquiteturas de classificadores.
Carregar e classificar seus próprios conjuntos de
dados
13/05/2021 • 2 minutes to read

Neste breve tutorial, vamos aprender como carregar seu próprio conjunto de dados para treinar um modelo
classificador com o QDK (Quantum Development Kit).
É altamente recomendável o uso de um formato de serialização padronizado, como arquivos JSON, para
armazenar seus dados. Esses formatos oferecem alta compatibilidade com estruturas diferentes, como Python e
o ecossistema .NET. Em especial, recomenda-se usar nosso modelo para carregar os dados, para que você possa
copiar e colar o código diretamente dos exemplos.

Modelo para carregar os conjuntos de dados


Suponha que tenhamos um conjunto de dados de treinamento $(x, y)$ de tamanho $N=2$, em que cada
instância $x i$ de $x$ tem três recursos: $x{i1}$, $x_{i2}$ e $x_{i3}$. O conjunto de dados de validação tem a
mesma estrutura. Esses conjuntos de dados podem ser representados por um arquivo data.json semelhante
ao seguinte:
{
"TrainingData": {
"Features": [
[
x_11,
x_12,
x_13
],
[
x_21,
x_22,
x_23
]
],
"Labels": [
y_1,
y_2
]
},
"ValidationData": {
"Features": [
[
xv_11,
xv_12,
xv_13
],
[
xv_11,
xv_12,
xv_13
]
],
"Labels": [
yv_1,
yv_2
]
}
}

Exemplo usando o modelo


Suponha que tenhamos um pequeno conjunto de dados com as alturas e os pesos de diferentes gatos e
cachorros. Esse conjunto de dados é muito pequeno para treinar um modelo, mas será suficiente para mostrar o
processo de carga de um conjunto de dados.

A LT URA ( M ) P ESO ( KG) A N IM A L

0,54 30 Cachorro

0,30 8 Gato

0.91 44 Cachorro

0,86 31 Cachorro

0,32 5 Gato

0,25 4 Gato

O processo é:
Primeiro, precisamos separar o conjunto de dados em treinamento e validação. Nesse caso, podemos usar os
três primeiros exemplos para treinamento e o restante dos exemplos para validação. Em geral, é uma boa
prática para fazer amostras aleatoriamente do conjunto de dados de treinamento e validação, para evitar
desvios indesejados nos dados de treinamento.
Em seguida precisamos atribuir um rótulo numérico a cada classe. Observe que por enquanto, a biblioteca
QML admite apenas problemas de classificação binária. Portanto, atribuímos o rótulo 0 à classe Dog e o
número 1 à classe Cat .
Por fim, preenchemos o modelo usando os dados do nosso conjunto de dados. Observe que para grandes
conjuntos de dados, você deve criar um pequeno script para gerar automaticamente o modelo de seu
conjunto de dados específico. Esse script dependerá do formato original do conjunto de dados.
Para nosso conjunto de dados, o arquivo data.json é:

{
"TrainingData": {
"Features": [
[
0.54,
30
],
[
0.30,
8
],
[
0.91,
44
]
],
"Labels": [
0,
1,
0
]
},
"ValidationData": {
"Features": [
[
0.86,
31
],
[
0.32,
5
]
[
0.25,
4
]
],
"Labels": [
0,
1,
1
]
}
}

Carregando os dados
Depois que os dados forem serializados como um arquivo JSON, carregue o arquivo usando as bibliotecas
JSON, fornecidas com a linguagem do host que você escolheu.
Python
C#

O Python fornece o pacote json integrado para trabalhar com dados serializados em JSON:

import json

data = json.load(f)
parameter_starting_points = [
[0.060057, 3.00522, 2.03083, 0.63527, 1.03771, 1.27881, 4.10186, 5.34396],

Próximas etapas
Agora você está pronto para começar a executar seus próprios experimentos com seus próprios conjuntos de
dados. Experimente classificadores e conjuntos de dados diferentes e compartilhe seus resultados para
contribuir com a comunidade!
Glossário de Machine Learning Quantum
13/05/2021 • 3 minutes to read

O treinamento de um classificador de quantum centrado em circuito é um processo com muitas partes móveis
que exigem a mesma (ou um pouco maior) quantidade de calibragem por tentativa e erro que o treinamento de
classificadores tradicionais. Neste artigo definimos os principais conceitos e ingredientes deste processo de
treinamento.

Agendamentos de testes/treinamentos
No contexto do treinamento do classificador, uma agenda descreve um subconjunto de exemplos de dados em
um treinamento geral ou conjunto de teste. Uma agenda geralmente é definida como uma coleção de índices de
exemplo.

Pontuações de parâmetros/desvios
Dado um vetor de parâmetro candidato e um desvio de classificador, sua pontuação de validação é medida em
relação a uma agenda de validação escolhida S, expressa por um número de classificações incorretas contadas
em todos os exemplos na agenda S.

Hiperparâmetros
O processo de treinamento do modelo é regido por determinados valores predefinidos chamados
hiperparâmetros:
Taxa de aprendizado
Este é um dos hiperparâmetros. Ele define o quanto a estimativa do gradiente estocástico atual impacta a
atualização do parâmetro. O tamanho da diferença da atualização de parâmetros é proporcional à taxa de
aprendizado. Valores menores de taxa de aprendizado (TA) levam à evolução mais lenta de parâmetros e à
convergência mais lenta, mas valores excessivamente grandes de TA podem quebrar a convergência por
completo, já que o gradiente descendente nunca se compromete com um mínimo local específico. Embora a
taxa de aprendizado seja ajustada de forma adaptativa (até certo ponto) pelo algoritmo de treinamento, é
importante selecionar um bom valor inicial para ela. Um valor inicial padrão comum para a taxa de aprendizado
é 0,1. A seleção do melhor valor da taxa de aprendizado é uma arte (consulte, por exemplo, a seção 4.3 de
Goodfellow et al., "Aprendizagem profunda, MIT Press, 2017).
Tamanho do minilote
Define quantas amostras de dados são usadas para uma única estimativa de gradiente estocástico. Valores
maiores de tamanho de minilote geralmente levam a uma convergência mais robusta e monotônica, mas
podem retardar o processo de treinamento, pois o custo de qualquer estimativa de gradiente é proporcional ao
tamanho do minilote. Um valor padrão comum para o tamanho do minilote é 10.
Épocas de treinamento, tolerância, travamentos
"Época" significa uma passagem completa dos dados de treinamento agendados. O número máximo de épocas
por um thread de treinamento (veja abaixo) deve ser limitado. O thread de treinamento é definido para terminar
(com os melhores parâmetros candidatos conhecidos) quando o número máximo de épocas é executado. No
entanto, esse treinamento terminará antes quando a taxa de classificação incorreta na agenda de validação cair
abaixo de uma tolerância escolhida. Vamos supor, por exemplo, que a tolerância de classificação incorreta seja
0,01 (1%). Se no conjunto de validação de 2.000 amostras observarmos menos de 20 classificações incorretas, o
nível de tolerância foi alcançado. Um thread de treinamento também será encerrado prematuramente se a
pontuação de validação do modelo candidato não tiver mostrado nenhuma melhoria em várias épocas
consecutivas (um travamento). No momento, a lógica para o término do travamentos está inserida no código-
fonte.
Contagem de medidas
Estimar as pontuações de treinamento/validação e os componentes do gradiente estocástico em um dispositivo
quântico equivale a estimar sobreposições de estados de quantum que requerem várias medições dos
observáveis apropriados. O número de medições deve ser dimensionado como $O(1/\epsilon^2)$, em que
$\epsilon$ é o erro de estimativa desejado. Como regra geral, a contagem das medições iniciais pode ser de
aproximadamente $1/\mbox{tolerância}^2$ (consulte a definição de tolerância no parágrafo anterior). Será
necessário revisar a contagem de medição para cima se o gradiente descendente parecer muito irregular e a
convergência muito difícil de ser alcançada.
Threads de treinamento
A função de semelhança, que é a utilidade do treinamento para o classificador, raramente é convexa. Isso
significa que geralmente existe uma infinidade de ótimos locais no espaço de parâmetros que podem diferir
significativamente em qualidade. Como o processo SGD pode convergir para apenas um ideal, é importante
explorar vários vetores de parâmetro iniciais. A prática comum no aprendizado de máquina é inicializar
aleatoriamente esses vetores iniciais. A API de treinamento Q# aceita uma matriz arbitrária dos vetores iniciais,
mas o código subjacente os explora em sequência. Em um computador de vários núcleos ou em qualquer
arquitetura de computação paralela, aconselha-se executar várias chamadas para a API de treinamento Q# em
paralelo com inicializações de parâmetros diferentes nas chamadas.
Como modificar os hiperparâmetros
Na biblioteca QML, a melhor maneira de modificar os hiperparâmetros é substituindo os valores padrões de
UDT TrainingOptions . Para fazer isso, chamamos ele com a função DefaultTrainingOptions e aplicamos o
operador w/ para substituir os valores padrões. Por exemplo, para usar 100.000 medições e uma taxa de
aprendizagem de 0,01:

let options = DefaultTrainingOptions()


w/ LearningRate <- 0.01
w/ NMeasurements <- 100000;
Introdução à Biblioteca de Numéricos do Quantum
15/04/2021 • 2 minutes to read

Muitos algoritmos quantum dependem de oracles que avaliam as funções matemáticas em uma superposição
de entradas. O principal componente do algoritmo de Shor, por exemplo, avalia $f(x) = a^x\operatorname{mod}
N$ para um $a$ fixo, o número para fatorar $N$ e um inteiro de $x$ a $2n$ qubits em uma superposição
uniforme em todas as cadeias de caracteres de $2n$ bits.
Para executar o algoritmo de Shor em um computador quantum real, essa função precisa ser escrita em termos
das operações nativas do computador de destino. Usando a representação binária de $x$ com $x_i$ denotando
a contagem de bits $i$-ésimos do bit menos significativo, $f(x)$ pode ser escrito como $f(x) =
a^{\sum_{i=0}^{2n-1} x_i 2^i} \operatorname{mod} N$. Por sua vez, isso pode ser escrito como um produto
(mod N) de termos $a^{2^i x_i}=(a^{2^i})^{x_i}$. A função $f(x)$ pode, portanto, ser implementada usando
uma sequência de multiplicações $2n$ (modulares) por $a^{2^i}$ condicional em $x_i$ sendo diferente de
zero. As constantes $a^{2^i}$ podem ser pré-calculadas e de módulo N reduzido antes da execução do
algoritmo.
Essa sequência de multiplicações modulares controladas pode ser simplificada ainda mais: Cada multiplicação
pode ser efetuada usando uma sequência de $n$ adições modulares controladas; e cada adição modular pode
ser criada com base em uma adição regular e um comparador.
Considerando que muitas etapas são necessárias para se chegar a uma implementação real, seria extremamente
útil ter essa funcionalidade disponível desde o início. É por isso que o Quantum Development Kit oferece
suporte para uma ampla variedade de funcionalidades numéricas.

Funcionalidade
Além da aritmética de inteiros mencionada até agora, a biblioteca de numéricos fornece
Funcionalidade de inteiro com (sem) sinal (multiplicação, quadrado, divisão com resto, inversão...) com um
ou dois números inteiros quantum como entrada
Funcionalidade de ponto fixo (adição/subtração, multiplicação, quadrado, 1/x, avaliação polinomial) com um
ou dois números de ponto fixo quantum como entrada

Introdução
Saiba como usar a biblioteca de numéricos
Utilização da Biblioteca de Numéricos
13/05/2021 • 3 minutes to read

Visão geral
A Biblioteca de Numéricos consiste em três componentes
1. Aritmética básica de inteiros com somadores e comparadores de inteiros
2. Funcionalidade de inteiros de alto nível criada com base na funcionalidade básica. Inclui multiplicação,
divisão, inversão etc. para inteiros com e sem sinal.
3. Funcionalidade aritmética de ponto fixo com inicialização, adição, multiplicação, recíproca, avaliação
polinomial e medição de ponto fixo.
Todos esses componentes podem ser acessados usando uma única instrução open :

open Microsoft.Quantum.Arithmetic;

Tipos
A biblioteca de numéricos é compatível com os seguintes tipos
1. LittleEndian : matriz qubit qArr : Qubit[] que representa um inteiro em que qArr[0] indica o bit menos
significativo.
2. SignedLittleEndian: igual ao LittleEndian , exceto que representa um inteiro com sinal armazenado no
complemento de dois.
3. FixedPoint : representa um número real que consiste em uma matriz qubit qArr2 : Qubit[] e uma posição
de ponto binário pos , que conta o número de dígitos binários à esquerda do ponto binário. qArr2 é
armazenado da mesma maneira que o SignedLittleEndian .

Operações
Para cada um dos três tipos acima existem várias operações disponíveis:
1. LittleEndian

Adição
Comparação
Multiplicação
Elevar ao quadrado
Divisão (com resto)
2. SignedLittleEndian

Adição
Comparação
Complemento do módulo de 2 de inversão
Multiplicação
Elevar ao quadrado
3. FixedPoint
Preparação/inicialização para valores clássicos
Adição (constante clássica ou outro ponto fixo quantum)
Comparação
Multiplicação
Elevar ao quadrado
Avaliação polinomial com especialização para funções pares e ímpares
Recíproco (1/x)
Medida (duplo clássico)
Para obter mais informações e documentação detalhada para cada uma dessas operações, consulte os
Q#documentos de referência da biblioteca em docs.microsoft.com.

Exemplo: adição de inteiro


Como um exemplo básico, considere a operação $$ \ket x\ket y\mapsto \ket x\ket{x+y} $$ ou seja, uma
operação que usa um inteiro n-qubit $x$ e um n- ou (n+1)-qubit registra $y$ como entrada, o último para o
qual ele mapeia para a soma $(x+y)$. Observe que a soma é calculada no módulo $2^n$ se $y$ estiver
armazenado em um registro $n$-bit.
Usando o Quantum Development Kit, essa operação pode ser aplicada da seguinte maneira:

operation TestMyAddition(xValue : Int, yValue : Int, n : Int) : Unit {


use (xQubits, yQubits) = (Qubit[n], Qubit[n]);
let x = LittleEndian(xQubits); // define bit order
let y = LittleEndian(yQubits);

ApplyXorInPlace(xValue, x); // initialize values


ApplyXorInPlace(yValue, y);

AddI(x, y); // perform addition x+y into y

// ... (use the result)

Exemplo: avaliação de funções suaves


Para avaliar funções suaves como $\sin(x)$ em um computador Quantum, em que $x$ é um número
FixedPoint quantum, a biblioteca de numéricos do Quantum Development Kit fornece as operações
EvaluatePolynomialFxP e Evaluate[Even/Odd]PolynomialFxP .

O primeiro EvaluatePolynomialFxP permite avaliar um polinomial da forma $$ P(x) = a_0 + a_1x + a_2x^2 +
\cdots + a_dx^d, $$, em que $d$ indica o grau. Para fazer isso, somente são necessários os coeficientes
polinomiais [a_0,..., a_d] (do tipo Double[] ), a entrada x : FixedPoint e a saída y : FixedPoint
(inicialmente zero):

EvaluatePolynomialFxP([1.0, 2.0], x, y);

O resultado, $P(x)=1+2x$, será armazenado em yFxP .


A segunda EvaluateEvenPolynomialFxP e a terceira EvaluateOddPolynomialFxP são especializações para os casos
de funções pares e ímpares, respectivamente. Ou seja, para uma função par/ímpar $f(x)$ e $$ P_{even}(x)=a_0 +
a_1 x^2 + a_2 x^4 + \cdots + a_d x^{2d}, $$ $f(x)$ aproxima-se bem pelo $P_{even}(x)$ ou $P_{odd}(x) :=
x\cdot P_{even}(x)$, respectivamente. Em Q#, esse dois casos podem ser tratados da seguinte forma:
EvaluateEvenPolynomialFxP([1.0, 2.0], x, y);

que avalia $P_{even}(x) = 1 + 2x^2$, e

EvaluateOddPolynomialFxP([1.0, 2.0], x, y);

que avalia $P_{odd}(x) = x + 2x^3$.

Mais exemplos
Encontre mais exemplos no repositório principal de exemplos.
Para começar, clone o repositório e abra a subpasta Numerics :

git clone https://github.com/Microsoft/Quantum.git


cd Quantum/samples/numerics

Em seguida, digite cd em uma das pastas de exemplo e execute o exemplo usando

dotnet run

Você também pode gostar