Programação Quântica
Carregando, aguarde alguns segundos.

5 - Programação Quântica

A Parte 4 aborda a programação quântica, que é a arte de escrever programas para computadores quânticos, sendo composta por três capítulos que exploram diferentes aspectos da programação quântica.

  • Capítulo 4.1: começa com uma discussão sobre a linguagem de programação quântica, que é uma forma de representar os programas quânticos de forma clara e concisa, para então apresentar diferentes linguagens de programação quântica, incluindo Qiskit, Cirq, PyQuil, Q# e outras, apresentando exemplos de cada linguagem e discutindo suas vantagens e desvantagens.
  • Capítulo 4.2: aborda a compilação e otimização de programas quânticos. Ele discute as técnicas usadas para transformar programas quânticos em circuitos, que podem ser implementados em hardware quântico. O capítulo também apresenta as técnicas usadas para otimizar circuitos quânticos, tornando-os mais eficientes e menos suscetíveis a erros.
  • Capítulo 4.3: aborda a simulação de programas quânticos com Qiskit, linguagem com código aberto para programação quântica, desenvolvida pela IBM e baseada no Python. Discute as diferentes técnicas usadas para simular programas quânticos, incluindo a simulação de estado completo e a simulação de operação, com algoritmos com o Qiskit.

Esta parte fornece uma visão geral abrangente da programação quântica. Os capítulos abordam diferentes aspectos da programação quântica, incluindo a linguagem de programação quântica, a compilação e otimização de programas quânticos e a simulação de programas quânticos, fornecendo uma introdução valiosa à programação quântica e aos desafios enfrentados pelos programadores quânticos.

5.1 - Introdução à programação quântica

O Capítulo 4.1 apresenta uma introdução à programação quântica, que é uma forma de escrever programas para computadores quânticos.

  • Começa explicando a diferença entre bits clássicos e qubits quânticos e apresentando os conceitos básicos de programação quântica, como portas quânticas, circuitos quânticos e algoritmos quânticos.
  • Em seguida, são apresentados alguns exemplos simples de programas quânticos, como o algoritmo de Deutsch-Jozsa, o algoritmo de Grover e o algoritmo de Shor. Esses algoritmos ilustram como a programação quântica pode ser usada para resolver problemas de forma mais eficiente do que a programação clássica.
  • Também discute algumas das dificuldades associadas à programação quântica, como a sensibilidade dos qubits a ruídos e interferências externas, a limitação do número de qubits disponíveis e a necessidade de cuidados especiais no processo de medição dos qubits.

Por fim, o capítulo 9 apresenta algumas das linguagens de programação quântica disponíveis atualmente, como Qiskit, Cirq, PyQuil e Q#, e discute as perspectivas futuras da programação quântica, incluindo aplicações em criptografia, simulação de sistemas quânticos e inteligência artificial.

Introdução à programação quântica

A programação quântica é uma forma de escrever programas para computadores quânticos, que se baseia em conceitos da mecânica quântica. Esses programas são escritos utilizando qubits, que são os análogos quânticos dos bits clássicos. Enquanto um bit clássico pode estar em um de dois estados, 0 ou 1, um qubit pode estar em uma superposição desses dois estados. Isso significa que um qubit pode representar mais informações do que um bit clássico.

Para programar um computador quântico, é necessário entender os conceitos básicos da programação quântica, como portas quânticas, circuitos quânticos e algoritmos quânticos. As portas quânticas são operações matemáticas que agem sobre os qubits e modificam sua superposição. Os circuitos quânticos são sequências de portas quânticas que realizam uma tarefa específica. Já os algoritmos quânticos são programas que utilizam circuitos quânticos para realizar tarefas que seriam difíceis ou impossíveis para um computador clássico.

A programação quântica apresenta desafios específicos, como a sensibilidade dos qubits a ruídos e interferências externas, a limitação do número de qubits disponíveis e a necessidade de cuidados especiais no processo de medição dos qubits. No entanto, a programação quântica também oferece oportunidades para resolver problemas de forma mais eficiente do que a programação clássica, especialmente em áreas como criptografia, simulação de sistemas quânticos e inteligência artificial.

Atualmente, existem diversas linguagens de programação quântica disponíveis, como Qiskit, Cirq e Q#, que permitem aos programadores escrever programas para computadores quânticos e explorar as possibilidades oferecidas pela programação quântica.

Diferença entre bits clássicos e qubits quânticos

A diferença fundamental entre bits clássicos e qubits quânticos é que um bit clássico pode estar em um de dois estados, 0 ou 1, enquanto um qubit pode estar em uma superposição desses dois estados. Isso significa que um qubit pode representar mais informações do que um bit clássico e permite que a computação quântica resolva certos problemas de forma mais eficiente do que a computação clássica.

Na programação quântica, os qubits são manipulados por meio de portas quânticas, que são operações matemáticas que agem sobre os qubits e modificam sua superposição. Existem várias portas quânticas, como a porta de Hadamard, que coloca um qubit em uma superposição de estados, e a porta CNOT, que aplica uma operação condicional entre dois qubits. As portas quânticas podem ser combinadas em circuitos quânticos, que são sequências de portas quânticas que realizam uma tarefa específica.

Os algoritmos quânticos são programas que utilizam circuitos quânticos para realizar tarefas que seriam difíceis ou impossíveis para um computador clássico. Alguns exemplos de algoritmos quânticos são o algoritmo de Deutsch-Jozsa, que determina se uma função é constante ou balanceada com apenas uma consulta, e o algoritmo de Shor, que fatora números inteiros em tempo polinomial.

A programação quântica apresenta desafios específicos, como a sensibilidade dos qubits a ruídos e interferências externas, a limitação do número de qubits disponíveis e a necessidade de cuidados especiais no processo de medição dos qubits. No entanto, a programação quântica também oferece oportunidades para resolver problemas de forma mais eficiente do que a programação clássica, especialmente em áreas como criptografia, simulação de sistemas quânticos e inteligência artificial.

Exemplos simples de algoritmos quânticos

A seguir são descritos brevemente cada um dos algoritmos mencionados:

  • Deutsch-Jozsa: algoritmo quântico que determina se uma função “booleana” é constante ou balanceada com apenas uma consulta. Em outras palavras, a partir de uma função f(x) que mapeia entradas binárias de tamanho n para uma saída binária, o algoritmo determina se a função retorna 0 para todas as entradas ou 1 para metade das entradas e 0 para a outra metade. Esse problema pode ser resolvido em tempo polinomial por um computador clássico para qualquer valor de n, mas o algoritmo quântico pode resolvê-lo com uma única consulta à função, o que é exponencialmente mais rápido do que o melhor algoritmo clássico conhecido. O algoritmo de Deutsch-Jozsa é um dos primeiros exemplos de um problema que pode ser resolvido exponencialmente mais rápido em um computador quântico do que em um computador clássico.
  • Grover: algoritmo quântico de busca que encontra um elemento específico em uma lista não ordenada de N elementos em tempo proporcional a N½, enquanto que em um computador clássico o tempo de busca é proporcional a N. O algoritmo é especialmente útil quando se busca elementos em grandes bancos de dados ou em problemas de otimização. O algoritmo começa com uma superposição igual de todas as possíveis entradas e, em seguida, aplica uma série de operações quânticas para aumentar a amplitude da entrada desejada. Isso é feito usando uma sequência de inversões de fase que resultam em uma amplitude alta para a entrada desejada e uma amplitude baixa para as outras entradas. O algoritmo é executado até que a entrada desejada seja encontrada com alta probabilidade.
  • Shor: algoritmo quântico que fatora números inteiros em tempo polinomial, enquanto que em um computador clássico o tempo de fatoração cresce exponencialmente com o tamanho do número. Isso tem implicações importantes para a criptografia, uma vez que muitos esquemas criptográficos confiam na dificuldade da fatoração de números grandes. O algoritmo de Shor utiliza a transformada de Fourier quântica para encontrar um período de uma função modular que está relacionada à fatoração do número. Em seguida, utiliza esse período para encontrar os fatores do número original. A complexidade desse algoritmo é expressa em termos do tamanho do número a ser fatorado, denotado por N, e logN representa o logaritmo na base 2 de N. O algoritmo tem complexidade O(\log(N)^3), enquanto que o melhor algoritmo clássico conhecido tem complexidade O(\exp\left(\frac{1}{3} \left( \log(N)^{1/3} (\log \log N)^{2/3} \right) \right)) . Essa complexidade é exponencial porque a função exponencial aparece no tempo de execução do algoritmo, o que implica um crescimento muito mais rápido à medida que N aumenta. Isso significa que o algoritmo quântico é exponencialmente mais rápido do que o algoritmo clássico.

Dificuldades associadas à programação quântica

Discutiremos agora algumas das dificuldades associadas à programação quântica, como a sensibilidade dos qubits a ruídos e interferências externas, à limitação do número de qubits disponíveis e a necessidade de cuidados especiais no processo de medição dos qubits.

A programação quântica apresenta uma série de desafios e dificuldades que os programadores precisam enfrentar ao projetar e implementar algoritmos para computadores quânticos. Alguns dos desafios mais significativos incluem:

  • Sensibilidade aos ruídos e interferências externas: os qubits são extremamente sensíveis a qualquer forma de interferência ou ruído ambiental, o que pode levar a erros na execução dos algoritmos quânticos. Para minimizar esses erros, os computadores quânticos devem ser mantidos em ambientes altamente controlados e resfriados a temperaturas próximas do zero absoluto. Além disso, os algoritmos quânticos precisam ser projetados para serem robustos o suficiente para funcionar mesmo quando os qubits são perturbados por interferências externas.
  • Limitação do número de qubits disponíveis: atualmente, os computadores quânticos mais avançados têm cerca de 50 a 400 qubits, o que limita o tamanho e a complexidade dos algoritmos que podem ser executados. Como resultado, a programação quântica requer uma abordagem diferente da programação clássica, uma vez que é necessário otimizar os algoritmos para funcionarem com um número limitado de qubits.
  • Necessidade de cuidados especiais na medição dos qubits: a medição dos qubits é um processo delicado que pode perturbar o estado quântico dos qubits, levando a erros na execução dos algoritmos. Como resultado, a medição dos qubits deve ser cuidadosamente projetada e executada para minimizar esses erros.

