MESTRADO INTEGRADO PROFISSIONAL EM COMPUTAÇÃO APLICADA – MPCOMP Universidade Estadual do Ceará - UECE Centro de Ciências Tecnológicas - CCT Instituto Federal de Educação, Ciência e Tecnologia do Ceará - IFCE Pró-Reitoria de Pós-Graduação – ProPG Flavio Jesus de Souza Emprego da Computação Quântica em Problemas de Difícil Decisão Rio de Janeiro 2015 i Flavio Jesus de Souza Emprego da Computação Quântica em Problemas de Difícil Decisão Dissertação apresentada ao Programa de Mestrado Integrado Profissional em Computação Aplicada da Universidade Estadual do Ceará e do Instituto Federal de Educação, Ciência e Tecnologia do Ceará, como requisito parcial para obtenção do Grau de Mestre em Computação Aplicada. Orientador: Prof. DSc. Flávio Luis de Mello. Rio de Janeiro 2015 ii Dados Internacionais de Catalogação na Publicação Universidade Estadual do Ceará Biblioteca Central Prof. Antônio Martins Filho A999x Souza, Flavio Jesus Emprego da Computação Quântica em Problemas de Difícil Decisão/ Flavio Jesus de Souza. – 2015. XXf. :il. color., enc. ; 30 cm. Dissertação (Mestrado) – Universidade Estadual do Ceará, Centro de Ciências e Tecnologia, Curso de Mestrado Profissional em Computação Aplicada, Fortaleza, 2015. Área de concentração: Sistemas de Apoio a Decisão Orientação: Prof. D. Sc. Flávio Luis de Mello Título. Emprego da Computação Quântica em Problemas de Difícil Decisão Introdução. 2. Complexidade. 3. Algoritmos Quânticos. 4. Problemas Experimentais. 5. Problema das N-Rainhas. 6. Conclusão CDD: 999.99 iii FLAVIO JESUS DE SOUZA EMPREGO DA COMPUTAÇÃO QUÂNTICA EM PROBLEMAS DE DIFÍCIL DECISÃO Dissertação apresentada ao Curso de Mestrado Profissional em Computação Aplicada da Universidade Estadual do Ceará, como requisito parcial para a obtenção do grau de Mestrado em Computação. Defesa em: 04/03/2015 BANCA EXAMINADORA Flávio Luis de Mello, D. Sc. (UFRJ) Presidente (Orientador) Marcos José Negreiros Gomes, D. Sc. (UECE) Membro Interno Julio Cesar Duarte, D. Sc. (IME) Membro externo Francisco Henrique de Freitas Viana, D. Sc. (CEFET) Membro externo iv RESUMO O presente trabalho consiste no uso da computabilidade quântica para solucionar problemas de difícil decisão. Dessa forma, a computação quântica foi amplamente trabalhada em problemas de busca com o intuito de gerar uma familiaridade com esse tipo de computação. Com isso, foi possível a construção de um algoritmo quântico para resolver um problema sofisticado da recreação matemática chamado N-Rainhas. O algoritmo foi implementado em um simulador de computação quântica, permitindo encontrar soluções para problemas com até 128 (cento e vinte oito) rainhas. Palavras-Chave: Computação Quântica, Processo de Tomada de Decisão, Problema das N-Rainhas. v ABSTRACT This work aims to use quantum computability to solve difficult decision problems. Therefore, quantum computing was apply to search problems in order to generate a knowledge with this type of computing. Moreover, a quantum algorithm was construct in order to solve a problem of sophisticated mathematical exercise called N-Queens. The algorithm was implemented in a quantum computing simulator obtaining solutions for problems with up to 128 (one hundred twenty eight) queens. Key Words: Quantum Computing, Decision-Making Process, N-Queens problem. vi Sumário Capítulo I ......................................................................................................................... 1 1.1 – Tema .................................................................................................................... 1 1.2 – Objetivos.............................................................................................................. 1 1.3 – Justificativa .......................................................................................................... 1 1.4 – Metodologia ......................................................................................................... 2 1.5 – Descrição ............................................................................................................. 3 Capítulo II ....................................................................................................................... 4 2.1 – Complexidade Computacional ............................................................................ 4 2.2 – Indecidibilidade ................................................................................................... 6 2.3 – Mecânica Quântica .............................................................................................. 7 2.4 – Evolução da Máquina de Turing ....................................................................... 11 Capítulo III.................................................................................................................... 14 3.1 – Pré-requisitos Algébricos .................................................................................. 14 3.2 – Algoritmo de Deutsch ........................................................................................ 16 3.3– Algortimo de Shor .............................................................................................. 19 3.4 – Algortimo de Grover ......................................................................................... 21 Capítulo IV .................................................................................................................... 25 4.1 – QCL ................................................................................................................... 25 4.2 – Prova de Conceito .............................................................................................. 27 Capítulo V ..................................................................................................................... 44 5.1 – Definição do Problema ...................................................................................... 44 5.2 – Soluções Clássicas ............................................................................................. 45 5.3 – Solução Híbrida ................................................................................................. 47 Capítulo VI .................................................................................................................... 56 6.1 – Conclusão .......................................................................................................... 56 6.2 – Trabalhos Futuros .............................................................................................. 58 Bibliografia .................................................................................................................... 59 Apêndice A - QCL ........................................................................................................ 62 Apêndice B – Algoritmo de Deutsch ........................................................................... 66 Apêndice C – Busca Quântica N-Queens ................................................................... 67 Apêndice D – Backtraking N-Queens ......................................................................... 70 vii Capítulo I Introdução 1.1 – Tema O tema do trabalho consiste no uso da computabilidade quântica para solucionar problemas de difícil decisão. Deste modo, o problema específico a ser resolvido é entender como empregar a computação quântica na solução de problemas tradicionais da computação e aplicar este entendimento em um problema emblemático de análise e otimização. 1.2 – Objetivos Avaliar o emprego da computação como alternativa mais ampla para resolver problemas da natureza tidos como de difícil decisão. Sob essa ótica, serão avaliados os seguintes objetivos específicos: Compreender os conceitos de complexidade computacional, indecidibilidade e mecânica quântica. Estudar o entendimento sobre Máquinas de Turing clássicas e quânticas. Realizar uma explicação mais detalhadas e didáticas sobre os algoritmos de Deutsch, Grover e Shor. Efetuar experimentos com a Quantum Computing Language. Propor uma solução do problema das N-Rainhas utilizando um algoritmo quântico. 1.3 – Justificativa A computação quântica fornece, em alguns casos, meios menos complexos para solução de problemas. Desta forma torna-se interessante a criação de um algoritmo híbrido (quântico-clássico) para ajudar na tomada de decisão de um problema específico da recreação matemática chamado N-Rainhas. 1 Se for possível encontrar soluções menos custosas em computadores clássicos para o referido problema, ter-se-á um ganho no tempo de decisão nas diferentes questões. Esquemas de armazenamento de memória paralela Testes de VLSI (Very-large-scale Integration) Controle de tráfego Prevenção de deadlocks em Sistemas Operacionais Processamento de imagem Portanto os ramos de desenvolvimento de Sistemas Operacionais, a indústria de vídeos e as empresas que fabricam processadores serão beneficiados caso o objetivo do trabalho seja alcançado. 1.4 – Metodologia Com base nos conhecimentos assimilados nas áreas de neurociência, mecânica quântica e nos estudos mais avançados de Métodos Formais de Lógica Matemática, sugere-se a criação de um algoritmo híbrido com Inputs/Outputs clássicos e processamento quântico para solucionar um problema NP-Completo. A abordagem utilizada será a computação quântica, realizada através de pesquisa bibliográfica em literatura especializada, empregando ferramentas disponíveis neste contexto. Houveram tentativas sem sucesso de obter auxílio no Laboratório Nacional de Computação Científica para a abordagem computacional do problema, mas foi de grande proveito as apostilas utilizadas pelo professor Renato Portugal da Sociedade Brasileira de Matemática Aplicada e Computacional (SBMAC). No Centro Brasileiro de Pesquisas Físicas foi possível obter apoio no entendimento dos algebrismos relacionados com a mecânica quântica. Neste sentido, a estratégia foi compreender os fundamentos teóricos sobre o tema, exercitar e instruir a capacidade de implementar algoritmos quânticos em QCL, e enfim, implementar uma solução quântica para o problema das N-Rainhas. É relevante que se diga que o trabalho não atingiria o sucesso desejado se não existisse o simulador criado por Bernard Ömer. Pois sem a QCL (Quantum Computation Language) a compreensão da funcionalidade de um algoritmo quântico não seria possível. 2 1.5 – Descrição No Capitulo II é introduzindo o conceito de indecidibilidade no mundo da ciência da computação e a mais importante criação humana: o computador (Máquina de Turing). No entanto, é elucidado de forma bem clara, suas vantagens e seus limites por conta do Teorema de Gödel bem como a evolução da Máquina de Turing como sendo o resultado direto da física-matemática e a teoria quântica necessária para a compreensão da computação quântica. No Capitulo III são apresentados os principais algoritmos quânticos existentes mostrando os pilares da computação quântica. Nele o leitor tem a oportunidade de se familiarizar com conceitos algébricos que são indispensáveis para o entendimento da computação quântica. O Capitulo IV consiste numa comparação entre algoritmos clássicos e um algoritmo quântico padrão. Na prova de conceito são apresentadas quatro soluções para um mesmo problema. Onde, depois de repetidas execuções das mesmas, chega-se em algumas conclusões importantes segundo a eficiência e complexidade dos mesmos mostrando as vantagens do quântico em relação ao mais eficiente algoritmo clássico. No Capitulo V é apresentado uma nova solução quântica para o problema complexo das N-Queens. Para finalizar são apresentadas as conclusões e os trabalhos futuros relacionados com essa dissertação. 3 Capítulo II Complexidade 2.1 – Complexidade Computacional Sob o ponto de vista computacional, complexidade é o estudo dos recursos de espaço e tempo necessários para resolver problemas computáveis. Alguns problemas são bem comportados independentemente do algoritmo e do caminho percorrido na busca da solução. Portanto, alguns problemas permitem que se chegue a limites de complexidades bem definidos, enquanto outros se encontram em classes com contornos não tão claros assim (Toscani, 2001). São definidas várias classes quanto à complexidade do problema: P, NP, NPCompleto, entre outras. Um problema é considerado fácil, tratável ou solúvel se existir um algoritmo para resolvê-lo usando recursos polinomiais (P). Um problema é considerado difícil, intratável e não razoável se o melhor algoritmo existente requer recursos exponenciais (NP) (Nielsen, 2005). O subconjunto de problemas NP, aos quais todos os problemas de NP são redutíveis em tempo polinomial, dá-se o nome de NPCompleto (NPCo). Em síntese, P é a classe de problemas que podem ser resolvidos rapidamente por um computador clássico. NP é a classe de problemas cujas soluções podem ser verificadas rapidamente em um computador clássico. O problema do caixeiro viajante (cálculo do menor percurso entre um conjunto qualquer de cidades) pode ser encarado como um elemento dos NPCo. Se o mesmo for submetido a um algoritmo clássico, dependendo do número a ser fatorado, torna-se intratável. Desta forma, não se chegará a nenhuma solução nem mesmo utilizando o melhor computador clássico existente. Alternativamente, o algoritmo Shor (que será discutido em detalhes a posteriori) tem o potencial para acelerar de forma quadrática a solução do problema, diminuindo sua complexidade e por vezes, aumentando sua eficiência. Duas condições de contorno devem ser levadas em consideração para que isso ocorra. Primeiro, o algoritmo deve ser 4 executado num computador quântico. Segundo, a quantidade de qubits deve estar na mesma ordem de grandeza que necessita a solução. Para um computador clássico, por exemplo, com o número de cidades maior do que 25, o cálculo do menor percurso entre as cidades torna-se praticamente impossível. Já para um computador quântico, utilizando o algoritmo de Shor, basta a criação de uma matriz na ordem de 25 (5 qubits) para representar o emaranhado que o problema exige. Esse dado é relevante, pois para um computador quântico, 5 qubits não representam uma alocação de recursos proibitiva. Entretanto, observe que não há aqui uma defesa de que o algoritmo de Shor tornará o problema do caixeiro viajante tratável. Apenas é uma evidência do benefício que algoritmos quânticos podem trazer a essa classe de problema. Ainda que este algoritmo tenha potencial para reduzir de forma quadrática a complexidade do problema, o resultado continua sendo exponencial. Mesmo reduzindo a complexidade, a ordem de grandeza permanece similar. Portanto, há sim um incremento da capacidade de computar soluções para uma quantidade maior de cidades do que a atual. A construção de um algoritmo quântico resultará, na maioria das vezes, numa solução mais rápida com menos complexidade que seu equivalente clássico. Entretanto, a construção desse tipo de algoritmo não é trivial. E como já foi mencionado antes, é interessante que um programa quântico seja executado num computador 100% quântico. Atualmente no mundo existem várias iniciativas e poucos computadores realmente quânticos no mundo. O Nobel de Física de 2012 (Sciam, 2012) gerou uma grande expectativa nesse sentido, pois já é possível realizar medições em jaulas de fótons e em armadilhas de íons aprisionados sem que os mesmos sejam observados. O que permite que suas propriedades quânticas não sejam perdidas, um ponto nefrálgico na construção de computadores quânticos. Essas experiências são animadoras, pois devem servir como base para a construção de novos computadores quânticos (Sciam, 2012). 5 2.2 – Indecidibilidade A indecidibilidade pode ser conceituada de modo indireto através daquilo que não é decidível, que por sua vez é o resultado de uma computação que retorna apenas “sim” ou “não”. Logo, todo problema pode ser encarado como decidível ou indecidível. Contudo, além da sua decidibilidade, um problema pode ser classificado quanto a sua tratabilidade. O problema da primacidade, do logaritmo discreto, do caixeiro viajante e o da busca desordenada num banco de dados são problemas decidíveis, porém também são considerados problemas decidíveis de difícil solução clássica (Nielsen, 2005) ou intratáveis. A matemática clássica assume que quaisquer funções, quando submetidas a uma entrada, produzem algum resultado, seja ele qual for. Entretanto, a computabilidade não compartilha esse grau de segurança proposto pela matemática clássica, pois um algoritmo pode receber uma determinada entrada e nunca encerrar o processamento da mesma (Mello, 2014). É nesse cenário que surge o problema indecidível mais famoso que se tem conhecimento, uma situação conhecida como o “Problema da Parada”. O Problema da Parada postula que existem problemas que não são resolvidos por uma Máquina de Turing, e ainda que, nem uma segunda Máquina da Turing (Prova de Turing) é capaz de avaliar se a primeira vai parar ou não, caso esta tente resolver um problema indecidível. A possibilidade de sistematizar a mente humana através da computabilidade passou a ser uma incógnita devido a essa limitação mesmo com a demonstração do Problema através do Teorema da Incompletude de Gödel (Goldstein, 2005). Porém sabese que muitas verdades dentro de sistemas complexos que a ciência tanto procurou demonstrar, nada mais são do que inferências impostas pelo sistema que o encapsula. Em parte, essa consequência do Teorema da Incompletude trouxe consigo uma frustração aos matemáticos e um melhor entendimento sobre essa limitação. Além disso, como certas verdades possuem demonstração, a automatização de alguns processos de raciocínio verdadeiros continua sendo perfeitamente cabível (Mello, 2014). Por isso, várias vertentes da ciência vêm utilizando cada vez mais o computador para ajudar o ser humano em tarefas que a mente levaria muito tempo para calcular; sendo que, em alguns casos, a solução nos é inviável. Por conta do exposto acima, as pesquisas sobre computabilidade devem ter em mente que sempre existirão mais funções a computar do que possíveis programas para computá-las (Cafezeiro e Heausler, 2007). Logo, contrapondo à Inteligência Artificial, 6 trata-se de uma Estupidez Natural (McDermott, 1976) achar que se pode, somente com a computabilidade clássica, computar habilidades humanas tais como: entendimento, desejo, compreensão e intuição (McDermott, 1976). Sabiamente ridicularizou o uso indiscriminado da computação como solução para todos os problemas, construindo assim uma maquiagem bem feita em algumas soluções de problemas, de modo que estas soluções se parecessem inteligentes. 2.3 – Mecânica Quântica A Mecânica Quântica foi desenvolvida no primeiro quarto do século XX para explicar os fenômenos do mundo microscópico: moléculas, átomos e partículas. Contrária à intuição clássica dos físicos, ela revolucionou a maneira pela qual a natureza é vista e interpretada. E ao longo das décadas, adquiriu o status de suprema teoria da matéria e suas interações. Paralelamente, surgiu outra revolução ainda na década de 1930, sendo no campo da matemática pura. Seu início se deu através de Kurt Gödel, sendo criada por Alan Turing e ratificada por Alonzo Church. A Teoria da Computação introduziu o conceito de algoritmo e fundou as bases teóricas da revolução tecnológica promovida pelos computadores, conhecida e utilizada atualmente no mundo inteiro. A visão quântica do mundo teve seu início na distinção entre partículas e os fenômenos de ondas. Os dois conceitos são diferentes, pois ninguém trata uma projetil voando como se fosse um pacote de onda ou a propagação do som como se fosse uma partícula. Porém quando partículas e comprimentos de onda ficam bem pequenos (na ordem da constante de Plank) as coisas não são bem assim (Ömer, 1998). No século XVII, Newton usou as duas teorias para definir os diferentes aspectos da luz, explicando a periodicidade e a interferência das ondas. Mais tarde, a teoria ondaluz foi amplamente aceita pelos cientistas da época. Em 1888, Hertz demonstrou que uma placa carregada negativamente pode descarregar energia quando exposta à luz ultravioleta. Logo depois, Lenard descobriu que a energia cinética dos elétrons é independente da intensidade da luz, mas está correlacionado com a sua frequência. 7 Em 1900, Max Plank explicou o espectro de energia do corpo negro pressupondo que os estados de energia possíveis estão restritos à equação 𝐸 = 𝑛ℎ𝑓, onde 𝑛 é um número inteiro, 𝑓 a frequência e ℎ = 6,626075 . 10−34 𝐽𝑠. Em 1905, Einstein reformulou a equação acima dando origem a famosa relação entre Energia, a constante de Plank e a frequência da luz. Interpretando 𝐸 como a energia da partícula luz, mais tarde chamada de fóton. 𝐸 = ℎ𝑓 = 𝜔 ℎ 2𝜋 A descoberta acima sugeriu que um elétron limita-se a certos tipos de energia, entrando em contradição com a mecânica clássica. Com isso, o modelo de BornSommerfeld introduziu a condição quântica, pois o momento dos elétrons em torno do núcleo atômico passou a ser múltiplos de ℎ. Tal restrição pode ser justificada pela atribuição de propriedades de onda para o elétron, mas esse tipo de teoria híbrida mostrouse insatisfatória. Esse problema veio a ser resolvido somente em 1923, quando Heisenberg utilizando um formalismo baseado em matrizes criou o que se chama de Princípio da Incerteza de Heisenberg. Em 1925, Schrödinger publicou uma solução alternativa utilizando funções de ondas complexas segundo a evolução temporal da partícula-onda, determinando assim a dinâmica do sistema de partículas. 𝐻𝜓 = 𝑖ħ 𝑑 (𝜓) 𝑑𝑡 Na equação de Schrödinger, 𝐻 é o operador Halmitoniano que representa a energia total do sistema e ħ = ℎ/2𝜋. Dois anos mais tarde, Dirac uniu os dois formalismos dando origem à álgebra computacional utilizada na computação quântica. A combinação de computação e mecânica quântica começou a despontar no início dos anos da década 1960, com a descoberta de lei de Moore, levando à inevitável conclusão de que, por volta do ano 2020 cada bit em um computador será codificado em apenas um átomo. (Nielsen, 2005) Para os físicos no início da década de 1980, a observação da lei de Moore tornou clara a noção de que a física traduz-se em computação. Essa nova noção abriu um espaço intangível para a computação, para a informação e também para a própria mecânica quântica. Surgiram assim a computação quântica e a informação quântica. Num mesmo patamar da história científica surgiram um novo paradigma de computação, um imenso desafio tecnológico e uma fascinante área de pesquisa da física. Desde então o que se vê 8 é a proliferação de resultados teóricos e experimentais, tanto na física quanto na ciência da computação e na teoria da informação. O emaranhado mudou-se do espaço de Hilbert para os laboratórios de física, transformando-se em um novo e estranho recurso da natureza para o processamento da computação, da informação e da comunicação. A evolução da computabilidade está caminhando para o mundo aleatório e em consequência disso consegue-se diminuir a complexidade de determinados problemas. Mas o movimento natural da ciência de se aprofundar no conceito do vetor de estado em qualquer base com números complexos não impedirá que a computação quântica continue restrita aos limites de Gödel. A teoria da Mecânica Quântica utiliza um instrumental matemático baseado em combinações lineares de vetores de estados com coeficientes complexos tendo sua probabilidade calculada a partir dos quadrados dos módulos desses números complexos. O modelo matemático onde reside a Mecânica Quântica em termos formais é o que se chama de Espaço de Hilbert (Griffiths, 2004). Neste sentido, um estado quântico |𝜓⟩ qualquer pode ser escrito da seguinte forma: |𝜓⟩ = 𝛼|0⟩ + 𝛽|1⟩ Esta fórmula descreve o estado de um sistema fechado composto por dois vetores de estado |0⟩ 𝑒 |1⟩ ditos em superposição, isto é, que existem simultaneamente até que se efetue alguma medição, quando então |𝜓⟩ é colapsado sobre algum dos vetores de estado. Além disto, 𝛼 𝑒 𝛽 ∈ ℂ são as amplitudes de cada vetor sendo |𝛼|2 + |𝛽|2 = 1. Como: 𝛼 = 𝑎 + 𝑏𝑖, 𝑎, 𝑏 ∈ ℝ 𝛽 = 𝑐 + 𝑑𝑖, 𝑐, 𝑑 ∈ ℝ Tem-se que: A probabilidade de |𝜓⟩ = |0⟩ é igual a |𝛼|2 = 𝑎2 + 𝑏 2 . A probabilidade de |𝜓⟩ = |1⟩ é igual a |𝛽|2 = 𝑐 2 + 𝑑 2 . A Mecânica Quântica teve seu início comprovado através do experimento da dupla fenda de Young (Griffiths, 2004). Neste experimento, elétrons são arremessados contra um anteparo de duas fendas gerando um modelo de interferência similar a uma 9 onda. Contudo, quando são observados, eles comportam-se como matéria. O simples fato de se observar faz com o que a menor partícula fundamental perca a sua função de onda. A Mecânica Quântica traz consigo quatro conceitos básicos que são utilizados nos modelos computacionais existentes, a saber. Paralelismo. Superposição. Interferência. Emaranhado. O entendimento do Paralelismo quântico está diretamente relacionado com a evolução temporal de um estado quântico, sendo seu comportamento regido pela equação de Schrödinger. Sem que se aprofunde no conceito físico, em computação quântica as operações são feitas simultaneamente segundo um operador unitário reversível que é traduzido em forma de função para determinados problemas. A Superposição acontece no mundo aleatório. Os vetores de estado |0⟩ e |1⟩ representam superposições dos vetores 0 e 1. O vetor |0⟩ pode se tornar 0 e 1, enquanto que o vetor |1⟩ pode se tornar 1 e 0 após uma medida. Em computação quântica os operadores unitários mexem nas probabilidades de se ocorrer 0 e 1 após a medida. Por exemplo, o operador unitário de Hadamard (H) é responsável por colocar os vetores de estado |0⟩ e |1⟩ em equiprobabilidade (50% cada uma) de ocorrem medições 0 e 1. O fenômeno da Interferência tem uma explicação simples, mas um entendimento difícil, pois como a mente humana está presa à realidade, é complicado para qualquer ser humano compreender que a simples observação de um sistema faz com que o mesmo passe a existir por conta dessa medida. Em termos computacionais, podemos determinar o valor de uma função para dois valores diferentes de um vetor de estado avaliando a função apenas uma vez. Isso é mais rápido de que qualquer aparato clássico que se possa imaginar, no qual seriam necessárias duas avaliações. (Sciam, 2012). Por fim, um estranho fenômeno da natureza quântica das partículas que até hoje não foi decifrado por completo é o Emaranhado, ou entrelaçamento quântico. Esse estado do sistema é caracterizado por pertencer somente ao mundo aleatório sem que sejam geradas representações na realidade. Dois ou mais objetos podem estar ligados de uma forma tão forte, ao ponto que um não pode ser descrito completamente sem que sua outra parte não seja mencionada, ainda que estes objetos estejam separados fisicamente. 10 Mas por razões desconhecidas, tais estados não impedem o importantíssimo papel do emaranhado na computação quântica e na informação quântica (Nielsen, 2005). 2.4 – Evolução da Máquina de Turing A Tese de Alonso Church e Alan Turing nos diz que. ”Se um problema é resolvido através de uma função computável num dado domínio, então existe uma Máquina de Turing que pode calcular o valor da função para todos os argumentos desse domínio (Palazzo, 2007).” Consequentemente, existe um dispositivo abstrato capaz de determinar a solução de um problema decidível. Essa descoberta foi concebida pelo matemático inglês chamado Alan Mathieson Turing em 1936 e desde então a Máquina de Turing é definida como o modelo da formalização do conceito de procedimento (Palazzo, 2007), isto é, uma sequencia finita de instruções que pode ser realizada num tempo finito. Como foi dito, Alan Turing contribuiu com o aparato abstrato da realidade, mas foi Deutsch (1985) que introduziu a mecânica quântica nesse contexto. A Computação Quântica surgiu da simbiose entre a Máquina de Turing e a Mecânica Quântica. Essa idéia foi proclamada por Deutsch em 1985 (Deutsch, 1985) que na ocasião criou o primeiro algoritmo quântico. Na Máquina de Turing Quântica, a fita e o cabeçote de leitura/gravação existem em um estado quântico. Neste sentido, os valores da fita podem ser {0, 1} ou uma superposição de valores 0 e 1. Assim, os símbolos {|0⟩, |1⟩} representam todos os gradientes entre eles ao mesmo tempo. Desta forma, se uma Máquina de Turing Clássica produz sempre um único resultado, a Máquina de Turing Quântica produz vários resultados simultaneamente, ou seja, efetua vários cálculos (paralelismo quântico). 11 A sequência lógica de raciocínio a seguir exprime, em poucas palavras, o entendimento da Máquina de Turing Quântica: 1. A Máquina de Turing Clássica representa uma máquina abstrata usada para modelar o efeito de computar. 2. A Máquina de Turing Probabilística representa uma Máquina de Turing não determinística. 3. A Máquina de Turing Quântica representa uma Máquina de Turing Probabilística, onde não é possível realizar o monitoramento da fita, pois o simples ato de medir acabaria por perturbar o sistema. O poder computacional de máquinas de Turing quântica é objeto de estudo de vários pesquisadores. Bernstein e Vazirani (1993; 1997) fazem algumas comparações com os modelos de classes de complexidade clássicos com aqueles criados com base nas evoluções obtidas na computação quântica. As classes BQP, EQP e ZQP acabaram por fundamentar as bases da teoria da complexidade quântica. Como indicado por Tetsushi (2004): Uma classe BQP (Bounded Error Quantum Polynomial Time) é aquela em que se existe uma máquina de Turing quântica tal que para qualquer entrada x uma observação de uma célula da fita após um processamento de tempo polinomial, ela retorna 1 com probabilidade maior que 2/3 se x pertencer a L ou 0 com probabilidade maior que 2/3 caso contrário. Uma classe EQP (Exact Quantum Polynomial Time) é aquela em que se existe uma máquina de Turing quântica e um polinomial p, tal que, para qualquer entrada x uma observação de uma célula da fita após um processamento de p(|x|) passos retorna 1 com probabilidade 1 se x pertencer a L ou 0 com probabilidade1, caso contrário. Uma classe ZQP (Zero Error Quantum Polynomial Time) é aquela em que se existe uma máquina de Turing quântica tal que para qualquer entrada x uma observação de uma célula da fita (célula indicadora de parada da máquina) após um processamento de tempo polinomial de seu tamanho ela retorna 1 com probabilidade maior que 1/2 e então se uma observação de outra célula (célula de decisão) retorna 1 com probabilidade 1 se x pertencer a L, 0 com probabilidade 1, caso contrário. 12 Atualmente existem três importantes algoritmos quânticos, e no total não passam de dez no mundo inteiro, são eles: Deutsch, Shor e Grover. Estes algoritmos serão abordados em detalhes no capítulo a seguir. Sob esta ótica, o avanço dos algoritmos quânticos está na compreensão dos fenômenos físicos propostos pela mecânica quântica e no domínio da álgebra tensorial utilizada pela mesma. Se os físicos, matemáticos e cientistas da computação conseguirem encontrar combinações de operadores unitários que traduzam o comportamento do mundo pequeno que gera a realidade, consequentemente este entendimento permitirá o surgimento de mais algoritmos quânticos eficientes. 13 Capítulo III Algoritmos Quânticos 3.1 – Pré-requisitos Algébricos Fazer computação quântica nada mais é do que manipular os operadores unitários diante dos vetores de estado seguindo o conceito da equação de Schrödinger. A principal característica desses operadores é que 𝐴𝑡 𝐴 = 𝐼, onde 𝐴𝑡 é matriz transposta conjugada de 𝐴. A seguir, são elencadas algumas propriedades importantes que serão utilizadas nesta dissertação. Algumas explicações pormenorizadas serão realizadas quando houver pertinência direta com o entendimento da matemática envolvida neste trabalho, outras explicações deverão ser consultadas em Nielsen (2005). Inicialmente são necessárias algumas observações a respeito dos vetores de estado, a saber: 1 |0⟩ = [ ] 0 0 |1⟩ = [ ] 1 𝛼 |𝜓⟩ = 𝛼|0⟩ + 𝛽|1⟩ = [ ] , 𝑜𝑛𝑑𝑒 𝛼 2 + 𝛽 2 = 1 𝛽 ⟨𝜓| = [𝛼 𝛽] ⟨𝜓|𝜓⟩ = ‖|𝜓⟩‖ = √𝛼 2 + 𝛽 2 1 − 3𝑖 1 − 𝑖 Em seguida, seja 𝐴 = [ ]. Neste caso, tem-se que a matriz −2𝑖 1 + 4𝑖 1 − 3𝑖 −2𝑖 transposta 𝐴𝑇 = [ ]. Calculando-se o conjugado desta matriz, tem-se 1 − 𝑖 1 + 4𝑖 1 + 3𝑖 2𝑖 que(𝐴𝑇 )∗ = [ ] = 𝐴𝑡 . Dessa forma 𝐴 é considerado um operador 1 + 𝑖 1 − 4𝑖 hermitiano e 𝐴𝑡 é o conjugado hermitiano da matriz 𝐴. Se 𝐴𝑡 𝐴 = 𝐴𝐴𝑡 , diz-se que 𝐴 é normal. Portanto todo operador hermitiano é normal. Se 𝐴𝐴𝑡 = 𝐼, diz-se que 𝐴 é unitário e que o operador hermitiano é unitário. 14 Além disso é importante observar que o complemento ortogonal de 𝐴 é definido como 𝑄 = 𝐼 − 𝐴. 0 1 Nesse sentido, considere também uma das Matrizes de Pauli 𝑋 = [ ]. Temos 1 0 0 1 𝛼 que 𝑋|𝜓⟩ = [ ] [ ] = [𝛽𝛼]. Sob esta ótica, nota-se facilmente que esse operador 𝑋 1 0 𝛽 inverte os coeficientes do vetor de estado. 𝑎𝑐 Por fim, seja 𝐴 = [𝑎𝑏] 𝑒 𝐵 = [𝑑𝑐 ]. Definimos 𝐴 ⊗ 𝐵 = [𝑎𝑑 𝑏𝑐 ] como sendo o produto 𝑏𝑑 tensorial entre A e B, onde segundo (Nielsen, 2005) esse produto é a forma de se expandir espaços vetoriais. Há que se fazer aqui uma ressalva notacional sobre a questão do produto tensorial. A representação matemática deste tipo de operação é de fato dada por 𝐴 ⊗ 𝐵. Contudo, a Física adota outra notação para denotar a mesma operação, isto é |𝐴⟩|𝐵⟩. Nesta dissertação será adotada a notação oriunda da Física uma vez que na literatura relacionada com Computação Quântica há o predomínio desta última em detrimento da notação matemática. Por exemplo, o estado quântico |01⟩ pode ser expandido da seguinte forma. 0 |01⟩ = |0⟩|1⟩ = [10] ⊗ [01]=[10] 0 O operador hermitiano 𝐻 = 1 √2 [ 1 1 ] coloca os estados |0⟩ e |1⟩ em eqüiprobabilidade 1 −1 de ser 0 ou 1. Deste modo, a aplicação deste operador sobre |0⟩ e |1⟩ é calculada da seguinte forma: 1 1 1 1 1 1 1 1 0 (|0⟩ + |1⟩) [ ][ ] = [ ]= ([ ] + [ ]) = = |+⟩ 1 √2 1 −1 0 √2 1 √2 0 √2 1 1 1 0 1 1 1 1 0 (|0⟩ − |1⟩) 𝐻|1⟩ = [ ][ ] = [ ]= ([ ] + [ ]) = = |−⟩ −1 √2 1 −1 1 √2 −1 √2 0 √2 𝐻|0⟩ = onde 𝐻 é chamado de operador de Hadamard. 15 3.2 – Algoritmo de Deutsch É possível explicar o algoritmo de Deutsch analisando as faces de uma moeda. Suponha que se deseja saber se uma moeda possui de um lado uma cara e do outro uma coroa com apenas uma observação. Classicamente, este problema seria trivial, haja vista que, se uma face da moeda é cara, é esperado que a outra face seja coroa. Contudo, há uma pressuposição importante para que isto aconteça. A moeda deve possuir uma face com coroa e outra com cara. No entanto, é possível existirem moedas cunhadas com duas faces iguais. Dessa forma, diz-se que moedas com duas faces iguais são constantes e moedas com faces diferentes são chamadas de balanceadas. O proposta de Deutsch foi justamente saber se uma dada função de leitura f:{0, 1} → {0, 1} é balanceada ou constante. De forma clássica, é óbvio que são necessárias duas leituras para saber o comportamento da função, pois jamais se tem o conhecimento se as faces são diferentes ou não. Porém Deutsch desenvolveu um algoritmo quântico cuja solução se dá com apenas uma medida, conseguindo asim, observar ambos os lados da moeda de uma só vez. Na tabela 3.1 tem-se todas as possibilidades do resultado dessas funções. As funções de leitura 𝑓0 , 𝑓1 , 𝑓2 e 𝑓3 correspondem às respectivas leituras dos casos em que a moeda é composta por cara-cara, coroa-coroa (funções constantes), coroa-cara ou cara-coroa (funções balanceadas). Tabela 3.1 – Resultado das funções segundo o valor de x 𝒇𝟎 (𝒙) 𝒇𝟏 (𝒙) 𝒇𝟐 (𝒙) 𝒇𝟑 (𝒙) 1 – cara 0 – coroa 0 - coroa 1 – cara 1 – cara 0 – coroa 1 – cara 0 – coroa É evidente que a quantidade mínima de qubits para armazenar todas as combinações de resultados para representar o estado inicial da moeda é dois, porque existem quatro combinações possíveis e 22 = 4, onde o primeiro qubit representa estado inicial da moeda e segundo o resultado da operação. Como estados quânticos só podem ser modificados segundo operadores unitários, logo existe um operador 𝑈𝑓 , tal que, 𝑈𝑓 que transforma 𝑥 ∈ {0, 1} em 𝑦 ∈ {0, 1} e 𝑓(𝑥) ∈ 16 {0, 1}. Desta forma, |𝑥, 𝑦⟩ → |𝑥, 𝑦⨁𝑓(𝑥)⟩, onde 𝑦⨁𝑓(𝑥) representa o (𝑦 + 𝑓(𝑥))𝑚𝑜𝑑2=resto da divisão de (𝑦 + 𝑓(𝑥)) por 2, representado pelo o esquema a seguir. A tabela 3.2 mostra com mais clareza o resultado da operação. Tabela 3.2 – Operação módulo 2 segundo Deutsch |𝒙⟩|𝒚⟩ 𝒇(𝒙) 𝒚 𝒚⨁𝒇(𝒙) |𝟎𝟎⟩ 0 0 0 |𝟎𝟏⟩ 1 0 1 |𝟏𝟎⟩ 0 1 1 |𝟏𝟏⟩ 1 1 0 Como 𝑓(𝑥) = { 𝑦, 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡𝑒 0, 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡𝑒 , tem-se 𝑦⨁𝑓(𝑥) = { ou seja, 𝑦̅, 𝑏𝑎𝑙𝑎𝑛𝑐𝑒𝑎𝑑𝑎 1, 𝑏𝑎𝑙𝑎𝑛𝑐𝑒𝑎𝑑𝑎 somando os elementos da coluna 𝑦 mais os da coluna 𝑓(𝑥) e dividindo por 2, o resto representa todas as possibilidades da outra face da moeda após o lançamento. Dado que |0⟩|1⟩ = |01⟩ e partindo-se do estado inicial |𝜓⟩ = |0⟩|1⟩ = |01⟩, aplicando o operador de Hadamard no vetor de estado inicial |𝜓⟩, tem-se: |𝜓1 ⟩ = 𝐻|0⟩𝐻|1⟩ = 1 1 ( √2 1 1 1 1 1 )( ) ( −1 0 √2 1 (|0⟩ + |1⟩) (|0⟩ − |1⟩) 1 0 )( ) = −1 1 √2 √2 1 [ |0⟩|0⟩ − |0⟩|1⟩ + |1⟩|0⟩ − |1⟩|1⟩ ] 2 1 1 = [|0⟩(|0⟩ − |1⟩) + |1⟩(|0⟩ − |1⟩)] = |𝑥⟩(|0⟩ − |1⟩) 2 √2 = 17 A operação utilizando o operador de Hadamard coloca o emaranhado em equiprobabilidade e definindo. |𝜓2 ⟩ = 𝑈𝑓 |𝜓1 ⟩ 0, 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡𝑒 Como 𝑦⨁𝑓(𝑥) = { 1, 𝑏𝑎𝑙𝑎𝑛𝑐𝑒𝑎𝑑𝑎 Logo |𝜓2 ⟩ = 𝑈𝑓 1 √2 |𝑥⟩(|0 ⊕ 𝑓(𝑥)⟩ − |1 ⊕ 𝑓(𝑥)⟩) Como os valores possíveis de 𝑓(𝑥) = {0,1}, dessa forma pode-se substituir |𝜓2 ⟩ pela forma homomórfica a seguir: |𝜓2 ⟩ = (−1) 𝑓(𝑥) |𝜓2 ⟩ = 1 √2 1 √2 |𝑥⟩[|0⟩ − |1⟩] [(−1)𝑓(0) |0⟩(|0⟩ − |1⟩) + (−1) 𝑓(1) |1⟩(|0⟩ − |1⟩)] 1 ± [(|0⟩ + |1⟩)(|0⟩ − |1⟩)] 𝑠𝑒 𝑓(0) = 𝑓(1) |𝜓2 ⟩ = { 2 1 ± [(|0⟩ − |1⟩)(|0⟩ − |1⟩)] 𝑠𝑒 𝑓(0) ≠ 𝑓(1) 2 Utilizando o formalismo acima, Deutsch concebeu o primeiro algoritmo quântico cuja a proposta é colocar 2 qubits em superposição num emaranhado e com apenas uma medida do primeiro qubit é possível obter o valor da outra face após o lançamento. No apêndice B é mostrado o algoritmo em QCL. 18 3.3– Algortimo de Shor O algoritmo de Shor (Nielsen, 2005) resolve de forma exponencial o cálculo da ordem de um determinado número, fazendo com que a complexidade do problema da fatoração diminua de forma quadrática. Uma abordagem inicial para explicar o algoritmo Shor passa por uma tentativa de fatorar um número 𝑁. O problema da fatoração de números é bastante importante para a criptografia, pois trata-se de um recurso que fatora um número em números primos. Observe que para isto basta checar o resto da divisão de um número natural 𝑝 por algum número menor ou igual que √𝑁. Se o resto é 0, conclui-se que 𝑝 é um fator 𝑁. Em uma primeira análise, esta propriedade parece ser despretensiosa, mas ela permite uma manipulação importante. Por exemplo, para 𝑁 = 15, √𝑁 = 3,87, toma-se o número primo mais próximo e menor ou igual a √𝑁. Neste caso 𝑝 = 3 (outro número primo é o 2, porém não é o mais próximo). Observe que 𝑁 𝑚𝑜𝑑 3 = 0, logo 3 divide 15. Consequentemente, o MDC(3, 15) = 3, portanto 3 é um divisor primo de 15. Após essa divisão o que resta é o número 5. Portanto resta o cálculo para 𝑁 = 5, cuja raiz √𝑁 = 2,24. Desta forma, o número primo mais próximo e menor ou igual a √𝑁 é 2, porém 𝑁 𝑚𝑜𝑑 2 ≠ 0. Como o MDC(5, 15) = 5, logo 5 é divisor de 15. Considerando valores de 𝑁 pequenos, este método é interessante, mas para 𝑁 maiores, esse método se torna inviável. Uma vez que sua solução depende da solução de diversos outros problemas similares de menor ordem. Além disso, há que se levar em consideração que esse problema trata-se de uma fatoração exata em dois números primos sem expoente. A perspicácia de Shor foi perceber que na verdade isto permite uma redução do problema da fatoração para um problema de periodicidade. Além disso, transformar uma sequência numa função periódica é uma tarefa relativamente fácil. Por exemplo, suponha: 𝑥 ∈ {0,1,2,3,4,5,6,7 … } 𝑒 𝑟 = 15 Ao se tentar calcular 2𝑥 𝑚𝑜𝑑 𝑟 para cada valor de x obtém-se o seguinte vetor de soluções: [1, 2, 4, 8, 1, 2, 4, 8, … . ] O que de fato pode ser considerado uma função de período 4. 19 Para 𝑥 ∈ {0,1,2,3,4,5,6,7,8,9,10,11,12, … } 𝑒 𝑟 = 21, com a mesma função de cálculo 2𝑥 𝑚𝑜𝑑 𝑟, tem-se: [1, 2, 4, 8, 16, 11, 1, 2, 4, 8, 16, 11, … ] Nesse caso o período da função é 6. Fazendo uso da periodicidade, pode-se prosseguir numa substituição dada por 𝑟 = 2𝑝 e generalizando o raciocínio acima, utilizando um número qualquer de uma potência qualquer, tem-se. 𝑟 𝑥 𝑚𝑜𝑑 𝑁, 𝑥 ∈ {0,1,2, … } Nota-se que, quando o 𝑥 fornecido não é divisível por p ou por q, a sequência acima irá se repetir com um período que eventualmente divide (𝑝 − 1)(𝑞 − 1). Ou seja, o problema se traduz em descobrir um período e depois fatorá-lo em dois outros números e depois somar mais 1 e descobrir dois fatores primos de 𝑁. Voltando para os casos acima. Para 𝑁 = 15 o período é 4, que por sua vez divide (𝑝 − 1)(𝑞 − 1) = 2 ∗ 4 = 8. 𝑝 = 2 + 1 = 3 𝑒 𝑞 = 4 + 1 = 5. Para 𝑁 = 21 o período é 6, que por sua vez divide (𝑝 − 1)(𝑞 − 1) = 2 ∗ 6 = 12 2 ∗ 6 = (𝑝 − 1)(𝑞 − 1), 𝑙𝑜𝑔𝑜 𝑝 = 3 𝑒 𝑞 = 7. E assim sucessivamente. A quantização do raciocínio disposto pode ser posta numa superposição de 𝑛 𝑥 𝑚𝑜𝑑 𝑁 ≡ 𝑟. Onde 𝑟 é o período a ser encontrado na tentativa de descobrir o produto (𝑝 − 1)(𝑞 − 1) e consequentemente os fatores de 𝑁. Desta forma, em linhas gerais, basta encontrar um método de cálculo eficiente no emaranhado para obter o período que satisfaça a sequência. Nesse ponto surge o conceito da Transformada de Fourier Quântica (QFT). Trazendo o conceito da transformada em si, tem-se. |𝑗⟩ → 1 √2𝑛 2𝑛 −1 𝑛 ∑ 𝑒 2𝜋𝑖𝑗𝑘/2 |𝑘⟩ 𝑘=0 0 onde |𝑗⟩ = |𝑗1 𝑗2 … 𝑗𝑛 ⟩ = 2 𝑗𝑛 + 21 𝑗𝑛−1 + ⋯ + 2𝑛−1 𝑗1 será encarado como o emaranhado da sequencia 𝑛 𝑥 𝑚𝑜𝑑 𝑁 com período 𝑟. De posse desse conhecimento, Shor define seu algoritmo como se segue. 1. Verificar se 𝑁 é par, e caso afirmativo retornar 2. 2. Determinar se 𝑁 = 𝑎𝑏 para 𝑏 ≥ 2 e se a resposta for positiva retornar a. 20 3. Escolher um número aleatório x (1 ≤ x ≤ N) e se 𝑀𝐷𝐶(𝑥, 𝑁) > 1 retornar 𝑀𝐷𝐶(𝑥, 𝑁). 4. Encontrar a ordem r de x, onde 𝑥 𝑟 = 1 𝑚𝑜𝑑 𝑁 𝑟 𝑟 𝑟 5. Se r for par e 𝑥 2 ≠ 1𝑚𝑜𝑑 𝑁, calcule 𝑀𝐷𝐶(𝑥 2 −1,N) e 𝑀𝐷𝐶(𝑥 2 + 1, 𝑁) 6. Se são soluções não triviais, retorne-as. O cálculo da ordem de forma quântica é realizado colocando o emaranhado em equiprobabilidade através da aplicação do operador de Hadamard. Depois aplica-se o operador 𝑈 controlado pelo primeiro qubit no estado |1⟩ chegando no estado abaixo. |𝜓2 ⟩ = 1 √2𝑡 2𝑡 −1 ∑ |𝑘⟩|𝑥 𝑘 𝑚𝑜𝑑 𝑁⟩ 𝑘=0 Inverte-se esse estado e realiza-se uma medida com o objetivo de encontrar o maior número da sequência. De posse disso chega-se na ordem que divide os fatores. Se esses fatores obedecerem aos cálculos dos máximos divisores comuns, encontra-se os fatores primos de 𝑁. 3.4 – Algortimo de Grover O algoritmo de Grover consegue de forma otimizada efetuar buscas numa base de dados desordenada com mais eficiência do que um algoritmo clássico equivalente (Nielsen, 2005). Supondo que haja somente uma solução 𝑖0 , a busca na lista {0,1,2, … , 𝑁}, 𝑜𝑛𝑑𝑒 𝑁 = 2𝑛 𝑐𝑜𝑚 𝑛 > 0 será realizada através da função. 𝑓(𝑖) = { 1, 0, 𝑠𝑒 𝑖 = 𝑖0 𝑠𝑒 𝑖 ≠ 𝑖0 É fácil notar que a complexidade desse algoritmo será dada pela quantidade de vezes que a função for executada. Para solucionar o problema, Grover criou um registrador com a quantidade de qubits suficiente para armazenar o emaranhado de todos os 𝑁 elementos. Neste sentido, Grover utiliza 𝑁 = 2𝑛 𝑐𝑜𝑚 𝑛 > 2, onde n é a quantidade de qubits e mais um qubit para armazenar o resultado da função 𝑓. O passo seguinte foi a criação de um operador para procurar a informação quântica desejada. Na verdade esse operador levou seu nome e consiste na aplicação de um operador de negação do vetor 21 inicial seguido do complemento normal do resultado, conforme será melhor detalhado a seguir. Grover demonstra em seu trabalho (Portugal, 2012), através de uma análise de grafos, que a complexidade dessa solução quântica é pelo menos quadraticamente menor do que a aleatória clássica equivalente (Portugal, 2012). Portanto pode-se afirmar que Grover acelerou, de forma quadrática, a procura da solução |𝑖0 ⟩. Aplicando o operador de Hadamard nos dois registradores, tem-se. 1 𝐻|𝜓⟩ = √𝑁 𝑁−1 ∑ |𝑖⟩ 𝑖=0 e 𝐻|1⟩ = 1 √2 (|0⟩ − |1⟩) = |−⟩ Partindo da premissa de que existe um operador unitário 𝑈𝑓 com a seguinte característica 𝑈𝑓 (|𝑖⟩|0⟩) = { |𝑖⟩|1⟩, 𝑠𝑒 𝑖 = 𝑖0 |𝑖⟩|0⟩, 𝑠𝑒 𝑖 ≠ 𝑖0 Ou seja, ele altera o estado do registrador para encontrar o valor procurado e de forma análogo, quando isso não ocorre. 𝑈𝑓 (|𝑖⟩|1⟩) = { |𝑖⟩|0⟩, 𝑠𝑒 𝑖 ≠ 𝑖0 |𝑖⟩|1⟩, 𝑠𝑒 𝑖 = 𝑖0 Utilizando o mesmo artifício algébrico do algoritmo de Deutsch para os dois casos acima 𝑈𝑓 (|𝑖⟩|𝑗⟩) = |𝑖⟩|𝑗⨁𝑓(𝑖)⟩ e aplicando o operador de Hadamard nos dois registradores, tem-se: 𝑈𝑓 (|𝜓⟩|−⟩) = 𝑈𝑓 ( = 1 √𝑁 1 √𝑁 𝑁−1 ∑ |𝑖⟩ |−⟩) = 𝑖=0 𝑁−1 ∑ 1 √2 𝑖=0 𝑁−1 1 √𝑁 ∑ 𝑈𝑓 |𝑖⟩|−⟩ = 𝑖=0 1 √𝑁 𝑁−1 ∑ 𝑈𝑓 |𝑖⟩ 𝑖=0 (𝑈𝑓 |𝑖⟩|0⟩ − 𝑈𝑓 |𝑖⟩|1⟩) Lembrando que 𝑈𝑓 (|𝑖⟩|𝑗⟩) = |𝑖⟩|𝑗⨁𝑓(𝑖)⟩, logo 𝑈𝑓 |𝑖⟩|0⟩ = |𝑖⟩|0⨁𝑓(𝑖)⟩ = |𝑖⟩|𝑓(𝑖)⟩ e 𝑈𝑓 |𝑖⟩|1⟩ = |𝑖⟩|1⨁𝑓(𝑖)⟩ 22 1 √2 (|0⟩ − |1⟩) Logo 𝑈𝑓 (|𝜓⟩|−⟩) = 𝑁−1 1 √𝑁 = 1 ∑ √2 𝑖=0 𝑁−1 1 √𝑁 (|𝑖⟩|𝑓(𝑖)⟩ − |𝑖⟩|1⨁𝑓(𝑖)⟩) 1 ∑ √2 𝑖=0 |𝑖⟩(|𝑓(𝑖)⟩ − |1⨁𝑓(𝑖)⟩) Analisando a expressão |𝑖⟩(|𝑓(𝑖)⟩ − |1⨁𝑓(𝑖)⟩) e lembrando que 𝑓(𝑖) = { 1, 0, 𝑠𝑒 𝑖 = 𝑖0 𝑠𝑒 𝑖 ≠ 𝑖0 |𝑖⟩(|𝑓(𝑖)⟩ − |1⨁𝑓(𝑖)⟩) = { |𝑖⟩(|1⟩ − |0⟩), |𝑖⟩(|0⟩ − |1⟩), 𝑠𝑒 𝑖 = 𝑖0 𝑠𝑒 𝑖 ≠ 𝑖0 Portanto 𝑈𝑓 (|𝜓⟩|−⟩) = 1 √𝑁 𝑁−1 1 (( ∑ 𝑖=0,𝑖≠𝑖0 𝑈𝑓 (|𝜓⟩|−⟩) = √2 1 √𝑁 1 √2 |𝑖0 ⟩(|1⟩ − |0⟩)) 𝑁−1 (( ∑ |𝑖⟩|−⟩) − |𝑖0 ⟩|−⟩) 𝑈𝑓 (|𝜓⟩|−⟩) = |𝜓1 ⟩ = |𝑖⟩(|0⟩ − |1⟩)) + 1 √𝑁 𝑖=0,𝑖≠𝑖0 1 √𝑁 𝑁−1 (∑(−1) 𝑓(𝑖) |𝑖⟩|−⟩) 𝑖=0 𝑁−1 (∑(−1) 𝑓(𝑖) |𝑖⟩) |−⟩ 𝑖=0 O resultado obtido pode ser interpretado geometricamente com sendo o a reflexão do vetor |𝜓⟩ em relação à |0⟩ segundo à figura 3.1. Fig 3.1 – 3º Passo do Algoritmo de Grover 23 E resultado do segundo registrador permanece inalterado mesmo depois da aplicação do operador. Com o resultado do segundo não se altera e, de forma análoga, o resultado do primeiro operador, Grover utilizou um segundo operador que fosse uma nova reflexão, mas usou como base o próprio estado inicial. Esse operador corresponde ao complemento normal, que é dado por |𝜓1 ⟩ = 𝐼 − |𝜓⟩ (ver sessão 3.1), mostrada na Figura 3.2. Fig 3.2 – 4º Passo do Algoritmo de Grover Tal como descrito na estratégia adotada por Grover, estes procedimentos são realizados sucessivamente até encontrar a solução, ou até 𝜋√𝑁 8 iterações. Fig 3.3 – Sucessivas aplicações do Operador de Grover Todo este processo pode ser escrito segundo a estratégia: Repita 1.Reset de todos os qubits para |0⟩ 2.Superposição do emaranhado 3.Negação do vetor 4.Complemento normal 5.Medida Até encontrar a solução ou até no máximo 𝜋√𝑁 8 vezes. 24 Capítulo IV Experimentos Iniciais 4.1 – QCL O acrônimo QCL significa Quantum Computation Language e representa uma linguagem de programação quântica (QPL) experimental e estruturada criada na Tese de Doutorado de Bernard Ömer, Ph.D. (Ömer, 1998). Por ser estruturada, a programação quântica em QCL procura manter os conceitos da semântica clássica sob a forma de programas estruturados. Que por sua vez são sequências de declarações e definições que são processados de cima para baixo utilizando blocos como rotinas e sub-rotinas. Uma descrição mais detalhada sobre QCL é apresentada no Apêndice A. A tabela 3.1 apresenta a referência entre os conceitos quânticos e seus homólogos clássicos. Tabela 4.1 – Analogia entre QCL e uma linguagem clássica Conceito Clássico Análogo Quântico modelo de máquina clássica Variáveis sub-rotinas argumentos e tipos de retorno variáveis locais memória dinâmica expressões booleanas execução condicional seleção laços condicionais Não disponível Não disponível arquitetura quântica híbrida registrador quântico operadores unitários tipos de dados quânticos Registradores gestão do espaço condições quântica operadores condicionais afirmação SE quântica bifurcação quântica execução de operadores inversos medição quântica O registrador quântico é o conceito mais importante em computação quântica. Pois enquanto num computador clássico, um bit necessita de milhares de elétrons para sua representação, sendo seu estado lógico determinado pelo valor da expectativa de seu conteúdo, em um computador quântico a informação é representada diretamente como o 25 estado comum de vários qubits. Essa representação pode ser realizada através do spin de uma partícula, da polarização de um fóton ou do estado de excitação de um íon (Ömer, 1998). Por se tratar de uma estrutura híbrida, para o desenvolvedor, o programa funciona exatamente como qualquer outro programa clássico, pois ele receberá entradas clássicas e produzirá saídas clássicas também, mas seu processamento será quantizado. A QCL será utiliza nessa dissertação para a confecção de um algoritmo quântico baseado no algoritmo de Grover. Como um número complexo pode ser representado na forma ortogonal e na geométrica, recomenda-se que a funcionalidade deum programa quântico seja interpretada através da visualização gráfica dos vetores de estado em bases ortogonais. Na figura 3.1 tem-se a representação de um vetor de estado qualquer através da esfera de Bloch. Fig 4.1 – Esfera de Bloch. Como |𝜓⟩ = 𝛼|0⟩ + 𝛽|1⟩, onde 𝛼 2 + 𝛽 2 = 1 Com ajuda da Esfera de Bloch, um vetor de estado qualquer pode ser reescrito da seguinte forma: 𝜃 𝜃 |𝜓⟩ = 𝑒 𝑖𝛾 (𝑐𝑜𝑠 |0⟩ + 𝑒 𝑖𝜑 𝑠𝑒𝑛 |1⟩) 2 2 Como o fator 𝑒 𝑖𝛾 pode ser descartado por não ser observável em mecânica quântica, logo: 𝜃 𝜃 |𝜓⟩ = 𝑐𝑜𝑠 |0⟩ + 𝑒 𝑖𝜑 𝑠𝑒𝑛 |1⟩ 2 2 26 Mas as bases ortogonais em espaços vetoriais complexos de duas dimensões é o quadro matemático e conceitual mais utilizado para representar um estado quântico de um vetor de estado qualquer. Fig 4.2 – Vetor de estado na base ortogonal. Sendo, 1 |0⟩ = ( ) 0 0 |1⟩ = ( ) 1 Logo, |𝜓⟩ = 𝛼|0⟩ + 𝛽|1⟩ = 𝛼(10) + 𝛽(01) = (𝛽𝛼) 4.2 – Prova de Conceito Para que haja uma análise entre as vantagens e desvantagens quanto à utilização das duas formas de computação, será proposto um problema básico que pode ser resolvido pelos dois métodos computacionais: o clássico e o quântico. O problema abordado será a busca de um elemento numa base de dados desordenada. Serão apresentadas quatro soluções para o problema, sendo as três primeiras clássicas e a quarta quântica. As clássicas são de fácil compreensão, mas a quântica exigirá do leitor o conhecimento prévio do algoritmo quântico de Grover, descrito na seção 3.4. A primeira solução desta prova de conceito consiste numa busca sequencial numa massa de dados aleatórios com complexidade 𝑂(𝑁), 𝑐𝑜𝑚 𝑁 > 0. Trata-se apenas de um 27 experimento de controle no qual se implementa uma busca segundo as técnicas tradicionais. A segunda solução, por sua vez, consiste numa busca sequencial numa massa de dados aleatórios com uma procura aleatória também. Nesse tipo de solução deve-se respeitar os elementos procurados para que os mesmos não se repitam, evitando assim o problema da parada. Neste caso, a solução também é clássica, porém começa a utilizar um fator de aleatoriedade no processo de localização do elemento a ser encontrado. A terceira mostrará a mesma solução, porém realizada através de uma amostra previamente ordenada para futura pesquisa binária. Por fim, a solução quântica é calcada no algoritmo de Grover. Neste sentido, foi criado um cenário composto por quatro programas em Linux Centos 6.3, cada um contendo uma solução. Depois de uma bateria de execuções, as interações foram coletadas para futura análise. A primeira solução clássica, confeccionada em gcc, faz uma busca sequencial simples nos dados aleatórios com complexidade 𝑂(𝑁), 𝑐𝑜𝑚 𝑁 > 0. A seguir é apresentada a solução codificada em “C” desenvolvida para esse trabalho. classical_search.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> void find(int no, int n) { int i=0,j=0,x; int q[n]; bool found; for(i=0;i<n;i++) q[i]= -1; while (j < n) { x=rand()%n; i=0; found = false; for(i=0; (i<n); i++) if (!found && q[i]==x) {found = true;} if (!found) { q[j]=x; j++; } } printf("Range into 0 and %i\n", (n-1)); printf("\n"); printf("Searching for %d...\n", no); i=0; found=false; 28 while (i<n && !found) { if (q[i] == no) { found=true; printf("%d found\n", no); } else { printf("%da interacao q=%d\n",i, q[i]); i++; } } } void main(int argc, const char* argv[]) { if (argc != 3) { fprintf(stderr, "Use: %s <numero de procura><quantidade de numeros>\n", argv[0]); exit(EXIT_FAILURE); } int No = atoi(argv[1]); int N = atoi(argv[2]); system("clear"); printf("N = %i\n\n", N); find(No, N); exit(EXIT_SUCCESS); } 29 A segunda solução clássica faz a mesma procura nos dados, mas de forma mais otimizada, pois além de ser randômica, ela sempre procura em itens que ainda não foram procurados, a fim de evitar o problema da parada. O código implementado encontra-se descrito a seguir. random_search.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> void random_vector(int *v, int n) { int i=0,j=0,x; bool found; for(i=0;i<n;i++) v[i]= -1; while (j < n) { x=random()%n; i=0; found = false; for(i=0; (i<n); i++) if (!found && v[i]==x) {found = true;} if (!found) { v[j]=x; j++; } } } void main(int argc, const char* argv[]) { if (argc != 3) { fprintf(stderr, "Use: %s <numero de procura><quantidade de numeros>\n", argv[0]); exit(EXIT_FAILURE); } int no = atoi(argv[1]); int n = atoi(argv[2]); int q[n], r[n]; bool found; srand(time(NULL)); system("clear"); printf("N = %i\n\n", n); printf("Range into 0 and %i\n", (n-1)); printf("\n"); 30 random_vector(q, n); random_vector(r, n); printf("Searching for %d...\n", no); int i=0; found=false; while (i<n && !found) { if (q[r[i]] == no) { found=true; printf("%d found\n", no); } else { printf("%da interacao q=%d\n",i, q[r[i]]); i++; } } exit(i); } 31 A terceira solução foi implementada numa lista ordenada com pesquisa binária. De forma clássica, com complexidade 𝑂(𝑙𝑜𝑔2 𝑁), não existe nada mais rápido do que esse mecanismo de procura. Segue a implementação construída para esse trabalho. bin_search.sh #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdbool.h> void find(int no, int n) { int i=0,j=0,x; int q[n]; bool found; for(i=0;i<n;i++) q[i]= i; printf("Range into 0 and %i\n", (n-1)); // for(i=0;i<n;i++) { printf("%d\n",q[i]); } printf("\n"); printf("Searching for %d...\n", no); int e, m, d; e = 0; d = n-1; i=0; while (e <= d) { i++; m = (e + d)/2; if (q[m] == no) { printf("%d found\n", no); return; } else printf("%da interacao q=%d\n",i, q[m]); if (q[m] < no) e = m + 1; else d = m - 1; } } 32 void main(int argc, const char* argv[]) { if (argc != 3) { fprintf(stderr, "Use: %s <numero de procura><quantidade de numeros>\n", argv[0]); exit(EXIT_FAILURE); } int No = atoi(argv[1]); int N = atoi(argv[2]); system("clear"); printf("N = %i\n\n", N); find(No, N); exit(EXIT_SUCCESS); } 33 A quarta solução é um programa quântico desenvolvido em QCL. Como foi dito anteriormente o mesmo foi baseado no algoritmo de Grover. Conforme descrito na sessão 3.4 a proposta do algoritmo de Grove é aplicar o operador no emaranhado que encapsula o espaço das soluções até o limite de interações possíveis (m) para aumentar a probabilidade de efetuar a medida correta no elemento a ser encontrado. Como trata-se de um algoritmo quântico esse procedimento é realizado até encontrar a solução propriamente dita. Segue seu fonte. qufunct query(qureg x,quvoid f,int n) { int i; for i=0 to #x-1 { // x -> NOT (x XOR n) if not bit(n,i) { Not(x[i]); } } CNot(f,x); // flip f if x=1111.. for i=0 to #x-1 { // x <- NOT (x XOR n) if not bit(n,i) { !Not(x[i]); } } } operator diffuse(qureg q) { H(q); // Hadamard Transform Not(q); // Invert q CPhase(pi,q); // Rotate if q=1111.. !Not(q); // undo inversion !H(q); // undo Hadamard Transform } procedure find(int no, int n) { int i; int j = 0; int x; int l=floor(log(n,2))+1; // no. of qubits int m=ceil(pi/8*sqrt(2^l)); int r=(2^l)-1; qureg q[l]; qureg f[1]; print "N = ",n; print "n = ",l; print "Range into 0 and ",r; print ""; print "Searching ",no; reset; H(q); 34 { for i=1 to m { reset; H(q); query(q,f,no); CPhase(pi,f); !query(q,f,no); diffuse(q); } j= j+1; measure q,x; print j,"a interacao encontrado ",x; } until (x==no); print x, " found"; dump q; } find(No, N); Desta forma, para compreender melhor o algoritmo, será realizado um exemplo instanciado e com uma explicação mais detalhada sobre os motivos que levaram a determinação dos cálculos descritos neste mesmo algoritmo ora proposto. Supondo que o número procurado seja 𝑁𝑜 = 5 = 101𝑏 e que o número de elementos da lista seja 𝑁 = 8 = 23 , 𝑙𝑜𝑔𝑜 𝑛 = 3. 1º Passo. Colocar o registrador quântico em superposição. |𝜓⟩ = 1 𝑁 ∑ |𝑖⟩ √𝑁 𝑖=0 Logo, |𝜓⟩ = |000⟩ + |001⟩ + |010⟩ + |011⟩ + |100⟩ + |101⟩ + |110⟩ + |111⟩ √8 2º Passo. Aplicar a primeira etapa do operador de Grover. |𝜓1 ⟩ = 𝑈𝑓 |𝜓⟩ 35 |𝜓1 ⟩ = 𝑈𝑓 |𝜓1 ⟩ = 1 𝑁 1 ∑ |𝑖⟩ √𝑁 𝑖=0 𝑁 ∑ 𝑈𝑓 |𝑖⟩ √𝑁 𝑖=0 Como 𝑓(𝑖) = { 1, 𝑠𝑒 𝑖 = 𝑖0 , logo 𝑈𝑓 = −1𝑓(𝑖) 0, 𝑠𝑒 𝑖 ≠ 𝑖0 Então |𝜓1 ⟩ = 1 𝑁 ∑ −1𝑓(𝑖) |𝑖⟩ √𝑁 𝑖=0 Portanto, |𝜓1 ⟩ = |000⟩ + |001⟩ + |010⟩ + |011⟩ + |100⟩ − |101⟩ + |110⟩ + |111⟩ √8 3º Passo. Aplicar a segunda etapa do operador de Grover. |𝜓2 ⟩ = 𝑅|𝜓1 ⟩ Como, 𝑅 = 2|𝜓⟩⟨𝜓| − 𝐼 Então, 𝑅|𝜓1 ⟩ = (2|𝜓⟩⟨𝜓| − 𝐼)|𝜓1 ⟩ Com uma manobra algébrica, pode-se escrever |𝜓1 ⟩ da seguinte forma: |𝜓1 ⟩ = |𝜓⟩ − 2 √𝑁 |𝑖0 ⟩ 36 Logo, |𝜓2 ⟩ = 𝑅|𝜓1 ⟩ = (2|𝜓⟩⟨𝜓| − 𝐼)|𝜓1 ⟩ = (2|𝜓⟩⟨𝜓| − 𝐼)(|𝜓⟩ − |𝜓2 ⟩ = 2⟨𝜓|𝜓⟩|𝜓⟩ − ( |𝜓2 ⟩ = ( 4 √𝑁 2 ⟨𝜓|𝑖0 ⟩) − |𝜓⟩ + 2 √𝑁 2 √𝑁 |𝑖0 ⟩) |𝑖0 ⟩ 𝑁−4 ) |𝜓⟩ + |𝑖0 ⟩ 𝑁 √𝑁 Visualizando as probabilidades das possibilidades, |𝜓2 ⟩ = |𝜓2 ⟩ = |000⟩ + |001⟩ + |010⟩ + |011⟩ + |100⟩ + |101⟩ + |110⟩ + |111⟩ 2√8 |000⟩ + |001⟩ + |010⟩ + |011⟩ + |100⟩ + |110⟩ + |111⟩ 2√8 + + 2|101⟩ √8 5|101⟩ 2√8 Ou seja, se uma medição fosse realizada agora, as chances de se obter a solução foram aumentadas consideravelmente, pois caiu pela a metade a probabilidade das outras possibilidades e aumentou-se em 2,5 a probabilidade da solução. Depois desse passo a passo, fica fácil perceber que com mais uma aplicação do operador de Grover no emaranhado, as probabilidades das outras possibilidades se reduzirão a zero e a solução ficará com 100% de chance. A figura 4.1 denota as operações acima descritas. Fig 4.1 –Manobra vetorial do operador de Grover 37 O benchmark dos dados coletados foi criado para 𝑁0 igual ao número procurado e 𝑁 a quantidade de números desordenados. Os dados foram obtidos através de scripts executados em paralelo numa máquina Virtual Linux 64x com 4GB de RAM. Nos casos em QCL foi necessário um tempo alongado de processamento (~ 6 horas) por falta de uma estrutura quântica apropriada para executá-lo. Todo o processamento foi executado realizando 10 repetições para cada uma das três listas de tamanho variável definido por 𝑁0 . Além disso em cada uma das listas também são realizadas buscas de 10 𝑁 distintos. Segue o script que efetua todo esse processamento. benchmark.sh rm -rf *.log for i in 0 1 2 3 4 5 6 7 8 9 do ./benchmark_40.sh ./benchmark_400.sh ./benchmark_4000.sh done benchmark_40.sh N=40 for i in 2 4 6 7 9 19 22 27 33 37 do ./bench.sh $i $N done benchmark_400.sh N=400 for i in 55 58 70 82 90 111 123 288 342 399 do ./bench.sh $i $N done benchmark_4000.sh N=4000 for i in 567 589 685 698 759 900 1976 2086 3096 3985 do ./bench.sh $i $N done Totalizando 100 valores de interações para cada 𝑁0 . 38 Onde, bench.sh I=`echo $1 | tr -d '\r'` N=`echo $2 | tr -d '\r'` iC=`./classical_search $I $N | grep interacao | wc -l ` C=`echo $iC | tr -d '\r'` iR=`./random_search $I $N | grep interacao | wc -l` R=`echo | tr -d a'\r'` Para 𝑁 = $iR 50 tem-se seguinte distribuição de médias para as interações. iB=`./bin_search $I $N | grep interacao | wc -l` B=`echo $iB | tr -d '\r'` iQ=`./quantum_search.sh $I $N | grep interacao | wc -l` Q=`echo $iQ | tr -d '\r'` echo $I" "$N" "$C" "$R" "$B" "$iQ >> benchmark_${N}.log Os gráficos a seguir mostram os valores obtidos no benchmark para as buscas utilizando valores de tamanho de lista 𝑁 = {40, 400 𝑒 4000}. No eixo horizontal dos gráficos são apresentados os valores médios das interações colocados à direita de cada tipo de pesquisa. O valor a ser procurado está na primeira linha acima dos tipos e varia segundo a distribuição abaixo: 𝑁0(𝑁=40) = {2, 4, 6, 7, 9, 19, 22, 27, 33 𝑒 37} 𝑁0(𝑁=400) = {55, 58, 70, 82, 90, 111, 123, 288, 342 𝑒 399} 𝑁0(𝑁=4000) = {567, 589, 685, 698, 759, 900, 1976, 2086, 3096 𝑒 3985} Para cada número procurado tem-se a distribuição das médias de interações mostradas através das barras coloridas segundo o tipo equivalente. 39 Para a quantidade de números desordenados 𝑁 = 40 chegou-se a seguinte distribuição de médias. Média de Interações 30 25 20 15 10 5 0 2 4 6 7 9 19 22 27 33 37 Sequencial 20 26,5 18 15,9 22,7 18 18 22,1 26,3 23,1 Randômica 18 15,4 26 25,3 16 21,3 20 13,8 11,4 19,2 Binária 4 2 3 4 1 0 4 4 5 3 9,1 7,4 11 7 5,5 7,9 6,3 6,9 7,4 5,6 Quântica Fig 4.2 –Média de Interações para N=40 com 10 repetições para cada 𝑁0 Para a quantidade de números desordenados 𝑁 = 400 a distribuição de médias Média de Interações comporta-se dessa forma. 300 250 200 150 100 50 0 58 70 82 90 111 123 288 342 399 Sequencial 192,6 200,5 211,1 216,4 177,9 177,5 199,7 159,5 207,1 172,2 Randômica 181,8 183,9 171,2 242,9 196,2 240,7 203,4 178,9 238,3 222,7 5 6 6 8 7 4 8 8 5 8 62,2 83,9 74,3 45,4 55,8 83,8 47,3 84,5 29,4 55,1 Binária Quântica 55 Fig 4.3 –Média de Interações para N=400 com 10 repetições para cada 𝑁0 40 Para a quantidade de números desordenados 𝑁 = 4000, tem-se. Média de Interações 3000 2500 2000 1500 1000 500 0 567 589 685 698 759 900 Sequencial 1802,4 2555,9 1731,9 1713 1806,9 1294 Randômica 2092 Binária Quântica 1976 2086 3096 3985 2273 2197,1 2135,6 2437 2285 1924,6 2000,9 1780,9 2446,5 2116,6 1558,2 2443 1954,7 11 11 11 11 11 11 11 10 9 11 343,3 520,5 423 444,3 460,7 450,8 746,4 483 397 505,5 Fig 4.4 –Média de Interações para N=4000 com 10 repetições para cada 𝑁0 Média da quantidade de interações das 100 buscas realizadas para cada valor de N. Tabela 4.1 – Média das Interações Sequencial Simples N 40 400 4000 Sequencial Randômico 21,19 191,45 1994,75 Binário Grover 18,77 3,00 7,41 206,00 6,50 62,17 2060,33 10,70 477,45 Desvio padrão da quantidade de interações das 100 buscas realizadas para cada valor. Tabela 4.2– Desvio Padrão das Interações Sequencial Simples N 40 400 4000 Sequencial Randômico 10,64 114,80 1181,89 41 Binário Grover 11,71 1,49 6,21 103,93 1,44 57,79 1221,86 0,64 440,30 De uma forma geral, as pesquisas binárias levaram vantagem por se tratar de pesquisas realizadas em dados previamente ordenados, algo que contribui significativamente para um resultado mais favorável. O algoritmo quântico mostra uma resposta bastante eficiente quando se utiliza um emaranhado de 8 qubits se comparado aos equivalentes clássicos. Muito embora 𝑂(√𝑁) < 𝑂(𝑁), com o acréscimo de apenas 1 qubit, o algoritmo quântico perde em eficiência. Por conta disso, se 𝑁 crescer demais, o problema se torna intratável para o simulador quântico. Também é interessante notar que o algoritmo randômico clássico, se comparado à pesquisa sequencial simples, mostra-se mais eficiente somente quando a quantidade de elementos ultrapassa de 400. Na figura 4.5 e na tabela 4.3 vemos um comparativo entre as quantidades máximas de buscas envolvidas. Analisando as pesquisas sob esse ponto de vista, nota-se que a busca puramente quântica não será pior do que a desordenada e nunca melhor do que a binária. Vale ressaltar que, no cálculo da complexidade binária, o custo do balanceamento contínuo da árvore não foi levado em consideração. Caso fosse, esse acréscimo de custo faria com que a complexidade quântica se aproximasse ainda mais da binária. Tabela 4.3 – Quantidades máximas de buscas N Quantidades máximas de buscas Desordenada Binária Quântica 40 3,68887945 6,32455532 40 400 5,99146455 20 400 4000 8,29404964 63,2455532 4000 Desordenada - O(N) Binária - O(logN) Quântica - O(√N) Fig 4.5 –Comparativo entre as Complexidade 42 Nesse capítulo foi realizado uma comparação entre métodos de buscas tradicionais e uma busca quântica. Foi observado que em listas desordenadas o algoritmo quântico é melhor que o desordenado clássico equivalente. Entretanto esse mesmo algoritmo é mais lento que uma busca binária clássica que está ordenada. Contudo existe um engano na comparação ingênua e direta desses dois algoritmos. O algoritmo de busca binário clássica leva uma vantagem significativa por encontrar um conjunto de dados previamente ordenados e o tempo de busca não leva em consideração o tempo para ordenar os dados. O algoritmo quântico por sua vez não necessita dessa ordenação. A complexidade do algoritmo mais rápido de ordenação de dados (quick sort) é 𝑂(𝑁𝑙𝑜𝑔2𝑁 ). A complexidade da busca binária é 𝑂(𝑙𝑜𝑔2𝑁 ). A complexidade de Grover é 𝑂(√𝑁). Tabela 4.4 – Comparativo de Complexidades n 2 3 4 5 6 7 8 𝒏 𝟐 4 8 16 32 64 128 256 𝒍𝒐𝒈𝑵 𝟐 1,38629 2,07944 2,77259 3,46574 4,15888 4,85203 5,54518 √𝑵 2,00000 2,82843 4,00000 5,65685 8,00000 11,31371 16,00000 𝒏𝒍𝒐𝒈𝑵 𝟐 2,77259 6,23832 11,09035 17,32868 24,95330 33,96421 44,36142 𝑵 𝑵 𝒏𝒍𝒐𝒈𝑵 𝒏𝒍𝒐𝒈𝑵 𝟐 + 𝒍𝒐𝒈𝟐 𝟐 + 𝒍𝒐𝒈𝟐 -√𝑵 4,15888 2,15888 8,31777 5,48934 13,86294 9,86294 20,79442 15,13756 29,11218 21,11218 38,81624 27,50253 49,90660 33,90660 Logo o ganho real entre a busca quântica e a mais rápida clássica será dado pelo custo da solução (ordenação + pesquisa binária – busca quântica), isto é, 𝑂(𝑁𝑙𝑜𝑔2𝑁 ) + 𝑂(𝑙𝑜𝑔2𝑁 ) − 𝑂(√𝑁) e está representado em números na tabela 4.4. Uma informação interessante desses dados é que na medida que 𝑁 aumenta o ganho também aumenta e esse fenômeno não ocorre no cenário clássico. Sob essa ótica, surge o questionamento sobre o comportamento de outros algoritmos quânticos em problemas de decisão mais sofisticados. O capitulo a seguir trata de uma implementação quântica para um problema dessa natureza. 43 Capítulo V Problema das N-Rainhas 5.1 – Definição do Problema O problema das N-Rainhas, originalmente introduzido em 1850 por Carl Gauss, pode ser definido como sendo os conjuntos de posições de 𝑁 rainhas em um tabuleiro de xadrez 𝑁𝑥𝑁, tais que nenhuma rainha pode ser atacada por qualquer outra. Neste sentido, a Figura 5.1 representa os possíveis movimentos de uma rainha e as Figuras 5.2 e 5.3 mostram duas combinações de posições onde a definição do problema não permite que ocorra por conta dos ataques em coluna e diagonal. Fig 5.1 Fig 5.2 Fig 5.3 A Figura 5.4 apresenta uma solução possível para 𝑁 = 4. Fig 5.4 É fácil notar que, em relação a figura 5.4, existe outra solução simétrica invertendo as posições a partir da segunda coluna. Ou seja, para 𝑁 = 4 tem-se duas soluções 𝑁 = {3,1,4,2} 𝑒 {2,4,1,3}, onde cada elemento i do conjunto representa a posição de cima para baixo da rainha na i-ésima coluna do tabuleiro. 44 Desde sua concepção foram criados inúmeros trabalhos relacionados ao problema. Existe uma coletânea bastante densa com relação ao mesmo que pode ser pesquisada em (Kosters, 2013). Nela constam 335 referências sobre o problema, onde o paper ”A Survey of Known Results and Research Areas for n-Queens" é considerado a síntese teórica mais importante (Kosters, 2013) do N-Queens. Nele encontramos a explicação pormenorizada sobre os mais relevantes Teoremas e Conjecturas acerca da questão. Os Teoremas e conjecturas tentam de alguma maneira amenizar o processo de cálculo das soluções, validá-la e prever a quantidade de soluções para um determinado valor de 𝑁. Esse movimento se faz necessário, pois para 𝑁 = 24 obtem-se 227514171973736 soluções. E para 𝑁 = 25 chega-se a espantosas 2207893435808352 soluções (Bell e Stevens, 2009). Sendo que, só é possível chegar nessas soluções utilizando-se computação em grade com algoritmos em paralelo. A solução do problema das rainhas é relevante, pois sua aplicação pode ser encontrada em sistemas de armazenamento de memória em paralelo, testes de VLSI (Very Large Scale Integration), controle de tráfego de redes de computadores e prevenção de deadlock em Sistemas Operacionais. 5.2 – Soluções Clássicas Em síntese, as várias soluções publicadas dependem de uma fórmula específica para a arrumação das rainhas ou de transposição de soluções menores para soluções com valores maiores de 𝑁. É interessante observar que soluções empíricas do problema com tabuleiros menores mostram que o número de soluções aumenta exponencialmente com o aumento de 𝑁. Por exemplo, para 𝑁 = 4, . . . ,11 (Bell e Stevens, 2009), tem-se respectivamente as quantidades de soluções {2, 10, 4, 40, 92, 352, 724, 2680} (Bell e Stevens, 2009). Por conta disso, algumas soluções alternativas veem sendo propostas. O algoritmo clássico mais famoso capaz de gerar sistematicamente todos os possíveis conjuntos de soluções para um determinado valor de 𝑁 é o backtracking ( Kosters, 2013). A abordagem recursiva com “backtracking” pode ser resumida da forma a seguir. Colocam-se as rainhas, uma de cada vez, por linha e por coluna nessa ordem. Dada uma linha, escolhe-se uma coluna de tal forma que a mesma não se respeita (para que não 45 haja ataque por coluna) e verifica se ocorre algum ataque em diagonal. Caso não ocorra ataque, o algoritmo é executado de forma recursiva para a próxima linha. Caso já não existam posições seguras, volta-se a rainha para a linha anterior e procura-se uma nova coluna. Se não for possível encontrar uma coluna segura para essa linha e a quantidade de linhas é menor ou igual a 𝑁 e algoritmo descarta essa combinação de linha e coluna. Se a rainha estiver segura, o processo continua para a próxima linha com outra coluna ainda não escolhida (Fernandes e Alho 2004). Na prática, a abordagem por backtracking produz uma classe muito limitada de soluções à medida que 𝑁 cresce. Além disso, devido a complexidade 𝑂(𝑁!) dessa solução, é extremamente custoso para computadores clássicos montar todas as busca que o problema exige. Desse modo, vários autores veem propondo outras técnicas de busca eficientes para superar essa limitação. Estes métodos incluem métodos de busca heurística (Pothumani, 2009), técnicas de minimização de busca e conflitos locais (Pothumani, 2009). Recentemente, os avanços da pesquisa na área de redes neurais têm gerado vários artigos propondo soluções para o problema. O uso das redes de Hopfield (Pothumani, 2009) tem sido aplicado para o problema de N–Queens, pois trata-se de uma rede neural simples que é capaz de armazenar certos padrões de modo semelhante ao cérebro, onde o padrão completo para um determinado problema pode ser recuperado desde que a rede seja apresentada com dados parciais. Para valores de 𝑁 > 100 (Sosic, 1993) é interessante que se utilize paralelismo clássico. O algoritmo sequencial de tempo linear é exemplo simples de solução e mostrase bastante eficiente. Essa técnica pode ser dividida em dois passos: o inicial e o final. Durante o passo inicial as rainhas são colocadas em sucessivas colunas da esquerda para a direita. Em cada coluna a posição é escolhida randomicamente. Se ocorrer algum conflito com a coluna escolhida, a posição da mesma passa para a próxima posição à direita. Esse processo é repetido até preencher todas as linhas com alguma rainha. No passo final ocorre a verificação de possíveis conflitos levando em consideração as diagonais de todas as rainhas colocadas. É absolutamente óbvio que se ocorrer algum conflito, é escolhida outra posição para a rainha de tal forma que a mesma não sofra nenhum ataque. Esse algoritmo encontra uma solução num tempo proporcional segundo a fórmula abaixo. 𝑇(𝑁) = 3,18𝑁 + 72𝐶𝑁 46 Onde 𝐶𝑁 é uma constante dependente de 𝑁, denotanto o número de rainhas com conflito depois do passo incial. Por exemplo, para 𝑁 = 1000000 tem-se 999950 rainhas sem conflito no primeiro passo com 50 rainhas, em média, relativas aos conflitos do segundo passo (Sosic, 1993). Todo esse cálculo sequencial é paralelizado para cada coluna e o ideal é colocar um processador para cada linha, ou seja, a quantidade de cores deve ser igual à quantidade de rainhas. 5.3 – Solução Híbrida Como o algoritmo de backtracking é capaz de encontrar todas as soluções para um determinado valor de 𝑁, mas à medida que 𝑁 aumenta o mesmo mostra-se intratável para a computação clássica e com o intuito de aumentar a eficiência na busca de uma solução com menor complexidade, pode-se avançar na computação da solução tratando o problema sob a ótica da Computação Quântica. O processo de construção desse novo algoritmo passou por 3 etapas de amadurecimento elencadas a seguir em ordem cronológica: 1. Construção de um algoritmo quântico baseado no backtraking utilizando o algoritmo de Grover. 2. Tentativa de mapear as possíveis séries das soluções com intuito de transformar a solução em QFT (Quantum Fourier Transformation). 3. Algoritmo baseado em busca quântica. O algoritmo de busca de Grover foi utilizado para encontrar uma possível posição numa determinada linha a partir de um algoritmo similar ao de backtraking. Entretanto, essa solução mostrou ser “ingênua”, pois uma simples busca por posições válidas numa determinada linha, além de não trazer o caráter quântico desejado à solução, trazia perda de eficiência no momento da execução. Isso aconteceu devido à necessidade de realizar pesquisas baseadas no algoritmo de Grove para cada linha do tabuleiro, isto é, realizar classicamente sucessivas chamadas do algoritmo quântico e assumindo o ônus da preparação dos disparos dos mesmos. Na verdade, introduzir o caráter quântico desta forma não explora todo o potencial de uma possível solução quântica para esse problema, portanto esse caminho foi desconsiderado. 47 A segunda etapa foi concebida devido ao aumento da quantidade de soluções à medida que 𝑁 cresce. Isso gerou uma suspeita de um possível comportamento funcional num determinado vetor solução, por exemplo. Com ajuda do programa confeccionado em “C” utilizando o algoritmo de backtraking que consta no Apêndice D, para 𝑁 = 4 (4 linhas e 4 colunas) pode-se criar uma série cujos elementos são pares ordenados <linha, coluna> que representa as posições das rainhas no tabuleiro par uma determinada solução. Por exemplo a série 1 da figura 5.5 representa a seguinte solução {<1,2>, <2,4>, <3,1>, <4,3>}. 5 4 3 Série1 2 Série2 1 0 1 2 3 4 Fig 5.5- Soluções de um tabuleiro 4X4 48 Para 𝑁 = 5 as séries comportam-se da forma descrita na figura 5.6. 6 Série1 Série2 5 Série3 4 Série4 Série5 3 Série6 2 Série7 Série8 1 Série9 0 1 2 3 4 Série10 5 Fig 5.6 – Soluções para um tabuleir 5X5 Para 𝑁 = 8 tem-se a distribuição a seguir. 9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8 Fig 5.7 – Soluções para um tabuleiro 8X8 Desta forma, visualmente a suspeita de um possível comportamento funcional faz sentido. Como o conjunto de soluções pode ser dividido por dois e invertido, com uma manobra matemática, é possível colocar o conjunto de soluções para 𝑁 = 4 sob a forma de números complexos. 49 As soluções para 𝑁 = 4 são {2, 4, 1, 3} 𝑒 {3, 1, 4, 2}. Mas como uma pode ser obtida a partir da outra invertendo o tabuleiro em 180º. É possível expressá-las da seguinte forma. {−3 − 𝑖, −1 + 3𝑖, 1 − 3𝑖, 3 + 𝑖} Para se obter o resultado desejado a leitura dos números complexos deve ser realizada da seguinte forma para cada metade da solução acima. Para a primeira metade deve-se: 1. Multiplicar por 𝑖. 2. Remover a parte imaginária 3. Multiplicar por −1. Para a segunda metade deve-se: 1. Multiplicar por 𝑖. 2. Obter o conjugado complexo do resulto e descartar a parte real 3. Multiplicar por 𝑖. Ou seja, aplicando o método acima respectivamente e sucessivamente tem-se: {−3 − 𝑖, −1 + 3𝑖, 1 − 3𝑖, 3 + 𝑖} {1 − 3𝑖, −3 − 𝑖, 3 + 𝑖, −1 + 3𝑖} {1 − 3, −3 − 1, −𝑖, −3𝑖} {−2, −4, −𝑖, −3𝑖} {2, 4, 1,3} Agora invertendo-se o método para cada metade. {−3 − 𝑖, −1 + 3𝑖, 1 − 3𝑖, 3 + 𝑖} {1 − 3𝑖, −3 − 𝑖, 3 + 𝑖, −1 + 3𝑖} {3,1,3 + 1, −1 + 3} {3,1,4,2} Para 𝑁 = 4, esse método de representação é útil, porém para todas as soluções variando o valor de 𝑁 não é possível encontrar uma função que forme a sequência das soluções. Por isso a utilização da Transformada de Fourier Quântica também foi descartada. A proposta híbrida para resolver o problema pode ser encarada como uma busca quântica e sequencial dependendo da coluna. A busca por novas possibilidades de 50 posições válidas para as damas é obtida através da aplicação de um operador unitário que ajusta as possibilidades válidas. Esse operador é obtido da forma descrita a seguir. Para cada linha ocorrerá uma busca na lista 𝑁 = {0, 1, 2, … , 𝑁 − 1}, onde 𝑛 = 𝑙𝑜𝑔2 (𝑁) é a quantidade de q-bits do registrador mais um q-bit para armazernar o resultado da função 𝑓: = {0, 1, 2, … , 𝑁 − 1}, → {0,1}. Definida da seguinte forma: 𝑓(𝑖) = { 1, 0, 𝑠𝑒 𝑖 = 𝑖0 𝑠𝑒 𝑖 ≠ 𝑖0 onde, para todo 𝑓(𝑖) = 1 tem-se a probabilidade confirmada de se encontrar uma posição inválida e 𝑓(𝑖) = 0 representa o conjunto de possibilidades válidas. O conjunto de possibilidades inválidas é facilmente calculado a partir dos valores das damas escolhidas em cada linha levando em consideração as colunas e as diagonais. Desse modo, um estado qualquer pode ser definido como |𝜑⟩ = 1 𝑁−1 √𝑁 ∑ |𝑖⟩ 𝑖=0 E supondo uma posição inválida qualquer diferente de |𝑖0 ⟩. Logo o estado que representa o conjunto de todas as possibilidades válidas pode ser representado por. |𝜑1 ⟩ = |𝜑⟩ − 2 √𝑁 |𝑖0 ⟩ Como |𝜑1 ⟩ = 1 √𝑁 𝑁−1 ∑(−1) 𝑓(𝑖) |𝑖⟩ 𝑖=0 Logo, 𝑓(𝑖) = 0 prepara o emaranhado com possibilidades de posições válidas para futuras medições. Com isso as chances de se obter uma posição válida para cada linha aumentam a eficiência da busca, um processo notadamente inspirado no algoritmo de Grover. No anexo B, tem-se o programa que utiliza o algoritmo explicado acima. Para valores de 𝑁 entre 4 e 128 (2 a 7 qubits) é possível encontrar um caminho válido com mais facilidade utilizando o simulador, mas para valores maiores de que 7 qubits nem sempre o simulador consegue obter êxito para encontrar uma solução. 51 O pseudo-código da estratégia adotada pode ser encarado da seguinte forma. Repita 1.1 Se for a ultima linha busca sequencial 1.2 Senão reset de todos os qubits para |0⟩ 2.Superposição do emaranhado f(0) 3.Negação do vetor 4.Complemento normal 5.Medida 5.Valida e guarda o valor Até encontrar a solução ou até no máximo 𝜋√𝑁 8 vezes. Entrando no console do QCL com o comando qcl e incluindo o algoritmo mostrado no apêndice B com o comando <include nqueens.qc.> é possível realizar vários testes com o novo algoritmo quântico apresentado para resolver o problema das NQueens. Para 𝑁 = 4 chega-se facilmente nas soluções <2,4,1,3> e <3,1,4,2>. Na tabela 5.1 vê-se a saída para valores de 𝑁 iguais ao montante do emaranhado, e alguns exemplos de soluções encontradas pelo algoritmo. Levando-se em consideração que a estrutura não é adequada, o equivalente em backtranking não conseguiria ter esse alcance em tão pouco tempo a menos que estivesse sendo executado numa grade científica. Uma observação importante é que para 𝑁 = 10 fica muito custoso para um computador clássico conseguir chegar numa solução, o que para o híbrido não representa tanta dificuldade assim. Portanto mesmo sendo simulado por um computador clássico, o algoritmo quântico consegue obter respostas melhores do que um clássico em determinados casos. Isso demonstra que em alguns casos, investir em soluções quânticas pode-se ganhar em eficiência mesmo que não se tenha um computador realmente quântico. Logo, é absolutamente óbvio que, algoritmos quânticos sendo executados em computadores quânticos essa eficiência aumentará ainda mais. 52 Tabela 5.1 – Output de execução do algoritmo híbrido qcl>NQueens(4) qcl>NQueens(8) : N-Queens N = 4 com 2 qubits 1 Ciclo : N-Queens N = 8 com 3 qubits 5 Ciclos : N-Queens N = 16 com 4 qubits 4 Ciclos qcl>NQueens(16) qcl>NQueens(32) : N-Queens N = 32 com 5 qubits 7 Ciclos : Alcancei a linha 3 com o primero 1 : Alcancei a linha 3 com o primero 2 : Alcancei o ultimo : Tentando linha 3 Coluna 1 : Mostra a solucao : Alcancei o ultimo : Tentando linha 7 Coluna 3 : Mostra a solucao : Alcancei a linha 13 com o primero 1 : Alcancei o ultimo : Alcancei a linha 27 com o primero 14 : Alcancei o ultimo : 1 : Tentando linha 15 Coluna 8 : Mostra a solucao : Tentando linha 31 Coluna 0 : Mostra a solucao : 5 : 2 : 15 : 3 : 8 : 14 : 4 : 1 : 6 : 5 : 19 : 4 : 3 : 7 : 22 : 2 : 7 : 10 : 25 : 2 : 4 : 17 : 4 : 11 : 10 : 15 : 23 : 3 : 32 : 16 : 8 : 8 : 16 : 12 : 9 : 1 : 2 : 13 : 31 : 6 : 27 : 9 : 24 : 7 : 18 : 13 : 5 : 14 : 6 : 30 : 20 : 12 : 3 : 28 : 11 : 21 : 29 : 26 : 1 53 Na tabela 5.2 tem-se o intervalo médio de execuções para uma bateria de testes com 100 repetições, onde cada repetição é composta pela busca de um caminho possível de distribuição das rainhas sendo 𝑁 a quantidade de linhas e colunas do tabuleiro. Tabela 5.2 Tempo Médio de Execuções N Média dos Intervalos 8 00:00:00,000 16 00:00:01,440 32 00:00:21,260 64 00:05:01,333 128 00:50:33,000 A título de curiosidade é mostrado uma dessas soluções para cada valor de 𝑁. Para 𝑁 = 8 < 3, 7, 2, 8, 5, 1, 4, 6> Para 𝑁 = 16 < 12, 10, 5, 1, 9, 13, 2, 14, 7, 11, 4, 6, 16, 3, 15, 8> Para 𝑁 = 32 < 15, 11, 8, 16, 24, 13, 28, 2, 22, 20, 12, 10, 4, 32, 23, 9, 19, 6, 18, 31, 21, 5, 27, 30, 14, 29, 7, 25, 3, 17, 26, 1> Para 𝑁 = 64 <38, 44, 23, 39, 32, 25, 50, 3, 62, 22, 45, 16, 41, 5, 54, 6, 4, 20, 36, 43, 37, 29, 64, 12, 17, 59, 49, 31, 11, 35, 60, 18, 1, 28, 13, 57, 52, 63, 40, 7, 47, 26, 51, 55, 15, 46, 27, 19, 21, 2, 61, 14, 8, 30, 9, 53, 56, 24, 48, 58, 34, 10, 42, 33> Para 𝑁 = 128 < 81, 6, 17, 122, 50, 56, 73, 29, 121, 65, 117, 83, 114, 4, 93, 107, 28, 92, 11, 63, 70, 25, 123, 78, 26, 74, 15, 13, 37, 42, 103, 71, 40, 67, 14, 54, 68, 119, 100, 98, 27, 35, 20, 53, 62, 69, 104, 45, 49, 9, 97, 79, 128, 34, 80, 124, 110, 118, 55, 21, 95, 57, 126, 106, 84, 96, 87, 36, 105, 22, 46, 61, 3, 112, 91, 111, 59, 18, 5, 33, 64, 30, 127, 108, 58, 85, 24, 52, 115, 47, 41, 86, 31, 12, 23, 94, 125, 1, 10, 16, 43, 89, 39, 109, 60, 76, 51, 77, 38, 88, 113, 8, 48, 120, 7, 90, 101, 32, 2, 82, 75, 19, 72, 44, 116, 102, 66, 99> 54 55 Capítulo VI Conclusão 6.1 – Conclusão Quando Gödel mostrou que a matemática não seria capaz de demonstrar todos os teoremas, ele introduziu o conceito de indecidibilidade especialmente na computação. A partir desse fato histórico, ficou claro que sempre existirão problemas indeterminados e determinados. Além disso, dentre os problemas que possuem solução, os mesmos podem ser subdivididos em tratáveis e intratatáveis. O poder de abstração da realidade promovido pela Máquina de Turing e unido com a mecânica quântica deu origem à computação quântica. Essa, por sua vez, produziu um avanço nas soluções dos problemas determinados e intratáveis do mundo clássico fazendo com que os NP-Completos (o caixeiro viajante, a busca em grafos, a fatoração de inteiros etc) ganhassem soluções com maior eficiência. Este fato deve-se ao conceito dos qubits através do mundo aleatório introduzido pelo vetor de estado numa base ortonormal de números complexos. Por isso que alguns autores costumam dizer que a equação FÍSICA ∪ MATEMÁTICA ≡ COMPUTAÇÃO passa a ter sentido quando se tenta contextualizar os fundamentos da computabilidade. Os números levantados na prova de conceito do trabalho ilustraram que o algoritmo quântico de busca terá um ganho quadrático de eficiência se for possível simulá-lo numa Máquina de Turing Quântica. Entretanto, tais máquinas quânticas ainda não estão disponíveis, tornando-se necessário simulá-las em computadores clássicos. Sob esta ótica, a capacidade de lidar com emaranhados em computadores clássicos mostrouse ineficiente à medida que se aumenta a quantidade de qubits. Este trabalho permitiu uma melhor compreensão da relação entre os conceitos de complexidade, indecidibilidade e mecânica quântica. Desta compreensão, foi possível entender as Máquinas de Turing clássica e quântica. Em seguida foi realizado um estudo mais detalhado dos algoritmos de Deutsch, Grover e Shor. Este estudo dos algoritmos forneceu explicações significativamente mais detalhadas, e assim se espera, didáticas, do 56 que aquelas encontradas na literatura. Logo após, foram feitos experimentos com a Quantum Computing Language com o objetivo de exercitar uma prática de programação permeada de conceitos da mecânica quântica. Neste sentido, foi implementada a busca de um número em uma lista não ordenada, cujos resultados foram comparados com soluções clássicas. Com o aprendizado adquirido no algoritmo de Grover e encorajado pelos resultados satisfatórios obtidos na prova de conceito foi possível a implementação de um algoritmo híbrido que conseguiu, mesmo que, utilizando o simulador clássico, obter soluções para o problema das N-Rainhas com número de casas acima de 9. Fato esse que, somente algoritmos clássicos mais sofisticados tais como: busca heurísticas, técnicas de minimização de busca e redes neurais conseguem fazê-lo. Ainda assim estes mesmos algoritmos fazem uso de vários recursos computacionais providos pelo paralelismo com inúmeros processadores e máquinas, o que não foi preciso na solução mostrada no trabalho. Desta forma, os resultados sugerem que a computação quântica é uma evolução em relação aos métodos tradicionais da computabilidade. 57 6.2 – Trabalhos Futuros Com a construção do algoritmo híbrido para solucionar o problema das N-Rainhas chegou-se em soluções com tabuleiros de até 128 casas. Na tentativa de encontrar soluções para tabuleiros maiores, sugere-se a execução do programa em QCL em máquinas com maior capacidade de processamento, tendo um limite aceitável para tabuleiros em torno de 1024 casas. Para encontrar todas as soluções propõe-se três melhorias. A primeira seria encontrar o melhor do ponto de corte entre a parte quântica e a clássica dentro do algoritmo, pois à medida que se evolui na descoberta das posições, quanto maior for a precisão do corte, maior será a eficiência ao algoritmo. A segunda seria descobrir uma maneira de orquestrar diferentes execuções quânticas utilizando paralelismo e recursividade clássicos, onde cada execução terá como objetivo encontrar todas as soluções para uma combinação fixa de linha e coluna. Algo semelhante ao algoritmo de backtraking, mas acrescido da solução quântica proposta no trabalho. E por último, sugere-se a reconstrução da ideia inicial calcada no algoritmo de Grover passar a ser analisada sob a ótica de caminhadas quânticas. 58 Bibliografia Bernstein , Ethan; Vazirani, Umesh. “Quantum complexity theory”, In: Proc. 25th Annual ACM Symposium on Theory of Computing, ACM, pp.11-20, 1993. Bernstein , Ethan; Vazirani, Umesh. “Quantum complexity theory”, In: SIAM Journal on Computing archive, Volume 26 Issue 5, Oct, pp.1411-1473, 1997. Cafezeiro, Isabel; Heausler, Edward Hermann.“Computabilidade”, VI ERIMG, DCC, UFLA, 29 a 31 de Agosto de 2007.< http://www.bcc.unifalmg.edu.br/~humberto/disciplinas/2010_paa/leitura/complementar_aula01.pdf>, Acessado em 19 de maio de 2012. Costa, Newton C. A., “Filosofia da Ciência”, Scientific American Brasil,< http://www.cs.auckland.ac.nz/~chaitin/costa2.html>, Acessadoem03 de fevereiro de 2012. Damásio, Antonio. “O Erro de Descartes”, Cia das Letras SP, 2ª ed., 1994. Del Nero, Henrique Schützer, “O Sítio da Mente”, c.11, São Paulo, Collegium Cognitio, 1997. Deutsch, D. “Quantum Theory, the Church-Turing Principle and the Universal Quantum Computer”, Procceedings of the Royal Society A, Mathematical, Physical & Engineering Sciences, 400, pp.97-117, 1985. Goldstein, Rebecca. “The Proof and Paradox”, Atlas Books, 2ª ed., 2005. Green, Brain, “Realidade Oculta”,Companhia das Letras, 1ª ed., SP, 2012. Griffiths, David J. "Introduction to Quantum Mechanics", Perason Prentice Hall, New Jersey, 2ª ed., 2004. Hodges, Andrew. “Turing Um filósofo da Natureza”. UNESP, SP, 1ª ed., 1997. Kubrusly, Ricardo S., “Uma viagem informal ao Teorema de Gödel”, Instituto de Matemática, Universidade Federal do Rio de Janeiro,<http://im.ufrj.br/~risk/diversos/godel.html/>, Acessado em 21 de janeiro de 2012. 59 Mello, Flávio Luis de. “Teoria da Computação”, Notas de aula do curso de Engenharia Eletrônica e de Computação, Escola Politécnica, Universidade Federal do Rio de Janeiro, 2014. McDermott, Drew, “Artificial Intelligence meets Natural Stupidity”, MIT AI Lab, AI Forum, SIGART Newsletter, n. 57, pp.4-9, Abril, 1976. Nielsen, Michael A., “Computação Quântica e Informação Quântica”, Bookman, PA, 1ª ed., 2005. Nunes, Maria das Graças Volpe; Dosualdo, Daniel Gomes , “Tese de Church”, Notas de Aula do curso de Teoria da Computação, Instituto de Ciências Matemáticas e de Computação, Universidade Estadual de São Paulo, <http://www.icmc.usp.br/~gracan/teoria/SubItem32Teoria.html>, Acessado em 13 de maio de 2012. Ömer, Bernard, “A Procedural Formalism for Quantum Computing”. Tese de Doutorado, Viena, 1998. Palazzo, Luiz A. M. “Máquina de Turing - Linguagens Sensíveis ao Contexto e Enumeráveis Recursivamente”, Texto 5 do Paper Linguagens Formais e Autômatos, Universidade de Pelotas, 2007. Paulo, Luiz. “Datamining”. Ciência Moderna, RJ, 1ª ed., 2005. Penrose, Roger. “O Pequeno o Grande e a Mente Humana”, MIT Press, EUA, 1ª ed., 1996. Pinker, Steven. “Como a Mente Funciona”. Cia das Letras, SP, 2ª ed., 1998. Portugal, Renato, “Introdução à Computação Quântica”2ª ed., SP, 2012. Portugal, Renato, “Algoritmos Quânticos de Busca”, 1ª ed., SP, 2012. Ribeiro, Henrique de Morais. “Uma Revisão da Teoria Quântica da Consciência de Penrose e Hameroff”. Revista Eletrônica Informação e Cognição, SP, 2001. Scientific American Brasil (sciam), Prêmio Nobel de Física 2012, <http://www2.uol.com.br/sciam/noticias/o_premio_nobel_de_fisica_de_2012.html>, Acessado em 15 de dezembro de 2012. 60 Toscani, L.V., Veloso, P.A., “Complexidade de Algoritmos: análise, projetos em métodos”, Sagra-Luzzato, Porto Alegre, Instituto de Informática da UFRGS, 2001. Pothumani, S. “Solving N Queen Problem Using Various Algorithms – A Survey”, International Journal of Advanced Research in Computer Science and Software Engineering, Volume 3 Questão 2, Fevereiro de 2009. Kosters, Walter. “N-Queens bibliography”, 2013, <http://www.liacs.nl/~kosters/nqueens/>, acesso em 20 de maio de 2014. Bell , J. and Stevens, B. “A Survey of Known Results and Research Areas for nQueens”, Discrete Mathematics, vol. 309, pp. 1-31, 2009. Fernandes, Marco e Alho, Miguel. "Solução eficiente para a resolução do Problema das N-Rainhas", REVISTA DO DETUA, vol. 4, Nº 2, Janeiro de 2004 Sosic, Rok. “A Parallel Search Algorithm for the N-Queens Problem”, School of Computing and Information Technology, Technical Report CIT-94-3, Agosto de 1993. 61 Apêndice A - QCL QCL (Quantum Computation Language) é uma linguagem de programação quântica (QPL) de alto nível que possui as principais características. Linguagem de controles clássicos com funções de controle de fluxo e interações com vários tipos de dados clássicos (int, string, boolean, real, complex) 2 tipos de operadores quânticos unitários gerais (operator) e portas pseudoclássicas reversíveis (qufunc) Execução inversa, permitindo execuções on-the-fly de um operador inverso através do cache de chamadas desses operadores. Acesso a vários tipos de dados quânticos (qubit registers) com informações em tempo de compilação (qureg, quconst, quvoid, quscratch). Funções convenientes para manipular registradores quânticos q[n] - qubit, q[n: m] - substring q & p - registro combinado Gerenciamento de memória quântica (quheap) possibilitada por variáveis locais quânticas. Integração transparente com gestão de espaço Bennet-style Fácil adaptação de conjuntos individuais compostos por operadores elementares 62 Exemplo: Transformada de Fourier Discreta dft.qcl operator dft(qureg q) { // main operator const n=#q; // set n to length of input int i; int j; // declare loop counters for i=0 to n-1 { for j=0 to i-1 { // apply conditional phase gates CPhase(2*pi/2^(i-j+1),q[n-i-1] & q[n-j-1]); } Mix(q[n-i-1]); // qubit rotation } flip(q); // swap bit order of the output } O bloco de código acima mostra a implementação quântica da Transformada de Fourier Discreta que serve de base para o algoritmo de Shor. Basicamente, dft.qcl contém dois circuitos: o loop externo realiza a Transformação de Hadamard do maior para o menor qubit (Mix), enquanto que o laço interno executa mudanças de fase condicionais (CPhase) entre os qubits. O operador dft possui um registrador quântico (qureg) q como argumento. O registrador quântico não é um estado quântico por si, mas sim um ponteiro que indica os qubits alvos no estado geral da máquina, assim como as linhas de entrada e saída no modelo de portas lógicas clássicas. Para permitir registrador independente da definição dos operadores, o número de qubits do registrador pode ser determinado em tempo de execução pelo tamanho do operador #. Assim como em linguagens procedurais clássicas, dentro da definição do operador, sub-operadores podem ser chamados apenas como subprocedimentos. Isto significa que a sequência real dos operadores (embutido ou definidos pelo usuário) pode ser totalmente determinada em tempo de execução, incluindo o uso de loops (neste caso, for-loops), declarações condicionais, etc. Semelhante a outras as linguagens clássicas, QCL tem uma rigorosa semântica matemática de funções e operadores, ou seja, duas chamadas subsequentes com os mesmos parâmetros têm que resultar exatamente na mesma operação. Isto exige que os operadores devam ser livres de efeitos colaterais, não sendo permitido o uso de variáveis globais. QCL permite execuções fora do contexto, por isso é possível fazer a chamada DFT(q) e !DFT(q), sendo que a segunda chamada terá todos os operadores invertidos e executados na ordem inversa. 63 O interpretador em QCL simula um computador quântico dentro de um número arbitrário de qubits e é chamado com a seguinte sintaxe. 𝒒𝒄𝒍 [𝒐𝒑𝒕𝒊𝒐𝒏𝒔][𝑸𝑪𝑳 𝒇𝒊𝒍𝒆𝒔] Seguem as opções para a linha de comando. Startup Options: -h, --help display this message -V, --version display version information -i, --interactive force interactive mode -n, --no-default-include don't read default.qcl on startup -o, --logfile specify a logfile -b, --bits=n: set number of qubits (32) Dynamic Options (can be changed with the set statement): -s, --seed=<seed-value> set random seed value (system time) -I, --include-path=<path> QCL include path (.) -p, --dump-prefix=<file-prefix> set dump-file prefix -f, --dump-format=b,a,x list base vectors as hex, auto or binary (a) -d, --debug=<y|n> open debug-shell on error (n) -a, --auto-dump=<y|n> allways dump quantum state in shell mode (n) -l, --log==<y|n> log external operator calls (n) -L, --log-state==<y|n> log state after each transformation (n) -T, --trace==<y|n> trace mode (very verbose) (n) -S, --syntax=<y|n> check only the syntax, no interpretation (n) -E, --echo=<y|n> echo parsed input (n) -t, --test=<y|n> test programm, ignore quantum operations (n) -e, --shell-escape=<y|n> honor shell-escapes (y) -r, --allow-redefines=<y|n> ignore redefines instead of error (n) A menos que esteja desabilitada com a opção --no-default-include, QCL inicia com o include do arquivo default.qcl. Se nenhum arquivo for especificado na linha de comando, QCL inicia em modo interativo via prompt de comandos. 64 Em síntese, qualquer programa quântico deve ser a composição de inicializações, operadores unitários e medições. Um típico algoritmo quântico probabilístico geralmente é executado num ciclo de avaliação. A demonstração abaixo pode ser encarada como um exemplo mínimo de um algoritmo quântico. single.qcl { reset; // R: |Psi> -> |0> myoperator(q); // U: |0> -> |Psi'> measure q,m; // M: |Psi'> -> |m> } until ok(m); // picked the right m ? O comando reset “reseta” o estado da máquina de |𝜓⟩ para |0⟩. O comando measure mede o registrador quântico e atribui o valor medido em bits para o identificador de variável inteira m. Se nenhum valor é retornado, a variável fica sem atribuição. O resultado da medição é determinado por um número aleatório dentro das possibilidades contidas no registrador no momento da medição respeitando as probabilidades modificadas pelo operador myoperator. Uma vez que as operações de reset e medida são irreversíveis, as mesmas não devem ocorrer dentro das definições do operador. 65 Apêndice B – Algoritmo de Deutsch const QCL coin1=(random()>=0.5) (Quantum Computation Language) é uma linguagem de programação const coin2=(random()>=0.5) quântica (QPL) de alto nível que possui as principais características. boolean f(boolean x) { if coin1 { // coin1=true -> g is constant return Linguagem de controles clássicos com funções de controle de fluxo e interações coin2; } else { com // coin1=false -> gboolean, is balanced vários tipos de dados clássicos (int, string, real, complex) return x xor coin2; 2 tipos de operadores quânticos unitários gerais (operator) e portas pseudo} } Operador F(quconst x,quvoid y) { if f(false) xor f(true) { CNot(y,x); } if f(false) { Not(y); } } operator U(qureg x,qureg y) { H(x); F(x,y); H(x & y); } procedure deutsch() { qureg x[1]; qureg y[1]; int m; { reset; U(x,y); measure y,m; } until m==1; measure x,m; print "f(0) xor f(1) =",m; Incializa a máquina Executa o operador É realizada a medida do Segundo registrador Repete-se o loop até encontrar 𝑓(𝑥) = 1 Lê-se o valor de 𝑥 Imprime o resultado } 66 Apêndice C – Busca Quântica N-Queens nqueens.qcl include "grover.qcl"; boolean ataca(int x1, int y1, int x2, int y2) { //mesma linha ou mesma coluna if ((x1==x2) or (y1==y2)) {return true;} //teste das diagonais if (x1>x2) { if ((x1-x2 == y1-y2) or (x1-x2 == y2-y1)) {return true;} } if (x2>x1) { if ((x2-x1 == y2-y1) or (x2-x1 == y1-y2)) {return true;} } return false; } boolean funciona(int k, int vector queen) { int i; for i=0 to k-1 { if (ataca(i, queen[i], k, queen[k])) { return false; } } return true; } operator exclude(qureg q, int n) { qureg f[0]; H(q); query(q,f,n); } int ValidandoPosicao(int vector queens, int colexcluida, int linha) { if (funciona(linha, queens)) { return queens[linha]; } else { return -1; } } 67 procedure NQueens(int N) { int n=floor(log(N,2)); int L; int i; int j; int k; int x; int ciclo; int vector queens[N]; int vector index[N]; qureg q[n]; print "N-Queens N =",N,"com",n,"qubits"; //Zera todo o vetor de solucao for i=0 to N-1 { queens[i] = -1; } //Zera todo o vetor reverso solucao for i=0 to N-1 { index[i] = -1; } reset; k = -1; L = 0; ciclo = 0; while (queens[N-1] == -1) { //Primeiro elemento do vetor, qualquer posicao funciona if (L == 0) { ciclo = ciclo+1; k = k+1; if (k == N) { k = 0; } queens[L] = k; //print "Forcei o primeiro",k; } else { if (L == N-1) { print "Alcancei o ultimo"; i = -1; { i = i+1; } until (i < N) and (index[i] == -1); print "Tentando linha",L,"Coluna",i; queens[L] = i; if not (funciona(L, queens)) { queens[L] = -1; print "Nao achei"; } } //Se for o ultimo elemento, nao há mais necessidade de procurar por outras opções else { j = 0; //Varro o vetor while j < L and (queens[L] == -1) { //print "Tentando encontrar uma posicao"; //Excluo a coluna exclude(q, queens[j]); measure q,x; queens[L] = x; //print "Tentando validar a coluna ",queens[L],"na linha",L,"excluindo a coluna",queens[j]; 68 queens[L] = ValidandoPosicao(queens, queens[j], L); if (queens[L] == -1) { //Excluo a diagonal à esquerda if (queens[j] - (L-j) > -1) { exclude(q, queens[j] - (L-j)); measure q,x; queens[L] = x; //print "Tentando validar a coluna ",queens[L],"na linha",L,"excluindo a coluna",(queens[j]-(L-j)); queens[L] = ValidandoPosicao(queens, (queens[j]-(L-j)), L); if (queens[L] == -1) { //Excluo a diagonal à direita if (queens[j] + (L-j) < N) { exclude(q, queens[j] + (L-j)); measure q,x; queens[L] = x; //print "Tentando validar a coluna ",queens[L],"na linha",L,"excluindo a coluna",(queens[j]+(L-j)); queens[L] = ValidandoPosicao(queens, (queens[j]+(L-j)), L); } } } } j = j+1; } } } // print "Linha",L,"Coluna",queens[L]; if (queens[L] == -1) { print "Alcancei a linha",(L+1),"com o primero",(k+1); for i=0 to N-1 { queens[i] = -1; } for i=0 to N-1 { index[i] = -1; } L = 0; reset; } else { index[queens[L]] = L; //print "Achei Linha",i,"Coluna",queens[i]; L = L+1; } } //Mostra a solucao if (queens[N-1] != -1) { print "Mostra a solucao"; for i=0 to N-1 { if (queens[i] != -1) { print queens[i]+1; } } //exit; } } 69 Apêndice D – Backtraking N-Queens nqueens.cpp #define EXIT_SUCCESS 1; #include <iostream> #include <cstdlib> #include <stdio.h> using namespace std; class Rainhas{ public: int contador; bool imprimeTabuleiros; int rainha[25]; int N; //Construtor Padrao Rainhas(){ contador = 0; imprimeTabuleiros = false; } void SetN(int n){ if ( n < 4 || n > 25 ) { cout<<"********************************************************"<<endl; cout<<"**** Entre com N entre 4 e 25 ****"<<endl; cout<<"********************************************************"<<endl; exit(1); } N = n; rainha[0] = 0; for (int i = 1; i<N;i++){ rainha[i] = N; } } void start(){ busca (rainha,0); cout << "Numero total de solucoes: " << contador << endl; } } 70 private: void busca (int rainha[], int k) { if (rainha[0]>(N-1)) return;//percorreu todas as posicoes possiveis ja achou solucao if (rainha[k]>(N-1)) { k--; //retorna para a ultima rainha rainha[k]++;//passa para a proxima posicao da rainha //nova busca busca(rainha, k); } //depois de passar por todos os campos do tabuleiro... if ((k==(N-1))&&(funciona(rainha,k)))//se esta na ultima posicao e funciona, achou solucao { contador++; //contador de solucoes impressao(rainha); //impressao da solucao rainha[k]++; //nova busca busca(rainha,k); } if (funciona(rainha,k)){ //funciona nesta posicao, passa para a proxima k++; //proxima rainha rainha[k]=0; //comeca a partir da posicao 0 } else{//senao, passa para a proxima posicao rainha[k]++; } //nova busca busca(rainha,k); } bool funciona (int rainha[], int k) { for (int i=0; i<k; i++){ if (ataca(i, rainha[i], k, rainha[k])) return false; } return true; } void impressao(int rainha[]) { for (int i=0; i<N; i++) cout << (1 + rainha[i]) << " cout<<endl; "; //opcao de impressao dos tabuleiros if (imprimeTabuleiros){ //cout<<"Deseja imprimir matriz desta solucao (s/n)?"<<endl; //cin>>x; // } //if (x =='s'){ for (int i=0; i<N; i++){ for (int j=0; j<N; j++){ if (j == rainha[i]){ cout<<" O"; } else{ cout<<" x"; } } cout<<endl; } cout << endl; } } 71 bool ataca(int x1, int y1, int x2, int y2) { if ((x1==x2)||(y1==y2)) return true;//mesma linha ou mesma coluna //teste das diagonais if (x1>x2){ if ((x1-x2 == y1-y2)||(x1-x2 == y2-y1)) return true; } if (x2>x1){ if ((x2-x1 == y2-y1)||(x2-x1 == y1-y2)) return true; } return false; } }; int main(int argc, char *argv[]) { int a; //cria objeto rainha if (argc == 2) { a = atoi(argv[1]); Rainhas r; r.SetN(a); r.start(); return EXIT_SUCCESS; } } 72