Escolar Documentos
Profissional Documentos
Cultura Documentos
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:
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.
Depois de criar um trabalho, vários metadados estão disponíveis sobre seu estado e o histórico de execuções.
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:
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:
Desinstalar a extensão
Para desinstalar a extensão de CLI do Azure quantum , execute o seguinte comando:
Atualizar a extensão
Se precisar atualizar uma instalação existente da extensão de CLI do Azure quantum , você poderá executar:
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 .
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.
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.
3. Se esta for a primeira vez que você cria workspaces do Quantum em sua assinatura, registre o provedor
de recursos com este comando:
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.
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:
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.
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:
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:
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.
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.
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.
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.
<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:
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 :
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:
.........
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.
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
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 :
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.
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.
<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>
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:
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 :
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.
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
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 :
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.
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.
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.
Na primeira vez que você executar esse código em seu dispositivo, uma janela poderá ser aberta no navegador
padrão solicitando suas credenciais.
terms: List[Term] = []
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.
A próxima etapa é criar uma instância de um Problem para enviar ao solucionador do Azure Quantum:
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
printResultSummary(result)
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.
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.
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.
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.
Na primeira vez que você executar esse código em seu dispositivo, uma janela poderá ser aberta no navegador
padrão solicitando suas credenciais.
terms: List[Term] = []
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.
A próxima etapa é criar uma instância de um Problem para enviar ao solucionador do Azure Quantum:
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
printResultSummary(result['solutions'][0])
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.
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#
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).
NOTE
Um provedor é um serviço Quantum de parceiro que consiste em hardware Quantum, um simulador ou um serviço de
otimização.
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:
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
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 :
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:
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
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:
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.
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:
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.
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.target ionq.simulator
%azure.execute GenerateRandomBit
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:
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.
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)
{'[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'}]
>>> 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.
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.
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.
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:
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".
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
"terms": [
{
"c": 1.0,
"ids": [0, 1, 2]
},
{
"c": 1.0,
"ids": [2, 3, 4]
}
]
"terms": [
{
"c": 1.0,
"ids": [0, 0, 0, 1, 1]
},
{
"c": 1.0,
"ids": [2, 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": []
}
]
{
"cost_function": {
"type": "pubo",
"version": "1.0",
"terms": [{
"c": 1.0,
"ids": [0, 1]
}, {
"c": 0.36,
"ids": [1, 2]
}]
}
}
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:
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
Tempo do sistema
M EDIDA DURA Ç Ã O M ÉDIA DO T EM P O ( Μ S)
T1 >10^7
T2 200.000
Leitura 100
Redefinição de registro 25
Fidelidade do sistema
O P ERA Ç Ã O F IDEL IDA DE M ÉDIA
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.
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).
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.
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"
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
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
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
REGIÃ O
NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.
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
Para criar um solucionador de recozimento simulado sem parâmetros para a CPU usando o SDK:
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.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O
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)
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
NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O
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.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O
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.
NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O
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.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O
NOTE
Para obter mais informações sobre qual solucionador usar, consulte Qual solucionador de otimização devo usar?.
N O M E DO PA RÂ M ET RO DESC RIÇ Ã O
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.
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)
Workspace.cancel_job
Cancela um trabalho que foi enviado anteriormente.
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()
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
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.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
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
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
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
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 .
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.
terms = [
Term(c=-9, indices=[0]),
Term(c=-3, indices=[1,0]),
Term(c=5, indices=[2,0])
]
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])
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.
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.
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)
job = solver.submit(problem)
result = job.get_results()
print(result)
> {'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:
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 :
solver = SimulatedAnnealing(workspace)
job = solver.submit(problem)
print(job.id)
> 5d2f9cd70f55f149e3ed3aef
job = workspace.get_job(job.id)
results = job.get_results()
print(results)
À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:
> ea81bb40-682f-11ea-8271-c49dede60d7c
job.refresh()
print(job.details.status)
> Succeeded
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.
NOTE
Não é preciso definir um URI de redirecionamento
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
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.
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.
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.
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.
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
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.
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
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
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.
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
Supor te a SO: Windows, macOS, Somente Windows Windows, macOS, Windows, macOS,
Linux Linux Linux
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.
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.
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.
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#.
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.
namespace Qrng {
open Microsoft.Quantum.Intrinsic;
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#.
cd host
dotnet add reference ../quantum/quantum.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()}");
Assert(bits.Parity() == restored.Parity());
}
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.
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>
<Project Sdk="Microsoft.Quantum.Sdk/0.12.20072031">
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:
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>
<Project Sdk="Microsoft.Quantum.Sdk/0.12.20072031">
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.*
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
1. Ative o ambiente de conda em que você instalou o pacote qsharp e execute este comando para atualizá-
lo:
1. Ative o ambiente de conda em que você instalou o pacote qsharp e execute este comando para atualizá-
lo:
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#.
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.
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#.
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.
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).
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.
}
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:
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.
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.
namespace Bell {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Canon;
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[()] ).
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 :
mutable numOnes = 0;
use qubit = Qubit();
for test in 1..count {
SetQubitState(initial, qubit);
let res = M(qubit);
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.
@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);
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:
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);
H(qubit);
let res = M(qubit);
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 :
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);
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);
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 :
H(q0);
CNOT(q0, q1);
let res = M(q0);
if M(q1) == res {
set agree += 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:
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.
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#.
$NOT$ X
$XOR$ CNOT
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);
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.
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.
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 {
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 ApplyMarkingOracleAsPhaseOracle(
markingOracle : (Qubit[], Qubit) => Unit is Adj,
register : Qubit[]
) : Unit is Adj {
use target = Qubit();
within {
X(target);
H(target);
} apply {
markingOracle(register, target);
}
}
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
Para as próximas execuções, não é necessário compilá-lo novamente. Para executá-lo, digite o seguinte
comando e pressione Enter:
Depois de pressionar Enter, você deverá ver a seguinte mensagem exibida no terminal:
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 {
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
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 ).
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
namespace NamespaceQFT {
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Diagnostics;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Arrays;
// operations go here
}
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[] .
Em nossa operação Q#, primeiro alocamos um registro de três qubits com a palavra-chave use :
use qs = Qubit[3];
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.
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):
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 ).
//second qubit:
H(qs[1]);
Controlled R1([qs[2]], (PI()/2.0, qs[1]));
//third qubit:
H(qs[2]);
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;
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("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 {
// ...
dotnet run
Após a conclusão, você deverá ver as saídas Message e DumpMachine abaixo impressas em seu console.
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.
Primeiro, modificamos nossa operação Perform3QubitQFT para retornar uma matriz de resultados de medição,
Result[] em vez de Unit .
Antes de alocar qubits (por exemplo, antes da instrução use ), declaramos e associamos essa matriz de
comprimento 3 (um Result para cada qubit):
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;
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]);
for i in IndexRange(qs) {
set resultArray w/= i <- M(qs[i]);
}
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 :
open Microsoft.Quantum.Convert;
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:
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 :
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
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.
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.
Algoritmos
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.
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 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.
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.
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.
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.
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.
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.
À 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.
$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]) :
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.
$Z\otimes Z$ $\operatorname{CNOT}_{10}$
$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 .
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.
é 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.
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:
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 $.
H-Gate Measurement
Qubit 0
Qubit 1
space
Qubit 2 CNOT01
time
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.
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.
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]$$
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.
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.
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 $.
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.
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.
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.
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:
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.
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.
NOTE
Essa instância tem apenas duas soluções com $\mathrm{cost}=0$ (uma espelhando a outra). Você consegue encontrá-
las?
"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.
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$.
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.
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.
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.
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.
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.
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.
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.
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 .
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!
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.
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:
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
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.
```powershell
python host.py
```
```powershell
dotnet run
```
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 .
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:
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.
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
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.
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.
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 .
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.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:
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:
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:
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:
Dependendo da versão do XUnit que seu projeto de teste usa, talvez você também precise atualizar o XUnit para
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
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?.
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:
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:
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
}
}
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.
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
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
[Zero,One,One,One]
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 :
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> .
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
}
}
Python
C#
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)
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}
import qsharp
qsharp.projects.add("../qrng/Qrng.csproj")
from Qrng import SampleQuantumRandomNumberGenerator
print(f"Qrng result: {SampleQuantumRandomNumberGenerator.simulate()}")
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 :
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 {
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.
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:
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:
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:
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) ---
∣1
∣+
∣-
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.
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;
}
}
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 ]
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:
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;
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;
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 {
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
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 :
Any combination of named and unnamed items is supported, and inner items may also be named. For example,
the type Nested , defined as
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[] :
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.
is equivalent to
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);
}
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
and is inserted inside the declaration of SWAP . On the other hand, inserting the line
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: -
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
is equivalent to
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# 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
let n = 5;
... // n is 5
let n = 8; // Error
...
and
let n = 8;
if (a == b) {
... // n is 8
let n = 5; // Error
...
}
...
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.
op(fn(input));
}
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,
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:
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:
Similarly, the following function multiplies each item in an array with the given factor:
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:
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;
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,
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 {
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();
// ...
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,
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.
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.
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":
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:
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 .
The second statement is nothing but a short-hand for the more verbose syntax set arr = arr w/ 0 <- 10; .
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.
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
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
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
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
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.
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:
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.
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,
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.
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
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:
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:
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.
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.
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:
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.":
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.
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
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.
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 ""
Result Zero
Pauli PauliI
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.
User defined type Represents a user defined type consisting of named and
anonymous items of different types. Values are instantiated
by invoking the constructor.
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:
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,
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
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 ,
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 AllCControlled<'T3> (
ops : ('T3 => Unit)[]
) : ((Bool,'T3) => Unit)[] {
operation Foo<'TArg> (
op : 'TArg => Unit,
arg : 'TArg
) : Unit {
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.
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 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 .
qubit_result = myOperation.simulate()
dotnet run
dotnet run -s QuantumSimulator
%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.
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#.
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.
qubit_result = myOperation.estimate_resources()
%estimate myOperation
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();
Métricas relatadas
O avaliador de recursos acompanha as seguintes métricas:
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.
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();
}
}
}
Observe que, se houver pelo menos uma operação de medida que não está anotada usando
AssertMeasurementProbability , o simulador gerará um UnconstrainedMeasurementException .
Verificador de uso de qubits invalidados Verifica se o programa aplica uma operação a um qubit que
já foi liberado
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
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.
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#.
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:
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.
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
open Microsoft.Quantum.Intrinsic;
operation ApplySampleWithCCNOT() : Unit {
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;
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> .
Por fim, gere todas as estatísticas coletadas pelo Contador de Operações Primitivas no formato CSV usando o
seguinte:
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
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.
open Microsoft.Quantum.Intrinsic;
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:
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.
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:
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:
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.
qubit_result = myOperation.toffoli_simulate()
%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.
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
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.
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:
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:
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:
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:
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:
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.
/// # 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:
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#.
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:
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);
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:
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.
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
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:
Da mesma forma, a função Subarray function pode ser usada para reordenar ou pegar subconjuntos dos
elementos de uma matriz:
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:
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:
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.
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 .
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 .
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
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.
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 .
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.
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:
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#.
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 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.
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 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]);
}
}
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:
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.
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.
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:
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
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 .
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.
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.
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:
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.
Install-Package Microsoft.Quantum.Numerics
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 .
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
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 :
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:
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 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;
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);
// 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;
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:
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();
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.
// These fermion terms are not equal. The following Boolean is `false`.
var sequenceEqual = fermionTerm0 == fermionTerm1;
// These Hermitian fermion terms are equal. The following Boolean is `true`.
var hermitianSequenceEqual = hermitianFermionTerm0 == hermitianFermionTerm1;
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 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()));
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.
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);
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}
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:
// `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);
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.
// `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);
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.
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) };
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.
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
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.
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();
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.
// 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 .
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:
// `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!;
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;
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.
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,
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 = "...";
Também poderemos carregar um hamiltoniano no formato LIQUi|>, usando uma sintaxe muito semelhante.
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.
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);
//////////////////////////////////////////////////////////////////////////
// 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);
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
};
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.
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 :
A política será revertida quando você sair do PowerShell. Se você quiser salvar a política, use um valor diferente para
-Scope :
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
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
.
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
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 :
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
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
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
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
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
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
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
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
two_electron_integrals:
index_convention: mulliken
units: hartree
format: sparse
values:
- [1, 1, 1, 1, 1.6]
- [6, 1, 3, 2, -0.1]
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
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
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:
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
O b j et o In t eg r ai s d e u m el ét r o n
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
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
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
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
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 .
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:
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.
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.
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
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]
]
miss_rate = ValidateHalfMoonModel.simulate(
validationVectors=data['ValidationData']['Features'],
validationLabels=data['ValidationData']['Labels'],
parameters=parameters, bias=bias
)
$ 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;
return Mapped(
WithProductKernel(scale, _),
samples
);
}
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);
}
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.
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.
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.
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:
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.
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):
Mais exemplos
Encontre mais exemplos no repositório principal de exemplos.
Para começar, clone o repositório e abra a subpasta Numerics :
dotnet run