Além desses desafios, a programação quântica também requer uma compreensão profunda da mecânica quântica e de como ela se aplica aos sistemas de qubits. Os programadores também precisam ser capazes de projetar e implementar portas quânticas e circuitos quânticos, que são diferentes dos componentes usados na programação clássica. Como resultado, a programação quântica é uma habilidade altamente especializada que requer um conjunto diferente de habilidades e conhecimentos em comparação com a programação clássica.

Linguagens de programação quântica

Atualmente, existem várias linguagens de programação quântica disponíveis, cada uma com suas próprias características e funcionalidades. Alguns exemplos incluem:

  • Qiskit: desenvolvida pela IBM, Qiskit é uma linguagem de programação quântica que permite a criação de algoritmos quânticos usando uma combinação de código Python e código quântico. Qiskit inclui uma ampla gama de ferramentas e recursos para ajudar os programadores a criarem, depurarem e executarem algoritmos quânticos.
  • Cirq: desenvolvida pelo Google, Cirq é uma linguagem de programação quântica de código aberto que permite a criação de algoritmos quânticos usando Python, incluindo um conjunto de ferramentas e bibliotecas para ajudar os programadores a criarem circuitos quânticos e executá-los em simuladores ou em computadores quânticos reais.
  • PyQuil: desenvolvido pela Rigetti Computing, PyQuil é um pacote de código aberto, baseado na linguagem de programação Python, para programação quântica. Oferece suporte para simulação de circuitos quânticos, bem como acesso a hardware quântico real de próxima geração.
  • Q#: desenvolvida pela Microsoft, Q# é uma linguagem de alto nível que permite a criação de programas quânticos usando uma combinação de código quântico e clássico, projetada para ser usada com o kit de desenvolvimento de software quântico Microsoft Quantum Development Kit.

Além dessas linguagens de programação, existem outras ferramentas e plataformas que podem ser usadas para programar computadores quânticos, como o Amazon Braket e o Rigetti Forest.

No futuro, a programação quântica tem o potencial de ter um impacto significativo em uma ampla gama de áreas, incluindo criptografia, simulação de sistemas quânticos e inteligência artificial. Por exemplo, a criptografia quântica é considerada inquebrável, tornando-se uma aplicação promissora da programação quântica. A simulação de sistemas quânticos, como moléculas, também pode ser significativamente acelerada com computadores quânticos, o que poderia ter aplicações em áreas como a descoberta de novos materiais e medicamentos.

Além disso, a programação quântica pode ser usada para acelerar algoritmos de aprendizado de máquina, o que poderia levar a avanços significativos em áreas como reconhecimento de padrões e previsão de séries temporais. No entanto, ainda há muitos desafios a serem superados antes que a programação quântica possa se tornar amplamente utilizada em aplicações do mundo real, incluindo a construção de computadores quânticos mais poderosos e a criação de algoritmos mais robustos e eficientes.

5.2 - Linguagens de programação quântica

O Capítulo 4.2 aborda as diferentes linguagens de programação quântica disponíveis para programadores quânticos.

  • Inicialmente, apresenta uma breve revisão sobre os conceitos fundamentais de programação quântica, como a representação de estados quânticos e a realização de operações lógicas.
  • Em seguida, o capítulo discute algumas das principais linguagens de programação quântica, como Qiskit, Cirq, Q# e PyQuil. Para cada uma das linguagens, são fornecidas informações sobre sua sintaxe, estrutura de dados, ferramentas e funcionalidades.
  • Também são apresentados exemplos de código em cada uma das linguagens para que o leitor possa comparar as diferenças e semelhanças entre elas.
  • Além disso, discute as vantagens e desvantagens de cada linguagem de programação quântica, bem como as limitações que os programadores podem enfrentar ao utilizá-las.
  • Por fim, fornece algumas recomendações para os programadores quânticos sobre como escolher a melhor linguagem para o seu projeto específico.

Este capítulo oferece uma visão geral das principais linguagens de programação quântica disponíveis e suas características, permitindo ao leitor escolher a melhor opção para suas necessidades e interesses.

Conceitos fundamentais da programação quântica

A programação quântica é uma área da computação que lida com a criação e execução de algoritmos em computadores quânticos. Nesta área, é importante entender conceitos fundamentais como a representação de estados quânticos e a realização de operações lógicas.

Em um computador quântico, a unidade básica de informação é o qubit, que pode estar em um estado de superposição de 0 e 1 simultaneamente. A representação de estados quânticos é feita por meio de vetores de estado, que são combinações lineares dos estados de 0 e 1. Por exemplo, um qubit em um estado de superposição pode ser representado por um vetor de estado que é uma combinação linear dos estados de 0 e 1. Essa representação é fundamental para a realização de operações quânticas, que são realizadas por meio de portas lógicas quânticas.

As portas lógicas quânticas são as operações fundamentais em um computador quântico. Elas são representadas por matrizes unitárias que atuam nos vetores de estado dos qubits. Essas portas são usadas para realizar operações lógicas, como a adição, subtração e comparação de números quânticos, que são importantes para a solução de problemas em diversas áreas, como criptografia, simulação molecular e otimização de problemas.

Alguns dos conceitos fundamentais da programação quântica incluem a medida dos estados quânticos, que permite a obtenção de informações sobre o estado quântico de um sistema, e a noção de emaranhamento, que é uma propriedade fundamental da mecânica quântica que permite que dois qubits estejam correlacionados de forma que a medição de um qubit possa influenciar o estado do outro qubit, mesmo que eles estejam fisicamente separados.

Em resumo, a programação quântica envolve a representação de estados quânticos, a realização de operações lógicas quânticas e o uso de conceitos fundamentais da mecânica quântica, como a sobreposição de estados e o emaranhamento quântico, para resolver problemas em áreas como criptografia, simulação molecular e otimização de problemas.

Visão geral das linguagens de programação quântica

Existem várias linguagens de programação quântica disponíveis, cada uma com suas próprias características, ferramentas e recursos. Nesta visão geral, discutiremos algumas das principais linguagens de programação quântica e suas características para ajudar o leitor a escolher a melhor opção para suas necessidades e interesses.

Existem diversas linguagens de programação quântica disponíveis para programadores quânticos, cada uma com suas próprias vantagens e desvantagens. Aqui iremos apresentar algumas das principais linguagens de programação quântica, como Qiskit, Cirq, PyQuil e Q#.

Existem várias linguagens de programação quântica disponíveis para programadores quânticos, cada uma com suas próprias vantagens e desvantagens. Algumas das linguagens de programação quântica mais populares são apresentadas a seguir.

Qiskit

Qiskit é um pacote baseado na linguagem de programação Python, com código aberto, para programação quântica. Desenvolvido pela IBM, o pacote disponibiliza uma linguagem que permite aos usuários criarem e executarem programas quânticos em computadores quânticos reais ou simulados. É uma dos mais populares disponíveis atualmente, Qiskit é amplamente usado pela comunidade quântica e oferece uma variedade de ferramentas e recursos para programação quântica, incluindo simulação de circuitos quânticos, acesso a hardware quântico real e suporte para operações lógicas quânticas, como a operação de portas quânticas, medições e emaranhamento. Além disso, a linguagem possui uma grande comunidade de usuários e uma vasta documentação, o que facilita o aprendizado e o desenvolvimento de programas.

Cirq

Cirq é um pacote baseado na linguagem de programação Python com código aberto para programação quântica desenvolvido pelo Google e projetado para permitir a criação e simulação de algoritmos quânticos em hardware de próxima geração. Oferece ferramentas para simulação de circuitos quânticos, acesso a hardware quântico e suporte para operações lógicas quânticas, como a realização de portas quânticas, medições e emaranhamento. A linguagem também possui recursos avançados, como a possibilidade de otimizar algoritmos quânticos para melhorar o desempenho em computadores quânticos reais.

PyQuil

PyQuil é um pacote de código aberto, baseado na linguagem de programação Python, para programação quântica, desenvolvido pela Rigetti Computing. Oferece suporte para simulação de circuitos quânticos, bem como acesso a hardware quântico real de próxima geração. PyQuil é usado principalmente para programação de hardware quântico baseado em qubits supercondutores. Oferece suporte para operações lógicas quânticas, como a realização de portas quânticas, medidas e emaranhamento. A linguagem também possui uma interface gráfica de usuário (GUI) que facilita o desenvolvimento de programas.

Q#

Q# é a linguagem de programação quântica desenvolvida pela Microsoft, baseada em .NET, que permite aos usuários criarem e executarem programas usando algoritmos quânticos, em diferentes plataformas de computadores quânticos, reais (incluindo qubits supercondutores e átomos em armadilhas de íons ) ou simulados. Oferece suporte para operações lógicas quânticas, como a realização de portas quânticas, medidas e emaranhamento. Também permite a integração com outras linguagens de programação, como c e Python, para criar programas híbridos, mesclando algoritmos clássicos e quânticos. Estão disponíveis ferramentas com Q# para simulação de circuitos quânticos, acesso a hardware quântico real e suporte a várias plataformas de hardware.

OpenQASM

OpenQASM é uma linguagem de programação quântica desenvolvida pela IBM, projetada para permitir a criação de algoritmos quânticos em uma variedade de plataformas quânticas, incluindo computadores de qubits supercondutores e de átomos em armadilhas de íons. A linguagem é usada principalmente para programação de hardware quântico e oferece suporte para criação de circuitos quânticos, simulação de circuitos quânticos e acesso a hardware quântico real.

Quipper

Quipper é uma linguagem de programação quântica de código aberto desenvolvida pela Universidade de Oxford. É uma linguagem baseada em Haskell que permite aos usuários criarem e executarem programas quânticos em computadores quânticos reais ou simulados.

Essas são algumas das principais linguagens de programação quântica disponíveis atualmente. Cada uma tem seus próprios recursos – características, ferramentas e bibliotecas – e comunidades. Ao escolher uma linguagem de programação quântica, é importante considerar fatores como facilidade de uso, documentação, recursos de comunidade e suporte para a plataforma de computação quântica que você planeja usar.

Por fim, vale ressaltar que a escolha da linguagem de programação quântica adequada depende não apenas da complexidade do problema a ser resolvido, mas também do nível de habilidade e experiência do programador.

Além disso, é importante considerar a comunidade de desenvolvedores e as ferramentas disponíveis para cada linguagem.

Portanto, antes de decidir qual linguagem utilizar, é recomendado explorar as opções disponíveis e testar diferentes abordagens para determinar qual é a mais adequada para o seu projeto específico.

Com o rápido avanço da computação quântica, é provável que novas linguagens e ferramentas surjam no futuro, tornando ainda mais importante estar atualizado sobre as últimas tendências e desenvolvimentos no campo.

Vantagens e desvantagens de cada linguagem

Cada uma das diferentes linguagens de programação quântica atualmente disponíveis têm suas próprias vantagens e desvantagens.

Qiskit é uma dos pacotes de programação quântica mais populares atualmente, escrito com o Python e apoiado pela IBM. Possui uma grande comunidade de usuários e uma vasta documentação, tornando-se fácil de aprender e usar. Além disso, possui uma variedade de ferramentas e recursos, incluindo simuladores, compiladores e hardware quântico real para experimentação. No entanto, sua a sintaxe pode ser um pouco complicada, especialmente para iniciantes, e a falta de padronização pode tornar difícil a portabilidade de código entre diferentes provedores de hardware quântico.

Cirq é outro pacote de programação quântica popular escrito com Python, apoiado pelo Google e conhecido por ser rápido e eficiente em termos de desempenho. Além disso, a sintaxe do código é limpa e simples, sendo assim fácil de usar e aprender. No entanto, o Cirq ainda é relativamente novo, o que significa que pode ter menos recursos e ferramentas disponíveis em comparação com outras linguagens de programação quântica mais estabelecidas.

PyQuil é o pacote de programação quântica apoiado pela Rigetti, escrito em Python, conhecido por ser fácil de usar e possuir uma variedade de recursos, incluindo um simulador e acesso a hardware quântico real para experimentação. No entanto, como é uma linguagem de programação quântica relativamente nova, ainda pode ter menos recursos e ferramentas em comparação com outras linguagens mais estabelecidas.

Q# é uma linguagem de programação quântica desenvolvida pela Microsoft, não apenas um pacote para outra linguagem de programação. Projetado para ser fácil de usar, tem uma sintaxe semelhante a outras linguagens de programação modernas como C# e F#. Além disso, a Microsoft fornece uma variedade de ferramentas e recursos, incluindo simuladores e acesso a hardware quântico real para experimentação. No entanto, a comunidade de usuários do Q# é relativamente pequena em comparação com outras linguagens de programação quântica mais populares.

Independente da linguagem de programação quântica escolhida, programadores podem enfrentar várias limitações ao trabalhar com computação quântica. Uma das principais limitações é a necessidade de hardware quântico para experimentação, que ainda está em desenvolvimento e é muito limitado em termos de escalabilidade. Além disso, a computação quântica é uma área em rápida evolução, o que significa que novas técnicas e algoritmos estão sendo desenvolvidos regularmente, o que pode tornar difícil acompanhar as últimas tendências e implementações em diferentes linguagens de programação quântica.

Recomendações para os programadores quânticos

Ao escolher a melhor linguagem de programação quântica para um projeto específico, os programadores quânticos devem considerar vários fatores, incluindo suas habilidades de programação, a complexidade do projeto, as limitações de hardware e as ferramentas e recursos disponíveis.

  • Habilidades de programação: os programadores quânticos devem escolher uma linguagem de programação quântica que seja adequada às suas habilidades de programação e conhecimento prévio. Por exemplo, se o programador já possui experiência em Python, pode ser mais fácil para ele aprender e usar o Qiskit ou PyQuil, se estiver acostumado com ambientes MS pode escolher o Q#.
  • Complexidade do projeto: o tipo e a complexidade do projeto podem influenciar a escolha da linguagem de programação quântica. Alguns projetos podem exigir recursos específicos que não estão disponíveis em todas as linguagens de programação quântica. Por exemplo, se o projeto requer um alto nível de otimização de desempenho, o Cirq pode ser a melhor opção devido à sua velocidade e eficiência.
  • Limitações de hardware: os programadores quânticos devem considerar as limitações de hardware ao escolher uma linguagem de programação quântica. Nem todas as linguagens de programação quântica podem ter acesso ao mesmo hardware quântico ou oferecer o mesmo nível de suporte para diferentes dispositivos quânticos.
  • Ferramentas e recursos: as linguagens de programação quântica podem ter diferentes conjuntos de ferramentas e recursos disponíveis para os programadores quânticos. Por exemplo, algumas linguagens de programação quântica podem ter um conjunto mais amplo de simuladores disponíveis, enquanto outras podem oferecer acesso a hardware quântico real para experimentação.
  • Comunidade de usuários: a comunidade de usuários de uma determinada linguagem de programação quântica pode ser um fator importante a considerar. As linguagens de programação quântica com grandes comunidades de usuários podem ter mais suporte e documentação disponíveis, o que pode tornar a aprendizagem e o uso mais fácil e eficiente.

Em resumo, a escolha da melhor linguagem de programação quântica depende das habilidades do programador, da complexidade do projeto, das limitações de hardware, das ferramentas e recursos disponíveis e da comunidade de usuários. É importante avaliar cuidadosamente cada um desses fatores ao escolher uma linguagem de programação quântica para um projeto específico.

5.3 - Detalhamento das linguagens

O Capítulo 4.3 detalha as linguagens de programação com relação à sintaxe, estrutura de dados, ferramentas, funcionalidades, instalação e importação, além de apresentar exemplos de aplicação para cada uma das linguagens de programação quântica.

5.4 - Qiskit

Qiskit é um pacote de programação de código aberto para computação quântica desenvolvida pela IBM, escrito em Python, fornecendo uma plataforma aberta para o desenvolvimento, simulação e executação de algoritmos quânticos em computadores quânticos reais e simulados. Abaixo estão algumas informações sobre a sintaxe, estrutura de dados, ferramentas e funcionalidades da linguagem Qiskit.

Sintaxe

A sintaxe da linguagem Qiskit é baseada em Python, o que significa que a maioria das estruturas de controle de fluxo e operadores aritméticos são semelhantes aos da linguagem Python. No entanto, Qiskit tem algumas diferenças significativas, como o uso de operadores quânticos, que não são encontrados na linguagem Python.

Estrutura de Dados

Os circuitos quânticos em Qiskit são representados por meio de objetos da classe QuantumCircuit, de objetos Python contendo uma lista de portas quânticas. Os estados quânticos são representados por meio de objetos da classe QuantumRegister, enquanto os estados clássicos são representados pelos objetos da classe ClassicalRegister.

Ferramentas

Qiskit oferece uma série de ferramentas de apoio ao desenvolvimento, simulação e execução de algoritmos quânticos. Algumas das principais ferramentas são:

  • qiskit-terra: é a base da plataforma Qiskit, fornece ferramentas para criação de circuitos quânticos e os simula em dispositivos quânticos locais ou remotos.
  • qiskit-aer: é uma biblioteca de simuladores quânticos altamente otimizados, que permitem a simulação de circuitos quânticos em um computador clássico com alta precisão.
  • qiskit-ibmq-provider: é uma interface para acesso a dispositivos quânticos da IBM e execução de circuitos quânticos remotamente.

Funcionalidades

Algumas das principais funcionalidades da linguagem Qiskit são:

  • Criação de circuitos quânticos.
  • Simulação de circuitos quânticos em um computador clássico.
  • Execução de circuitos quânticos em dispositivos quânticos reais.
  • Implementação de algoritmos quânticos, como o algoritmo de Shor e o algoritmo de Grover.
  • Implementação de portas quânticas e operações em qubits.
  • Visualização dos resultados da simulação ou da execução em dispositivos quânticos reais.

Instalação

Para instalar o Qiskit usando o pip do Python, siga os seguintes passos:

  • Abra um terminal ou prompt de comando.
  • Veja se o pip esteja atualizado com o comando: pip install --upgrade pip
  • Instale o Qiskit com o comando: pip install qiskit

Verifique se o Qiskit foi instalado corretamente:

  • Execute o comando: qiskit version
  • Veja a versão do Qiskit instalada em seu sistema.

Dependendo da configuração do seu sistema, pode ser necessário executar o comando com privilégios de administrador (usando sudo no Linux ou McOS, ou executando o terminal ou prompt de comando como administrador no Windows).

Você também pode instalar pacotes adicionais do Qiskit usando o pip.

Por exemplo, para instalar o pacote qiskit-terra execute o comando:

pip install qiskit-terra

Exemplos

Algoritmo de Shor

A seguir está um exemplo de implementação do algoritmo de Shor usando o Qiskit com o Python.

Importamos as bibliotecas necessárias:

from qiskit import QuantumCircuit, Aer, execute
from qiskit.circuit.library import QFT
from qiskit.providers.aer import QasmSimulator
import numpy as np

Definimos o número que queremos fatorar:

n = 21

Definimos o tamanho do registrador quântico para armazenar o número:

n_qubits = n.bit_length()

Criamoso o circuito quântico:

qc = QuantumCircuit(n_qubits * 2, n_qubits)

Aplicamos uma porta hadamard em todos os qubits do primeiro registrador:

for i in range(n_qubits):
  qc.h(i)

Definimos a função f(x) = a^x mod n

a = 3
for i in range(n_qubits):
  qc.x(n_qubits + i)
  for j in range(2 ** i):
    qc.swap(i, n_qubits + i)
    qc.barrier()
  qc.p(2 * np.pi * a * 2 ** i / n, i)
  qc.barrier()

Aplicamos a Transformada de Fourier Quântica (QFT) ao primeiro registrador

qc.append(QFT(n_qubits).inverse(), range(n_qubits))

Medimos o primeiro registrador

qc.measure(range(n_qubits), range(n_qubits))

Executamos o circuito quântico em um simulador de computação quântica:

backend = Aer.get_backend('qasm_simulator')
result = execute(qc, backend=backend, shots=1).result()

Obtemos o resultado da medição

m = result.get_counts(qc)
measurements = list(m.keys())[0]

Convertemos a medição de binário para inteiro

x = int(measurements, 2)

Encontramos o maior divisor comum entre n e x

def gcd(a, b):
  while b != 0:
    a, b = b, a % b
  return a
factor = gcd(x ** (n // 2) + 1, n)

Imprimimos o resultado

if factor == 1 or factor == n:
  print("O algoritmo de Shor falhou.")
else:
  print("Os fatores de", n, "são", factor, "e", n // factor)
Algoritmo quântico de regressão

É possível implementar um algoritmo quântico de regressão usando o Qiskit.

No entanto, atualmente não existem algoritmos quânticos de regressão conhecidos que possam superar os algoritmos clássicos de regressão em termos de precisão ou velocidade, pelo menos para casos gerais. Em vez disso, os algoritmos quânticos de regressão são úteis principalmente para exemplificar como podem ser implementados usando os recursos do Qiskit.

Abaixo, segue um exemplo de como um algoritmo de regressão linear quântico pode ser implementado usando o Qiskit.

Importamos as bibliotecas necessárias:

import numpy as np
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import execute, Aer

Criamos as funções necessárias.

A função get_qc() cria um circuito quântico com q qubits e c registradores clássicos.

def get_qc(q,c):
    qc = QuantumCircuit(q,c)
    qc.h(q) # Inicializa os qubits em estado superposto
    return qc

A função iterar_r() aplica uma transformação unitária theta em r e calcula a saída prevista e o erro.

def iterar_r(r):
    # aplica a transformação unitária ‘theta’
    global q, n_qubits, theta
    for j in range(n_qubits): r(theta[j][0], q[j])

A função iterar_qc() aplica a função iterar_r() e calcula a saída prevista e o erro.

def iterar_qc(qc,q):
    iterar_r(qc.rz)
    qc.cz(q[0], q[1])
    iterar_r(qc.rx)
    # Calcula a saída prevista e o erro
    qc.measure(q[0], c[0])

A função executar_qc() executa o circuito quântico criado com q qubits e c registradores clássicos.

def executar_qc(qc,q):
    iterar_qc(qc,q)
    be = Aer.get_backend('qasm_simulator')
    job = execute(qc, backend=be, shots=1024)
    counts = job.result().get_counts() 
    return -1 if max(counts, key=counts.get) == '1' else 1

Definimos a matriz de entrada X e o vetor de saída y.:

X = np.array([[0, 1], [1, 0], [1, 1], [0, 0]])
y = np.array([0, 1, 1, 0])

Defimos a constante de aprendizado e o número de iterações:

taxa_aprendizado = 0.1
num_iteracoes = 100

Definimos o circuito quântico:

n_qubits = 2
n_outputs = 1
theta = np.random.rand(n_qubits, n_outputs) * 2 * np.pi
q = QuantumRegister(n_qubits)
c = ClassicalRegister(n_outputs)

Realizamos a regressão:

qc = get_qc(q,c)
for i in range(num_iteracoes):
    resultado_predito = executar_qc(qc,q)
    error = y[0] - resultado_predito
    # Atualiza os pesos
    for j in range(n_qubits):
        theta[j][0] += taxa_aprendizado * error * X[0][j]

Executamos o circuito para a entrada X[0]:

qc = obter_qc()
resultado_predito = executar_qc(qc,q)

Imprimimos o resultado predito:

print("A saída prevista para a entrada X[0] é:", resultado_predito)

Este código implementa um algoritmo quântico de regressão que usa a técnica de descida de gradiente para otimizar os pesos dos qubits. A otimização é realizada usando o algoritmo variacional de otimização quântica (VQAO), que é uma técnica de otimização de parâmetros em um circuito quântico variacional.

No algoritmo, o vetor de entrada X é codificado em qubits, e o vetor de pesos é representado por um circuito variacional que é parametrizado por ângulos. Em cada iteração do algoritmo de descida de gradiente, um novo conjunto de ângulos é escolhido, e o circuito variacional é executado em um simulador quântico. O resultado da execução é medido e comparado com o resultado esperado, e o erro é usado para atualizar os valores dos ângulos.

É importante notar que, embora o algoritmo de regressão quântica seja uma área promissora de pesquisa, atualmente ele é limitado em sua aplicabilidade devido à falta de hardware quântico disponível e à alta sensibilidade a erros quânticos. Além disso, a complexidade do algoritmo cresce exponencialmente com o tamanho dos dados de entrada, o que pode tornar a implementação prática em grande escala inviável.

5.5 - Q#

Q# é uma linguagem de programação quântica desenvolvida pela Microsoft. Ela foi projetada para permitir que programadores quânticos escrevam algoritmos quânticos e os executem em computadores quânticos reais ou simulados. Abaixo estão algumas informações sobre sintaxe, estrutura de dados, ferramentas e funcionalidades do Q#.

Sintaxe

  • Q# é uma linguagem de programação baseada em .NET, o que significa que sua sintaxe é semelhante à do C# e outras linguagens .NET.
  • A linguagem usa a notação de bra-ket (|0⟩, |1⟩) para representar os estados quânticos e usa operadores quânticos (como H, X, Y, Z, CNOT, etc.) para realizar operações de portas lógicas quânticas.

Estrutura de dados

O tipo de dados principal no Q# é o qubit, que representa um bit quântico.

Além disso, a linguagem suporta outros tipos de dados quânticos, como qutrits, qudits, e estados densos.

O Q# também suporta tipos de dados clássicos, como int, double e string.

Ferramentas

O Q# é projetado para trabalhar com o Microsoft Quantum Development Kit (QDK), que inclui um simulador quântico e um ambiente de desenvolvimento integrado (IDE) chamado Visual Studio Code.

O QDK também inclui bibliotecas e ferramentas para ajudar na depuração e otimização de programas quânticos.

Funcionalidades

O Q# suporta a realização de portas quânticas, medidas e emaranhamento, bem como a construção de algoritmos quânticos mais avançados, como algoritmos de busca e fatorização.

O Q# também permite a integração com outras linguagens de programação, como C# e Python, para criar programas híbridos.

Exemplos

Algoritmo de Grover

Como a operação GroverAlgorithm depende de um oráculo específico que deve ser implementado para cada problema, é importante explicar como implementar o oráculo para diferentes situações.

Por exemplo, suponha que queiramos encontrar uma entrada específica em uma lista não ordenada de 4 elementos. O oráculo para este problema pode ser implementado da seguinte forma:

operation Oracle(qubits : Qubit[], target : Qubit) : Unit is Adj {
   // Inverte o qubit-alvo se a entrada corresponder ao valor desejado
   if (qubits == [One, Zero, Zero, Zero])  {
      X(target);
   }
}

O oráculo recebe um array de qubits de trabalho e um único qubit-alvo. Ele inverte o qubit-alvo se a entrada corresponder ao valor desejado. Neste exemplo, a entrada desejada é "1000", então se a entrada correspondente for encontrada, o qubit-alvo será invertido.

O algoritmo de Grover é um algoritmo de busca em bancos de dados quânticos. Ele busca uma entrada específica em um banco de dados não ordenado, fornecendo um quadrado mágico que pode melhorar a busca em aproximadamente a raiz quadrada do número de entradas. O algoritmo recebe como entrada uma função booleana que avalia se um elemento é a entrada desejada ou não, e a implementação consiste em uma sequência de transformações em um conjunto de qubits para amplificar a amplitude do estado correspondente à entrada desejada.

O código Q# para implementar o algoritmo de Grover é o seguinte:

namespace Grover {
   open Microsoft.Quantum.Diagnostics;
   namespace Grover {
   open Microsoft.Quantum.Math;
   open Microsoft.Quantum.Canon;
   //
   operation GroverAlgorithm(
      nQubits : Int, oracle: ((Qubit[], Qubit) => Unit)): Unit {
        //
        // Cria um array de nQubits de trabalho
        using (workQubits = Qubit[nQubits]) {
          // Cria um único qubit auxiliar
          using (ancilla = Qubit()) {
              // Inicializa os qubits de trabalho no estado uniforme
              ApplyToEachA(H, workQubits);
              // Define o número de iterações necessárias
              let iterations = IntAsDouble(Power(2.0, nQubits / 2));
              // Aplica as transformações de Grover
              for (i in 1 .. iterations) {
                oracle(workQubits, ancilla);
                ApplyToEachA(X, workQubits);
                ApplyToEachA(H, Most(workQubits));
                Controlled Z(Most(workQubits), Tail(workQubits));
                ApplyToEachA(H, Most(workQubits));
                ApplyToEachA(X, workQubits);
              }
              // Mede os qubits de trabalho
              let result = MultiM(workQubits);
              // Verifica se a entrada encontrada é a desejada
              if (oracle(result, ancilla) == -1) {
                Message("A entrada não foi encontrada.");
              } else {
                Message("A entrada foi encontrada.");
              }
              // Limpa os qubits
              ResetAll(workQubits);
              Reset(ancilla);
           }
         }
      }
   }
}

Este código define uma operação chamada GroverAlgorithm que recebe o número de qubits de trabalho e uma função booleana que atua como o oráculo.

  • A operação começa criando um array de qubits de trabalho e um qubit auxiliar.
  • Em seguida, os qubits de trabalho são inicializados no estado uniforme, usando uma porta Hadamard.
  • O número de iterações necessárias é definido como a raiz quadrada do número de elementos a serem pesquisados.
  • O loop principal do algoritmo aplica as transformações de Grover em cada iteração.
  • Primeiro, o oráculo é chamado para marcar a entrada desejada, fazendo com que seu estado mude de fase.
  • Em seguida, as transformações de Grover são aplicadas, que ampliam a amplitude do estado correspondente à entrada desejada.
  • Finalmente, os qubits de trabalho são medidos e o resultado é exibido na tela.

Para executar o algoritmo de Grover para este problema, podemos criar um arquivo de driver chamado "Driver.qs" com o seguinte código:

namespace Grover {
   open Grover;
   open Microsoft.Quantum.Diagnostics;
   //
   operation RunGrover() : Unit {
      // Define o número de qubits de trabalho
      let nQubits = 4;
      // Cria um array de qubits de trabalho
      using (qubits = Qubit[nQubits]) {
         // Cria um qubit auxiliar
         using (target = Qubit()) {
            // Chama a operação de Grover
            GroverAlgorithm(nQubits, Oracle(qubits, target));
            // Mede os qubits e o qubit-alvo
            let result = MultiM(qubits);
            let targetResult = M(target);
            // Exibe o resultado na tela
            Message($"O resultado da busca foi {result}.");
            Message($"O qubit-alvo foi invertido? {targetResult}.");
            // Limpa os qubits
            ResetAll(qubits);
            Reset(target);
         }
      }
   }
}

Este código define uma operação chamada RunGrover que chama a operação GroverAlgorithm com o número de qubits de trabalho e o oráculo apropriado. Em seguida, ele mede os qubits de trabalho e o qubit-alvo, exibe o resultado na tela e limpa os qubits.

Para executar o algoritmo, basta chamar a operação RunGrover a partir da linha de comando do Q# ou de um ambiente de desenvolvimento integrado (IDE) compatível.

Outra implementação do algoritmo de Grover com Q#:

namespace Quantum.Grover {
    open Microsoft.Quantum.Intrinsic;
    operation ApplyOracle(oracle: (Qubit[] => Unit is Adj + Ctl)): () {
        use qs = Qubit[2];
        H(qs[0]);
        oracle(qs);
        H(qs[0]);
        ResetAll(qs);
    }
    operation GroverSearch(
        oracle: (Qubit[] => Unit is Adj + Ctl), nIterations : Int): () {
        #
        use qs = Qubit[2];
        repeat {
            ApplyOracle(oracle);
            ApplyToEach(H,qs);
            within {
                MeasureInteger(BigEndian(qs), _);
            } apply {
                // Não usamos o resultado da medição
                ResetAll(qs);
            }
        } until (nIterations == 0) {
            set nIterations = nIterations - 1;
        }
    }
}

Neste exemplo, um algoritmo de busca de Grover é implementado usando o Q# usando uma operação quântica nomeada ApplyOracle para aplicar uma operação lógica quântica a um conjunto de qubits. O algoritmo também usa a operação GroverSearch para executar várias iterações do algoritmo de busca de Grover.

Algoritmo de Deutsch-Jozsa

Abaixo temos um código em Q# implementando o algoritmo de Deutsch-Jozsa.

Para isso, é preciso primeiro definir a função "oracle" que representa a função desconhecida ‘f’ que o algoritmo de Deutsch-Jozsa tem como entrada. Vamos assumir que a função ‘f’ seja balanceada, ou seja, retorna 0 para metade das entradas e 1 para a outra metade.

// Define a função "oracle" que representa a função desconhecida f

function DeutschJozsaOracle(qubits: Qubit[]) : Unit {
   // O número de qubits é igual ao logaritmo na base 2 do número de
   // entradas. Para uma função balanceada, a primeira metade das entradas
   // recebe o resultado 0 e a segunda metade recebe o resultado 1.
   // Vamos aplicar uma porta CNOT em cada qubit para garantir que
   // o resultado será 1 em todas as entradas da segunda metade.
   for (i in 0 .. Length(qubits) - 1) {
       if (i < Length(qubits) / 2) {
          // Primeira metade das entradas
          // Nenhum gate é aplicado
       } else {
          // Segunda metade das entradas
          CNOT(qubits[i], qubits[0]);
       }
   }
}

Agora podemos implementar o circuito quântico completo do algoritmo de Deutsch-Jozsa, que consiste em um registro de entrada de n qubits, um qubit auxiliar e um registro de saída de um único qubit.

O algoritmo segue os seguintes passos:

  • Começa aplicando uma porta Hadamard em cada qubit de entrada, colocando-os em uma superposição igual de 0 e 1.
  • Em seguida, o qubit auxiliar é colocado em uma superposição de |1⟩ usando uma porta X e uma porta Hadamard. A função desconhecida f é então aplicada aos qubits de entrada usando a função oracle.
  • Em seguida, uma porta Hadamard é aplicada a cada qubit de entrada novamente e os resultados são medidos. Se a função f for constante, o resultado da medição será 0. Se a função f for balanceada, o resultado da medição será, com alta probabilidade, diferente de 0.
// Define o circuito quântico completo para o algoritmo de Deutsch-Jozsa

operation AlgoritmoDeutschJozsa (qubits: Qubit[]) : Bool {
    // Cria um qubit auxiliar e um qubit de saída
    use aux = Qubit();
    use output = Qubit();
    // Aplica uma porta Hadamard em cada qubit de entrada
    ApplyToEach(Hadamard, qubits);
    // Prepara o qubit auxiliar em uma superposição de |1>
    X(aux);
    Hadamard(aux);
    // Aplica a função desconhecida f usando a função "oracle"
    DeutschJozsaOracle(qubits);
    // Aplica uma porta Hadamard em cada qubit de entrada novamente
    ApplyToEach(Hadamard, qubits);
    // Mede o resultado e retorna verdadeiro se a função for balanceada
    // e falso se for constante
    let result = M(qubits[0]);
    ResetAll(qubits);
    Reset(aux);
    Reset(output);
    return result == One;
}

Para executar o algoritmo, podemos usar o simulador de Q# incluído no kit de desenvolvimento do Microsoft Quantum. Vamos supor que nosso algoritmo esteja salvo em um arquivo chamado "DeutschJozsa.qs". Podemos criar um novo arquivo chamado "Driver.qs" para chamar o algoritmo e executá-lo com o simulador. O código do "Driver.qs" ficaria assim:

// Importa o namespace do algoritmo de Deutsch-Jozsa
open DeutschJozsa;
// Define o número de qubits de entrada
let n = 4;
// Cria um array de qubits de entrada
using (qubits = Qubit[n]) {
    // Executa o algoritmo de Deutsch-Jozsa
    let result = AlgoritmoDeutschJozsa(qubits);
    // Exibe o resultado
    txt = "balanceada" if result else "constante"
    Message($"A função é {txt}.");
}

Neste exemplo, definimos o número de qubits de entrada como 4, mas isso pode ser alterado conforme necessário. Em seguida, criamos um array de qubits usando a palavra-chave "using" para garantir que eles sejam liberados automaticamente após o uso. Por fim, chamamos o algoritmo de Deutsch-Jozsa e armazenamos o resultado em uma variável. Depois, exibimos o resultado no console usando a função "Message".

Para executar o código, basta salvar os arquivos "DeutschJozsa.qs" e "Driver.qs" em um mesmo diretório e abrir um terminal na pasta. Em seguida, execute o seguinte comando para compilar e executar o código com o simulador:

dotnet run

Após a execução, o resultado deve ser exibido no console. Se a função for balanceada, o resultado será "A função é balanceada." Se a função for constante, o resultado será "A função é constante."

Algoritmo de Shor

O algoritmo de Shor é um algoritmo quântico para fatorar números inteiros grandes em tempo polinomial, com eficiência exponencial em relação ao tamanho do número a ser fatorado. É importante porque a segurança de muitos sistemas criptográficos é baseada na dificuldade de fatoração de números grandes.

O algoritmo consiste em duas partes: uma transformada de Fourier quântica e uma estimativa de período quântica.

Em Q#, podemos implementar o algoritmo de Shor da seguinte forma:

namespace Quantum.Samples.Shor {
  open Microsoft.Quantum.Math;
  open Microsoft.Quantum.Canon;
  open Microsoft.Quantum.Convert;
  open Microsoft.Quantum.Diagnostics;
  //
  // Implementa a transformada de Fourier quântica em n qubits
  operation QFT(n : Int, q : Qubit[]) : Unit is Adj + Ctl {
    for i in 0 .. n - 1 {
      H(q[i]);
      for j in i + 1 .. n - 1 {
        ControlledPhaseShift(1.0 / (1 <<< (j - i)), [q[j]], q[i]);
      }
    }
    for i in 0 .. n - 1 { X(q[i]); }
    for i in 0 .. n / 2 - 1 { SWAP(q[i], q[n - 1 - i]); }
  }
  //
  // Implementa a estimativa de período quântica
  operation EstimatePeriod(a : Int, N : Int) : Int {
     using (qubits = Qubit[2 * Ceiling(Log2(N))]) {
       let N_ = big_endian(N);
       let a_ = big_endian(a);
       // Aplica uma transformação modular em um registrador auxiliar
       let aux = ModularExp(a_, qubits, N_);
       // Aplica a transformada de Fourier quântica no reg.aux.
       QFT(2 * Ceiling(Log2(N)), aux);
       // Mede os qubits do registrador auxiliar
       let result = MeasureInteger(LittleEndian(aux));
       // Calcula o período estimado
       let period = 1 <<< (2 * Ceiling(Log2(N)) - result.Length);
       return period;
     }
  }
  // Implementa o algoritmo de Shor
  operation Shor(N : Int) : (Int, Int) {
    // Verifica se N é par ou se é um primo
    if (N % 2 == 0) { return (2, N / 2); }
    if (IsPrime(N)) { return (N, 1); }
    // Escolhe um valor aleatório entre 1 e N-1
    mutable a = 0;
    repeat {
       set a = RandomInt(2, N - 1);
    } until (GreatestCommonDivisor(a, N) == 1);
    // Estima o período r de a mod N
    let r = EstimatePeriod(a, N);
    // Se r for ímpar ou a^(r/2) mod N = -1, tenta novamente
    if (r % 2 == 1 || PowModI(a, r / 2, N) == N - 1) {
       return Shor(N);
    } 
    // Calcula os fatores de N
    let factor1 = GreatestCommonDivisor(PowModI(a, r / 2, N) + 1, N);
    let factor2 = GreatestCommonDivisor(PowModI(a, r / 2, N) - 1, N);
    return (factor1,factor2);
  }
}

O código implementa o algoritmo quântico de Shor, que roda em tempo polinomial para fatorar um número inteiro $N$ em seus fatores primos.

A operação Shor recebe como entrada o inteiro $N$ e retorna uma tupla (fator1, fator2) contendo os fatores primos de $N$.

O algoritmo primeiro verifica se $N$ é par ou primo e retorna os fatores nesses casos. Caso contrário, escolhe um número inteiro aleatório a entre $1$ e $N-1$ que é coprimo com $N$. Em seguida, ele estima o período $r$ da função $f(x) = ax mod N$ usando a operação EstimatePeriod. Se $r$ for ímpar ou $ar/2 mod N = -1$, significa que o algoritmo não conseguiu encontrar os fatores e tenta novamente com um a diferente. Caso contrário, ele calcula os maiores divisores comuns de $ar/2 + 1$ e $ar/2 - 1$ com $N$, que são os fatores primos de $N$.

A operação EstimatePeriod implementa a transformada de Fourier quântica e a exponenciação modular quântica. A transformada de Fourier quântica é aplicada a um registro que contém o resultado da exponenciação modular. O resultado é então medido, e o período é estimado a partir do resultado da medição.

Observe que este código usa a linguagem de programação Q#, que é projetada para computação quântica. Ele inclui funções e operações integradas para operações quânticas, como H para o gate de Hadamard, ControlledPhaseShift para portas de deslocamento de fase controladas e ModularExp para exponenciação modular.

Na última linha do código, são retornados os fatores encontrados.

O algoritmo de Shor funciona porque, se encontrarmos o período $r$ de $a mod N$, podemos fatorar $N$ usando a seguinte identidade:

$ar ≡ 1 (mod N)$

Se $r$ for par, então podemos escrever:

$ar ≡ -1 (mod N)$

$ar/2 ≡ ±i (mod N)$

onde $i$ é a unidade imaginária. Então, a fatoração de $N$ pode ser encontrada como o máximo divisor comum de $ar/2 ± 1$ e $N$. Se r for ímpar, ou se $$ar/2 mod N = -1, precisamos tentar novamente com outro valor de $a$.

O algoritmo de Shor tem uma complexidade de tempo exponencial em relação ao número de dígitos de N, mas é muito mais eficiente do que os algoritmos clássicos conhecidos para fatoração de inteiros grandes. É importante notar que a implementação do algoritmo de Shor em Q# requer uma compreensão profunda de teoria quântica e matemática, além de habilidades em programação quântica.

Podemos codificar de forma diferente.

Antes de começar com o novo código, é importante lembrar que o algoritmo de Shor é composto por duas partes: uma parte clássica e outra parte quântica:

  • Parte clássica: usada para preparar o estado inicial do sistema quântico e para fazer medições depois da parte quântica.
  • Parte quântica: responsável por encontrar o período de uma função modular.

Primeiro, vamos definir a função que calcula o máximo divisor comum entre dois números:

function AlgoritmoEuclides(a : Int, b : Int) : Int {
  if (b == 0) {
    return a;
  } else {
    return AlgoritmoEuclides (b, a % b);
  }
}

Essa função implementa o algoritmo de Euclides, que é usado para calcular o máximo divisor comum entre dois números.

Em seguida, vamos definir a função que encontra um fator de um número inteiro composto:

function EncontrarFator(N : Int) : Int {
  mutable i = 1;
  while (true) {
    i += 1;
    a = PowMod(2, i, N);
    if (a == 1) {
      continue;
    }
    d = AlgoritmoEuclides (a - 1, N);
    if (d != 1 && d != N) {
      return d;
    }
  }
}

Essa função implementa o método de fatoração de base 2. O método começa com i = 1 e calcula 2i mod N. Se o resultado for igual a 1, então o algoritmo tenta um novo valor de i. Se o resultado for diferente de 1, o algoritmo calcula o máximo divisor comum entre a-1 e N. Se o máximo divisor comum for diferente de 1 e N, então ele encontrou um fator de N.

Agora, vamos definir a parte quântica do algoritmo de Shor:

operation AlgoritmoShor (N : Int, precision : Int) : Int[] {
  // Prepara os qubits para a parte quântica do algoritmo
  using (qubits = Qubit[precision]) {
    X(qubits[0]);
    for (i in 1 .. precision - 1) {
      H(qubits[i]);
    }
    // Implementa a parte quântica do algoritmo
    repeat {
      // Aplica a transformada de Fourier quântica
      AplicarQFT(qubits);
      // Mede os qubits
      result = MultiM(qubits);
      // Verifica se o resultado é válido
      r = VerificarResultado(result, N, precision);
    } until (r != -1);
    // Libera os qubits e retorna o resultado
    ResetAll(qubits);
    return [r, N / r];
  }
}

Essa função começa preparando os qubits para a parte quântica do algoritmo. O primeiro qubit é inicializado em estado |1⟩, enquanto os demais qubits são colocados em superposição de estados usando a porta Hadamard (Hadamard gate). Em seguida, a função implementa um loop que executa a Transformada de Fourier Quântica, mede os qubits e verifica se o resultado é válido.

A função AplicarQFT implementa a transformada de Fourier quântica.

operation AplicarQFT(qubits : Qubit[]) : Unit is Adj+Ctl {
   let n = Length(qubits);
   for (i in 0 .. n - 1) {
      ApplyPhase(i, qubits);
      ApplyHadamard(i, qubits);
      for (j in i + 1 .. n - 1) {
         ApplyControlledPhase(qubits[j], qubits[i], Pow(2.0, n - j));
      }
   }
}

É uma operação que opera sobre a superposição dos qubits, de forma que ela transforma a amplitude de cada estado em uma fase correspondente ao valor da sua amplitude. A Transformada de Fourier Quântica é implementada usando portas de fase e portas de Hadamard, de forma similar à implementação clássica da Transformada de Fourier.

A função MultiM mede todos os qubits do sistema e retorna o resultado em um array de booleanos.

function MultiM(qubits : Qubit[]) : Bool[] {
  mutable resultados = new Bool[Length(qubits)];
  for (i in 0 .. Length(qubits) - 1) {
    set resultados w/= i <- M(qubits[i]);
  }
  return resultados;
}

A função VerificarResultado verifica se o resultado da medição é válido, recebendo como entrada o resultado da medição, o número N e a precisão dos qubits.

A função usa o resultado da medição para calcular uma estimativa do período da função modular.

Se a estimativa do período for correta, então a função retorna o valor do fator encontrado. Caso contrário, a função retorna -1, indicando que o resultado não é válido.

function VerificarResultado(
result : Bool[], N : Int, precision : Int) : Int {
  let n = 2 ^ precision;
  let r = 0;
  for (i in 0 .. precision - 1) {
    r = r * 2 + (result[i] ? 1 | 0);
    let a = PowMod(2, r, N);
    if (a == 1) {
      return -1;
    }
    let d = AlgoritmoEuclides(a - 1, N);
    if (d != 1 && d != N) {
      return d;
    }
    return -1;
  }
}

Por fim, a função AlgoritmoShor une todas as funções em um algoritmo completo que recebe como entrada o número N e a precisão dos qubits e retorna um array com os fatores de N.

operation AlgoritmoShor(N : Int, precision : Int) : Int[] {
  // Prepara os qubits para a parte quântica do algoritmo
  using (qubits = Qubit[precision]) {
    X(qubits[0]);
    for (i in 1 .. precision - 1) {
      H(qubits[i]);
    }
    // Implementa a parte quântica do algoritmo
    repeat {
      // Aplica a transformada de Fourier quântica
      AplicarQFT(qubits);
      // Mede os qubits
      result = MultiM
      // Verifica o resultado da medição
      let factor = VerificarResultado (result, N, precision);
      if (factor != -1) {
          return [factor, N / factor];
      }
    } until (false);
  }
  return [-1];
}

O algoritmo completo começa criando um array de qubits com a precisão desejada e preparando-os para a parte quântica do algoritmo. Em seguida, ele executa um loop que implementa a parte quântica do algoritmo até que o resultado seja válido. Se o resultado for válido, então a função retorna os fatores de N encontrados. Caso contrário, a função retorna [-1], indicando que o algoritmo não encontrou nenhum fator.

Para usar o algoritmo, basta chamar a função AlgoritmoShor com o número N e a precisão desejada. Por exemplo, para fatorar o número N = 15 com precisão de 4 qubits, podemos chamar a função da seguinte forma:

let fatores = AlgoritmoShor(15, 4);
Message($"Fatores de 15: {fatores[0]}, {fatores[1]}");

O resultado da execução será:

Fatores de 15: 3, 5

O algoritmo de Shor é uma demonstração impressionante do poder dos computadores quânticos e sua capacidade de executar tarefas que seriam impraticáveis ou impossíveis para computadores clássicos. No entanto, o algoritmo ainda é altamente teórico e requer um número muito grande de qubits para fatorar números significativos. À medida que a tecnologia dos computadores quânticos avança, espera-se que algoritmos como o de Shor possam ser implementados em sistemas reais, abrindo novas possibilidades para a criptografia e a computação em geral.

Algoritmo de regressão

É possível implementar um algoritmo de regressão em Q# utilizando uma sequência de bits onde 1 representa uma amostra verdadeira e 0 representa uma amostra falsa. Aqui está um exemplo simples de como isso pode ser feito:

open Microsoft.Quantum.Math;
open Microsoft.Quantum.Diagnostics;
// Define uma função que calcula o produto interno entre dois vetores
function InnerProduct(a : Double[], b : Double[]) : Double {
    mutable result = 0.0;
    for (i in 0 .. Length(a) - 1) {
        set result += a[i] * b[i];
    }
    return result;
}

Declaração da operação de regressão:

// Define a operação principal que realiza a regressão
operation Regression(data : Int[]) : Double[] {
    // Define a matriz de dados X
    let X = new Double[,]
        [ [ 1.0 , 1.0 , 0.0 ],
          [ 1.0 , 0.0 , 1.0 ],
          [ 1.0 , 0.0 , 0.0 ],
          [ 1.0 , 1.0 , 1.0 ] ];
    // Define o vetor de resultados y
    let y = new Double[] { 1.0, 1.0, 0.0, 1.0 };
    // Define o vetor de pesos inicial
    let w = new Double[] { 0.0, 0.0, 0.0 };
    // Define o tamanho do passo de atualização
    let alpha = 0.1;
    // Define o número máximo de iterações
    let maxIter = 100;
    // Realiza a otimização dos pesos usando o gradiente descendente
    for (i in 1 .. maxIter) {
        // Calcula a previsão atual usando os pesos atuais
        let yPred = new Double[Length(data)];
        for (j in 0 .. Length(data) - 1) {
            set yPred[j] = InnerProduct(X[j], w);
        }
        // Calcula o erro
        let error = new Double[Length(data)];
        for (j in 0 .. Length(data) - 1) {
            set error[j] = yPred[j] - y[j];
        }
        // Atualiza os pesos usando o gradiente descendente
        for (j in 0 .. Length(w) - 1) {
            mutable gradient = 0.0;
            for (k in 0 .. Length(data) - 1) {
                set gradient += error[k] * X[k, j];
            }
            set w[j] -= alpha * gradient;
        }
    }
    // Retorna os pesos finais
    return w;
}

Execução da operação de regressão:

// Executa a operação principal
operation RunRegression() : Unit {
    // Define a sequência de dados de entrada
    let data = [1, 1, 0, 1];
    // Chama a operação de regressão
    let weights = Regression(data);
    // Imprime os pesos finais
    Message("Final weights: ");
    for (i in 0 .. Length(weights) - 1) {
        Message(weights[i]);
    }
}

Neste exemplo, a regressão é realizada usando a técnica de gradiente descendente, que atualiza os pesos iterativamente com base no erro de previsão. A matriz X representa a matriz de entrada de dados, onde cada linha representa uma amostra e cada coluna representa uma característica.

O vetor y representa a variável de saída a ser prevista. O vetor y é um vetor unidimensional que contém os valores reais de saída correspondentes às amostras de entrada na matriz X. Em outras palavras, cada elemento do vetor y é a saída real correspondente à linha correspondente na matriz X. O objetivo do algoritmo é ajustar os pesos para que a saída prevista do modelo, dada uma entrada, seja o mais próxima possível dos valores reais do vetor y.

Algoritmo quântico de soma

O algoritmo de soma quântica, também conhecido como Algoritmo de Fourier Quântico, é um algoritmo quântico que permite somar uma sequência de números em tempo quase-linear, em relação ao número de elementos da sequência.

Aqui está um exemplo de implementação de algoritmo de soma quântica em Q#:

namespace Quantum.Sum {
  open Microsoft.Quantum.Diagnostics;
  open Microsoft.Quantum.Math;
  open Microsoft.Quantum.Measurement;
  open Microsoft.Quantum.Convert;
  open Microsoft.Quantum.Canon;
  operation QuantumSum(input : Qubit[], output : Qubit) : Unit {
    let n = Length(input);
    using (ancilla = Qubit()) {
      H(ancilla);
      for (i in 0 .. n - 1) {
        ControlledRotate(Phase(1.0 / Pow2(i)), [input[i]], ancilla);
      }
      Controlled SWAP(input, output, ancilla);
    }
  }
}

O código acima implementa a operação QuantumSum, que recebe uma sequência de qubits como entrada e um qubit auxiliar como saída. A operação usa um qubit auxiliar para realizar a Transformada de Fourier Quântica, que é aplicada à sequência de entrada. O resultado é armazenado no qubit auxiliar.

Este algoritmo pode ser utilizado para somar uma sequência de números quânticos, de forma eficiente e precisa.

Transformada de Fourier Clássica (CFT)

A Transformada de Fourier é uma técnica matemática que permite representar uma função no domínio do tempo (ou espaço) em termos de suas componentes no domínio da frequência.

A Transformada de Fourier Clássica (CFT) transforma uma função contínua no domínio do tempo em uma função contínua no domínio da frequência. Ela é definida por:

F(w) = \int f(t) e^{-iwt} \, dt

onde f(t) é a função original no domínio do tempo, F(w) é a função transformada no domínio da frequência, e i é a unidade imaginária.

A CFT é muito utilizada em diversas áreas, como processamento e análise de sinais e imagens, comunicações, óptica, física e matemática, entre outras.

É importante notar que a CFT é uma operação que atua em funções contínuas e não é diretamente aplicável a sinais discretos, como é o caso de sinais digitais. Para tratar sinais digitais, é utilizada a Transformada Discreta de Fourier (DFT), que é uma versão discreta da CFT.

Transformada de Fourier Quântica (QFT)

A Transformada de Fourier Quântica (QFT) é uma versão quântica da Transformada de Fourier Clássica. Ela é uma operação fundamental em computação quântica, e é amplamente utilizada em algoritmos quânticos como o algoritmo de Shor para fatoração de números inteiros e o algoritmo de Grover para busca em bancos de dados não estruturados.

A QFT opera em estados quânticos, representados por qubits, e é definida por uma série de rotações controladas que são aplicadas aos qubits de entrada. A transformada de Fourier quântica tem uma estrutura semelhante à transformada de Fourier clássica, mas é baseada em conceitos quânticos, como superposição e emaranhamento.

A QFT é uma transformada unitária, o que significa que é reversível e conserva a norma. Ela é muito importante para o desenvolvimento de algoritmos quânticos eficientes, pois permite a manipulação de informações quânticas em domínios de frequência, possibilitando a resolução de problemas de forma mais eficiente do que os algoritmos clássicos equivalentes.

Uma das características mais importantes da QFT é a sua habilidade de realizar operações em paralelo. Ela permite que várias operações sejam realizadas em paralelo em um único processador quântico, o que pode levar a uma enorme aceleração em comparação com algoritmos clássicos equivalentes.

5.6 - Cirq

Cirq é uma biblioteca de programação quântica desenvolvida pelo Google, disponibilizada como um pacote do Python, contendo uma estrutura de código aberto para programação de computadores quânticos, permitindo aos usuários escreverem, manipularem e otimizarem circuitos quânticos e, em seguida, executá-los em computadores quânticos e simuladores quânticos.

O Cirq fornece abstrações úteis para lidar com os ruidosos computadores quânticos de escala intermediária de hoje, onde os detalhes do hardware são vitais para que resultados de ponta sejam alcançados.

A seguir estão informações sobre sintaxe, estrutura de dados, ferramentas e funcionalidades.

Sintaxe

A sintaxe do Cirq é baseada em Python, mas inclui algumas construções específicas de programação quântica, como o uso de circuitos quânticos e operações quânticas. A seguir, está um exemplo simples de um circuito quântico em Cirq:

import cirq
# Cria um circuito quântico de 2 qubits
circuito = cirq.Circuit()
q0 = cirq.LineQubit(0)
q1 = cirq.LineQubit(1)
# Adiciona uma porta Hadamard no primeiro qubit
circuito.append(cirq.H(q0))
# Adiciona uma porta CNOT entre o primeiro e o segundo qubits
circuito.append(cirq.CNOT(q0, q1))
# Imprime o circuito
print(circuito)

A saída do código acima fica:

0: ───H───@───
          │
1: ───────X───

Estrutura de dados

O Cirq possui uma série de classes e estruturas de dados para representar circuitos quânticos, operações quânticas e outros conceitos relacionados à programação quântica. Alguns exemplos incluem:

  • cirq.Circuit: classe para representar um circuito quântico composto por uma sequência de operações quânticas.
  • cirq.Gate: classe abstrata para representar uma operação quântica genérica, que pode ser aplicada a um ou mais qubits.
  • cirq.QubitId: classe para representar um identificador único para um qubit em um circuito quântico.

Ferramentas

O Cirq inclui uma série de ferramentas para facilitar o desenvolvimento e a execução de programas quânticos. Algumas delas incluem:

  • cirq.Simulator: classe para simular a execução de um circuito quântico em um computador clássico.
  • cirq.google: módulo com ferramentas específicas para usar o Cirq com hardware quântico da Google, como o processador de qubits Sycamore.
  • cirq.vis: módulo com ferramentas de visualização para circuitos quânticos, como o cirq.vis.plot_circuit para exibir um circuito em um diagrama.

Funcionalidades

O Cirq oferece uma série de funcionalidades para programação quântica, incluindo:

  • Construção e manipulação de circuitos quânticos.
  • Implementação de operações quânticas e portas personalizadas.
  • Simulação de circuitos quânticos em um computador clássico.
  • Otimização de circuitos quânticos para melhorar a eficiência e a precisão.
  • Integração com hardware quântico da Google e outros fornecedores.

Instalação

Para instalar o Cirq no Python usando o pip, siga estes passos:

  • Certifique-se de ter o Python 3.6 ou superior instalado em seu sistema.
  • Abra o terminal ou prompt de comando.
  • Digite o seguinte comando e pressione Enter para instalar o Cirq usando o pip: pip install cirq
  • O pip baixará e instalará todas as dependências necessárias para o Cirq
  • Após a conclusão da instalação, importe o Cirq em seu código Python usando: import cirq

Exemplos

Algoritmo de Shor com Cirq

Os módulos cirq e numpy são importados.

import cirq
import numpy as np

A função shor_period_finding é declarada retornando um circuito quântico que calcula o período de ax % N usando o algoritmo de Shor.

def shor_localizar_periodo(a, N, n_qubits):
  # Retorna um circuito quântico que calcula o período
  # de a^x % N usando o algoritmo de Shor.
  # Criar os registradores quânticos
  x = cirq.LineQubit.range(n_qubits)
  y = cirq.LineQubit.range(n_qubits, 2 * n_qubits)
  # Criar o circuito quântico
  circuito = cirq.Circuit()
  # Aplicar as portas H no registrador y
  circuito.append(cirq.H.on_each(y))
  # Aplica as potências de U_a controladas
  for i in range(n_qubits):
    expoente = 2 ** i
    controlled_Ua = cirq.ControlledGate(
        cirq.ops.Ry(np.pi / expoente)(y[i])) ** expoente
    circuito.append(controlled_Ua.on(x[:n_qubits], y[:n_qubits]))
  # Aplicar a transformada de Fourier quântica no registrador y
  circuito.append(cirq.qft(*y).inverse())
  # Mede os qubits do registrador y
  circuito.append(cirq.measure(*y, key='y'))
  return circuito

A função shor executa o algoritmo de Shor para fatorar N.

def shor(N, n_shots):
  # Executa o algoritmo de Shor para fatorar N
  # Encontra um a que é coprimo com N
  a = 2
  while np.gcd(a, N) != 1: a += 1
  # Calcula o período de a^x % N
  n_qubits = int(np.ceil(np.log2(N)))
  circuit = shor_localizar_periodo (a, N, n_qubits)
  # Executa o circuito quântico várias vezes para obter os resultados
  simulator = cirq.Simulator()
  results = simulator.run(circuit, repetitions = n_shots)
  # Encontra o período usando a estimativa de fase
  y = results.measurements['y'][0]
  r = int(''.join(str(i) for i in y), 2)
  if r % 2 != 0: r *= 2
  # Verifica se r é o período correto
  if a ** (r // 2) % N == N - 1: r //= 2
  # Calcula os fatores de N
  p = np.gcd(a ** (r // 2) + 1, N)
  q = np.gcd(a ** (r // 2) - 1, N)
  return p, q

Para executar o algoritmo, basta chamar a função shor com o número a ser fatorado e o número de repetições do circuito quântico para obter os resultados desejados. Por exemplo:

p, q = shor(35, 1000)
print('p =', p)
print('q =', q)

Este exemplo deve imprimir "p = 5" e "q = 7", que são os fatores primos de 35. Note que o algoritmo pode levar algum tempo para ser executado, especialmente para números grandes.

5.7 - PyQuil

O PyQuil é um módulo Python para programação quântica, desenvolvido pela Rigetti Computing. A seguir, estão informações sobre sua sintaxe, estrutura de dados, ferramentas e funcionalidades:

Sintaxe

O PyQuil é baseado em Python e sua sintaxe é bastante similar à do Python convencional, permitindo a criação de circuitos quânticos usando instruções Python padrão, como loops, condições e funções.

A seguir, está um exemplo simples de um circuito quântico em PyQuil.

Importamos os módulos necessários:

from pyquil import Program
from pyquil.gates import *

Criamos um programa quântico com 2 qubits:

p = Program()
qubits = p.qubits(2)

Adiciona uma porta Hadamard no primeiro qubit:

p += H(qubits[0])

Adicionamos uma porta CNOT entre o primeiro e o segundo qubits:

p += CNOT(qubits[0], qubits[1])

Imprimimos o programa:

print(p)

Estrutura de dados

PyQuil possui uma série de classes e estruturas de dados para representar circuitos quânticos, operações quânticas e outros conceitos relacionados à programação quântica. Alguns exemplos incluem:

  • pyquil.quil.Program: classe para representar um programa quântico composto por uma sequência de instruções quânticas.
  • pyquil.quilatom.QuilAtom: classe abstrata para representar uma operação quântica genérica, que pode ser aplicada a um ou mais qubits.
  • pyquil.quil.Qubit: classe para representar um qubit em um circuito quântico.

Ferramentas

PyQuil inclui uma série de ferramentas para facilitar o desenvolvimento e a execução de programas quânticos. Algumas delas incluem:

  • pyquil.api.QVMConnection: classe para simular a execução de um circuito quântico em um computador clássico.
  • pyquil.api.QuantumComputer: classe para representar um hardware quântico real ou simulado.
  • pyquil.api.Job: classe para representar um trabalho em um processador quântico, que pode ser monitorado e recuperado posteriormente.

Funcionalidades

PyQuil oferece uma série de funcionalidades para programação quântica, incluindo:

  • Construção e manipulação de programas quânticos.
  • Implementação de operações quânticas e portas personalizadas.
  • Simulação de circuitos quânticos em um computador clássico.
  • Integração com hardware quântico da Rigetti Computing e outros fornecedores.
  • Ferramentas para depuração e teste de programas quânticos.

Instalação

Para instalar o PyQuil no Python usando o pip, siga estes passos:

  • Certifique-se de ter o Python 3.6 ou superior instalado em seu sistema.
  • Abra o terminal ou prompt de comando.
  • Digite o seguinte comando para instalar o PyQuil usando o pip: pip install pyquil
  • Espere o pip baixar e instalar todas as dependências necessárias para o PyQuil.
  • Importar o PyQuil em seu código Python usando: import pyquil

Pronto! Agora você pode usar o PyQuil em seus projetos de computação quântica.

Exemplos

Algoritmo de Shor

Segue abaixo um exemplo de implementação do algoritmo de Shor com PyQuil.

Importamos as bibliotecas necessárias:

import numpy as np
from pyquil import Program, get_qc
from pyquil.gates import *
from pyquil.api import WavefunctionSimulator

Definimdos a função para calcular o máximo divisor comum (gcd):

def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

Definimos a função para realizar a transformada de Fourier quântica (QFT):

def qft(qubits):
    programa = Program()
    for i in range(len(qubits)):
        programa += H(qubits[i])
        for j in range(i+1, len(qubits)):
            programa += CPHASE(np.pi/(2**(j-i)), qubits[j], qubits[i])
        programa += SWAP(qubits[i], qubits[len(qubits)-1-i])
    return programa

Definimos a função para aplicar a porta Ua:

def aplicar_ua(programa, a, N, n, ancilla):
    for i in range(n):
        programa += H(ancilla[i])
    for j in range(n):
        x = pow(a, 2**j, N)
        for i in range(N):
            if i == x:
                programa += CNOT(ancilla[j], ancilla[n-1])
            elif i > x:
                programa += Z(ancilla[n-1])
                break

Definimos a função para realizar a medição:

def medicao(programa, qubits):
    ro = programa.declare('ro', 'BIT', len(qubits))
    for i in range(len(qubits)):
        programa += MEASURE(qubits[i], ro[i])
    return programa

Definimos os valores de entrada:

N = 15
n = 4

Criamos o registrador quântico:

qubits = list(range(n+1))

Definimos o programa:

programa = Program()

Aplicamos a porta H nos qubits:

for i in range(n):
    programa += H(qubits[i])

Adicionamos o qubit ancilla:

ancilla = [n]

Aplicamos a porta X no qubit ancilla:

programa += X(ancilla[0])

Aplicamos a transformada de Fourier quântica (QFT):

programa += qft(qubits[:n])

Aplicamos a porta Ua:

a = 7
aplicar_ua(programa, a, N, n, ancilla)

Aplicamos a transformada de Fourier quântica inversa (QFT^-1):

programa += qft(qubits[:n]).dagger()

Medimos os qubits:

programa = medicao(programa, qubits[:n])

Criamoso simulador de função de onda:

wf_sim = WavefunctionSimulator()

Rodamos o programa no simulador de função de onda:

wavefunction = wf_sim.simulate(programa)

Extraimos a informação do registrador clássico:

outcomes = wavefunction.get_outcome_probs()

Calculamos o resultado:

result = 0
for outcome in outcomes:
    if outcome[0:n] == '1' * n:
        measured = int(outcome[n:], 2)
        factor = gcd(a ** (measured//2) + 1, N)
        if factor not in [1, N]:
            result = factor

Imprimimos o resultado:

print(f'O fator de {N} é {result}.')

Este código implementa o algoritmo de Shor para fatorar o número 15.

O resultado esperado é o fator de 15 igual a 3. É importante notar que o algoritmo de Shor é um algoritmo probabilístico e a resposta correta pode não ser encontrada em todas as execuções.

Além disso, a complexidade do algoritmo cresce exponencialmente com o tamanho do número a ser fatorado, tornando inviável na prática sua utilização para números grandes.

Arduino
Coautor
Betobyte
Autor
Autores
||| Áreas ||| Estatística ||| Python ||| Projetos ||| Dicas & Truques ||| Quantum ||| Programação Quântica || Programação Quântica || Aulas | Introdução (Introdução à Mecânica, Computação e Programação Quânticas) | Mecânica Quântica (Mecânica Quântica) | Modelos do Hidrogênio (Modelos de Bohr e Shrödinger para o átomo de hidrogênio) | Computação Quântica (Computação Quântica) | Programação Quântica (Programação Quântica) | Programação Quântica com Qiskit (Programação Quântica com Qiskit) | IBM Quantum Experience (Computadores quânticos reais com a IBM Quantum Experience) |