Skip to main content
IBM Quantum Platform

Solucionador de Eigenvalores Quânticos Variacionais (VQE)

Para este módulo, os alunos devem ter um ambiente Python em funcionamento e as versões mais recentes dos seguintes pacotes instaladas:

  • qiskit
  • qiskit_ibm_runtime
  • qiskit-aer
  • qiskit.visualization
  • numpy
  • pylatexenc

Para configurar e instalar esses pacotes, consulte o guia Instalar o Qiskit. Para executar trabalhos em computadores quânticos reais, os alunos precisarão configurar uma conta IBM Cloud, seguindo as etapas do guia Configurar sua conta IBM Cloud.

Esse módulo foi testado e usou aproximadamente 8 minutos de tempo de QPU. Essa é uma estimativa, e seu uso real pode variar.

# Uncomment and modify this line as needed to install dependencies
#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'

Introdução

Desde o desenvolvimento do modelo mecânico quântico no início do século XX, os cientistas entenderam que os elétrons não seguem caminhos fixos ao redor do núcleo de um átomo, mas existem em regiões de probabilidade chamadas orbitais. Esses orbitais correspondem a níveis de energia específicos e discretos que os elétrons podem ocupar. Os elétrons residem naturalmente nos níveis de energia mais baixos disponíveis, conhecidos como estado fundamental. No entanto, se um elétron absorver energia suficiente, ele pode saltar para um nível de energia mais alto, entrando em um estado excitado. Esse estado excitado é temporário, e o elétron acabará retornando a um nível de energia mais baixo, liberando a energia absorvida, geralmente na forma de luz. Esse processo fundamental de absorção e emissão de energia é importante para entender como os átomos interagem e formam ligações.

Quando os átomos se juntam para formar moléculas, seus orbitais atômicos se combinam para formar orbitais moleculares. A disposição e os níveis de energia dos elétrons dentro desses orbitais moleculares determinam as propriedades da molécula resultante e a força das ligações químicas. Por exemplo, na formação de uma molécula de hidrogênio ( H2H_2 ) a partir de dois átomos de hidrogênio individuais, o elétron de cada átomo ocupa orbitais atômicos. À medida que os átomos se aproximam um do outro, esses orbitais atômicos se sobrepõem e se combinam para formar novos orbitais moleculares - um com energia mais baixa (um orbital de ligação) e outro com energia mais alta (um orbital anti-ligação). Os dois elétrons, um de cada átomo de hidrogênio, ocuparão preferencialmente o orbital de ligação de menor energia, levando à formação de uma ligação covalente estável que mantém a molécula H2H_2 unida. A diferença de energia entre os átomos separados e a molécula formada, especialmente a energia dos elétrons nos orbitais moleculares, determina a estabilidade e as propriedades da ligação.

Nas seções a seguir, exploraremos esse processo de formação molecular, com foco na molécula H2H_2. Usaremos um computador quântico real, combinado com técnicas de otimização clássica, para encontrar a energia desse processo simples, porém fundamental. Esse experimento fornecerá uma demonstração prática de como a computação quântica pode ser aplicada para resolver problemas em química computacional, fornecendo insights sobre a função da energia do elétron.


VQE - Um algoritmo quântico variacional para problemas de valores próprios

Técnicas de aproximação para química - princípio variacional e conjunto base

As contribuições de Erwin Schrödinger para a mecânica quântica não se limitam à introdução de um novo modelo eletrônico; fundamentalmente, ele estabeleceu a mecânica das ondas ao desenvolver a famosa equação de Schrödinger dependente do tempo:

iddtψ=H^ψi\hbar \frac{d}{dt}|\psi\rangle = \hat{H}|\psi\rangle

Aqui, H^\hat{H} é o operador Hamiltoniano, que representa a energia total do sistema, e ψ|\psi\rangle é a função de onda que contém todas as informações sobre o estado quântico do sistema. (Observação: ddt\frac{d}{dt} é a derivada do tempo total, e não incluímos explicitamente o valor próprio da energia EE aqui)

No entanto, em muitas aplicações práticas, como a determinação dos níveis de energia permitidos de átomos e moléculas, usamos a equação de Schrödinger independente do tempo (equação do valor próprio da energia), que é derivada da forma dependente do tempo, assumindo um estado estacionário. Um estado estacionário é um estado quântico no qual a densidade de probabilidade de encontrar uma partícula em um determinado ponto no espaço não muda com o tempo.

H^ψ=Eψ\hat{H}|\psi\rangle = E|\psi\rangle

Nessa forma, EE representa o valor próprio de energia correspondente ao estado quântico ψ|\psi\rangle. O Hamiltoniano inclui várias contribuições de energia, como a energia cinética de elétrons e núcleos, as forças atrativas entre elétrons e núcleos e as forças repulsivas entre elétrons.

A solução da equação do valor próprio de energia nos permite calcular os níveis de energia quantizada dos sistemas atômicos e moleculares. Entretanto, no caso das moléculas, é difícil resolvê-lo com exatidão porque a função de onda Ψ\Psi, que descreve a distribuição espacial dos elétrons, é complexa e de alta dimensão.

Como resultado, os cientistas usam técnicas de aproximação para obter soluções práticas e precisas. Neste trabalho, vamos nos concentrar em dois métodos principais:

  1. Princípio variacional

    Esse método aproxima a função de onda e a ajusta para chegar o mais próximo possível da energia alvo, geralmente a energia do estado fundamental do sistema. A ideia principal por trás do princípio variacional é simples:

    • Se adivinharmos uma função de onda Ψtrial\Psi_\text{trial} (uma "função de teste"), a energia calculada a partir dela será sempre igual ou superior à energia do estado fundamental ( E0E_0 ) do sistema. Eapprox=ΨtrialH^ΨtrialΨtrialΨtrialE0E_\text{approx} = \frac{\langle \Psi_\text{trial}|\hat{H}|\Psi_\text{trial}\rangle}{\langle \Psi_\text{trial}|\Psi_\text{trial}\rangle} \geq E_0
    • Ajustando os parâmetros θ\theta na função de teste, Ψtrial(θ)|\Psi_\text{trial}(\theta)\rangle, podemos obter uma aproximação cada vez melhor da energia do estado fundamental.
    • Sua precisão depende muito da escolha da função de onda de teste Ψtrial\Psi_\text{trial}. Uma função de teste mal escolhida pode levar a uma estimativa de energia que está longe de ser precisa.
  2. Aproximação do conjunto de bases

    O segundo método de aproximação vem no estágio de construção da função de onda - a abordagem do conjunto de bases. Na química quântica, é quase impossível resolver a equação de Schrödinger exatamente para moléculas. Em vez disso, aproximamos a complexa função de onda de vários elétrons, construindo-a a partir de funções matemáticas mais simples e predefinidas. Um conjunto de bases é essencialmente uma coleção dessas funções matemáticas conhecidas, normalmente centradas nos átomos da molécula, que são usadas como blocos de construção para representar a forma e o comportamento dos elétrons no sistema. Pense nisso como tentar recriar uma escultura detalhada usando apenas uma coleção de peças padrão da LEGO - quanto mais tipos e tamanhos de peças você tiver (quanto maior o conjunto de base), mais precisamente você poderá se aproximar da forma original.

    Essas funções de base geralmente são inspiradas nas soluções analíticas de sistemas simples, como o átomo de hidrogênio, assumindo formas como funções gaussianas ou do tipo Slater, embora ainda sejam aproximações. Em vez de trabalhar com os orbitais moleculares completos teoricamente "exatos", mas intratáveis, nós os expressamos como uma combinação linear (uma soma com coeficientes) dessas funções de base. Esse método é conhecido como abordagem LCAO (Linear Combination of Atomic Orbitals, combinação linear de orbitais atômicos) quando as funções de base se assemelham a orbitais atômicos. Ao otimizar os coeficientes dessa combinação linear, podemos encontrar a melhor função de onda aproximada possível e a energia dentro das limitações do conjunto de bases escolhido.

    • Quanto mais funções forem incluídas no conjunto de base, melhor será a aproximação, mas isso tem o custo de um esforço computacional maior.
    • Um conjunto de base pequeno fornece uma estimativa aproximada, enquanto um conjunto de base grande fornece resultados mais precisos à custa da necessidade de mais recursos computacionais.

Em resumo, para tornar os cálculos viáveis e reduzir o custo computacional, usamos o princípio variacional aproximando a função de onda, o que reduz a complexidade computacional e permite a otimização iterativa para minimizar a energia. Enquanto isso, a abordagem do conjunto de bases simplifica os cálculos ao representar os orbitais atômicos como uma combinação de funções predefinidas, em vez de resolver diretamente uma função de onda contínua.

Verifique sua compreensão

Considere a função de onda de teste Ψtrial(α,x)=Aeαx2\Psi_\text{trial}(\alpha,x) = Ae^{- \alpha x^2} em que AA é uma constante de normalização e α\alpha é um parâmetro ajustável.

(a) Normalize a função de onda experimental determinando um valor de AA tal que Ψtrial2dx=1\int_{-\infty}^{\infty} |\Psi_\text{trial}|^2 dx = 1.

  • Para normalizar uma determinada função de onda experimental:

    Ψtrial2dx=A2e2αx2dx=1\int_{-\infty}^{\infty} |\Psi_\text{trial}|^2 dx = \int_{-\infty}^{\infty} A^2 e^{-2 \alpha x^2} dx = 1

    Use a integral Gaussiana:

    eax2dx=πa, for a>0 \int_{-\infty}^{\infty} e^{-a x^2} dx = \sqrt{\frac{\pi}{a}} \text{, for } a>0

    definir a=2αa = 2\alpha e obter: A2πa=1A^2\sqrt{\frac{\pi}{a}} = 1 A=(2απ)1/4\therefore A = (\frac{2\alpha}{\pi})^{1/4}

(b) Calcule o valor esperado do hamiltoniano H^\hat{H}, dado por H^=22md2dx2+V(x) \hat{H} = -\frac{\hbar^2}{2m} \frac{d^2}{dx^2} + V(x), onde V(x)=12mω2x2V(x) = \frac{1}{2}m\omega^2x^2, o que corresponde a um potencial de oscilador harmônico simples.

  • O hamiltoniano de um oscilador harmônico é:

    H^=22md2dx2+12mω2x2\hat{H} = -\frac{\hbar^2}{2m} \frac{d^2}{dx^2} + \frac{1}{2} m \omega^2 x^2

    Valor esperado da energia cinética

    T=22mΨtriald2dx2Ψtrialdx \langle T \rangle = -\frac{\hbar^2}{2m} \int_{-\infty}^{\infty} \Psi_\text{trial}^* \frac{d^2}{dx^2} \Psi_\text{trial} dx

    Tomando a segunda derivada:

    ddxΨtrial=2αxAeαx2\frac{d}{dx} \Psi_\text{trial} = -2\alpha x A e^{-\alpha x^2}

    d2dx2Ψtrial=Aeαx2(4α2x22α)\frac{d^2}{dx^2} \Psi_\text{trial} = A e^{-\alpha x^2} (4\alpha^2 x^2 - 2\alpha)

    Assim:

    T=22mA2e2αx2(4α2x22α)dxT = -\frac{\hbar^2}{2m} \int_{-\infty}^{\infty} A^2 e^{-2\alpha x^2} (4\alpha^2 x^2 - 2\alpha) dx

    Usando resultados integrais Gaussianos padrão:

    T=2α2m\langle T \rangle = \frac{\hbar^2 \alpha}{2m}

    Valor esperado da energia potencial

    V=12mω2x2Ψtrial2dx\langle V \rangle = \frac{1}{2} m \omega^2 \int_{-\infty}^{\infty} x^2 |\Psi_\text{trial}|^2 dx

    Usando:

    x2eax2dx=π2a3/2\int_{-\infty}^{\infty} x^2 e^{-a x^2} dx = \frac{\sqrt{\pi}}{2a^{3/2}}

    obtemos:

    V=mω24α\langle V \rangle = \frac{m \omega^2}{4\alpha}

    Valor esperado da energia total

    Eapprox(α)=2α2m+mω24α\therefore E_\text{approx}(\alpha) = \frac{\hbar^2 \alpha}{2m} + \frac{m \omega^2}{4\alpha}

(c) Utilize o princípio variacional para determinar o α\alpha o ótimo, minimizando Eapprox(α)E_\text{approx}(\alpha).

  • Otimizar o conjunto de pontos ( α\alpha ) para o mínimo de energia

    Diferenciar

    ddα(2α2m+mω24α)=0\frac{d}{d\alpha} \left( \frac{\hbar^2 \alpha}{2m} + \frac{m \omega^2}{4\alpha} \right) = 0

    Resolver:

    22mmω24α2=0\frac{\hbar^2}{2m} - \frac{m \omega^2}{4\alpha^2} = 0

    αopt=mω2\alpha_\text{opt} = \frac{m\omega}{2\hbar}

    Substituindo αopt\alpha_\text{opt} em EapproxE_\text{approx} :

    Eapprox=ω2\therefore E_\text{approx} = \frac{\hbar \omega}{2}

    que corresponde à energia exata do estado fundamental do oscilador harmônico quântico.

VQE (Solucionador de Eigenvalores Quânticos Variacionais)

O eigensolver quântico variacional (VQE) é o principal método que usaremos para explorar o processo H+H=H2H+H = H_2 e, aqui, daremos uma olhada no que é o VQE e como ele funciona. Mas primeiro vamos fazer uma pausa e analisar um aspecto muito importante por meio da pergunta de check-in.

Verifique sua compreensão

Se já temos tantas estratégias para problemas de química, então por que precisamos de um computador quântico? E qual é o propósito de usar computadores quânticos e clássicos juntos?

  • A computação quântica tem a chance de revolucionar a química ao lidar com problemas que os computadores clássicos enfrentam devido à escala exponencial dos estados quânticos. Richard Feynman observou que, para simular a natureza, os cálculos também devem ser quânticos [ref. 1].

    Por exemplo, a simulação de cafeína com o conjunto de base mais simples ( STO-3G ) exigiria 104810^{48} bits, muito maior do que o número total de estrelas no universo observável ( 102410^{24} ) [ref 2]. Um computador quântico pode descrever os orbitais eletrônicos da cafeína com 160 qubits.

    Os computadores quânticos processam naturalmente as interações quânticas usando superposição e emaranhamento, o que proporciona uma maneira promissora de permitir simulações moleculares precisas. Além disso, podemos combinar as vantagens dos computadores quânticos (simulação de elétrons) e dos computadores clássicos (pré/pós-processamento de dados, gerenciamento de processos de algoritmos, otimização e assim por diante). Espera-se que eles aprimorem a descoberta de materiais, o design de medicamentos e as previsões de reações, reduzindo os dispendiosos experimentos de tentativa e erro. [ref 3][ref 4]

    Se você quiser saber por que os computadores quânticos são necessários para problemas de química e por que usar recursos de computação quântica e clássica, confira os artigos a seguir:

Agora vamos voltar ao VQE.

O VQE combina o poder dos computadores quânticos com os computadores clássicos, usando fundamentalmente princípios variacionais para obter a energia do estado fundamental do sistema. Para entender o VQE, primeiro divida-o em três partes:

Fluxo de trabalho VQE

Observável (quântico): O Hamiltoniano molecular (energia de uma molécula)

Na VQE, o Hamiltoniano molecular/atômico é um observável, o que significa que podemos medir seu valor por meio de um experimento. Nosso objetivo é encontrar a menor energia possível (a energia do estado fundamental) da molécula. Para isso, usamos um estado quântico experimental, gerado por um circuito quântico parametrizado (ansatz). Medimos o observável e otimizamos o estado quântico até atingirmos a menor energia possível.

O conjunto de bases usado para o Hamiltoniano molecular determina o número de qubits necessários e afeta diretamente a precisão do VQE. A escolha do conjunto de bases correto é fundamental para equilibrar a eficiência e a precisão. Para simplificar os cálculos sem alterar o conjunto de bases, podemos usar estratégias como a imposição de simetria e a redução do espaço ativo. Muitas moléculas têm formas simétricas (como uma borboleta ou um floco de neve), o que significa que algumas partes se comportam da mesma maneira. Em vez de calcular tudo separadamente, podemos nos concentrar apenas em partes exclusivas, economizando recursos quânticos e, assim, aproveitando a simetria. Na redução do espaço ativo, consideramos apenas os orbitais importantes, pois nem todos os elétrons afetam significativamente a energia molecular. Os elétrons próximos ao núcleo permanecem praticamente inalterados, enquanto outros influenciam a ligação. Ao aplicar esses métodos, podemos tornar o VQE mais eficiente e manter a precisão.

Depois de obtermos um Hamiltoniano molecular usando o conjunto de bases adequado e as estratégias acima, precisamos transformar esse Hamiltoniano em um adequado para computadores quânticos. O mapeamento de problemas para operadores de Pauli pode ser bastante complicado. Isso é especialmente verdadeiro na química quântica, que trabalha com partículas indistinguíveis (elétrons), já que os qubits são distinguíveis. Não entraremos em detalhes sobre os mapeamentos aqui, mas recomendamos que você consulte os seguintes recursos. Uma discussão geral sobre o mapeamento de um problema para operadores quânticos pode ser encontrada em Quantum computing in practice (Computação quântica na prática ). Uma discussão mais detalhada sobre o mapeamento de problemas de química em operadores quânticos pode ser encontrada em Química quântica com VQE.

Para este módulo, forneceremos os Hamiltonianos apropriados (um qubit) para HH e H2H_2 para que possamos nos concentrar no uso do computador quântico. Esses Hamiltonianos de um qubit são preparados usando o conjunto de bases STO-6G e o mapeamento de Jordan-Wigner, que é o mapeamento mais direto com a interpretação física mais simples, pois mapeia a ocupação de um orbital de spin para a ocupação de um qubit. Além disso, usamos uma técnica de redução de qubit por meio de uma simetria do Hamiltoniano, que usa os padrões de como as ocupações de spin se comportam para reduzir o número de qubits. Para a molécula H2H_2, assumimos que a distância entre os dois átomos de hidrogênio é 0.735 A˚\mathring A.

(Quantum) Ansatz: A função de onda experimental (Como construir um estado quântico trivial com um circuito quântico)

Para o VQE, a ansatz (plural: ansätze) consiste em dois componentes principais. A primeira é a preparação do estado inicial, que configura o estado do qubit aplicando portas quânticas sem parâmetro de variação. O segundo componente é o circuito quântico parametrizado, um circuito quântico especial com parâmetros ajustáveis, semelhante aos mostradores de um rádio. Esses parâmetros serão usados na última parte - o otimizador clássico - para nos ajudar a alcançar o melhor estado básico possível.

Na seção sobre o princípio variacional, aprendemos que a qualidade do estado de teste afeta a qualidade dos resultados do algoritmo variacional. Isso significa que a escolha de um bom ansatz é importante no VQE. Mais uma vez, esse é um tópico rico e complexo. Não abordaremos aqui os diferentes tipos de ansatz ou suas origens. Se estiver interessado em saber mais sobre circuitos quânticos parametrizados e ansatz, você pode explorar a lição Ansatz e forma variacional do curso Projeto de algoritmo variacional, que fornece explicações detalhadas e exemplos de ansätze.

Como vamos usar um Hamiltoniano de um qubit neste módulo, precisamos de um circuito quântico parametrizado de um qubit como um ansatz. Veremos três tipos de ansätze de um qubit na seção a seguir. Vamos compará-los e discutir as principais considerações ao selecionar um ansatz.

Otimizador (clássico): ajuste fino do circuito quântico

Quando o computador quântico mede a energia do observável a partir do ansatz, os parâmetros do ansatz e o valor da energia são enviados ao otimizador clássico para ajuste. Esse processo de otimização é realizado em um computador clássico, normalmente usando pacotes científicos de uso geral, como o SciPy.

O otimizador clássico trata a energia medida como uma função de custo. Em problemas de otimização, uma função de custo (às vezes também chamada de função objetiva) é uma função matemática que mede a "qualidade" de uma determinada solução. O objetivo do otimizador é encontrar o conjunto de parâmetros que minimiza essa função de custo. No contexto da descoberta da energia do estado fundamental de uma molécula, a própria energia serve como função de custo - queremos encontrar os parâmetros do nosso circuito quântico (nossa "solução") que produzam a menor energia possível. O otimizador clássico usa esse valor de energia medido (o custo) e determina o próximo conjunto de parâmetros otimizados para o ansatz quântico. Esses parâmetros atualizados são então enviados de volta ao circuito quântico e o processo é repetido. A cada iteração, o otimizador clássico ajusta os parâmetros para tentar reduzir a energia (minimizar a função de custo) até que um critério de convergência predefinido seja atendido, garantindo idealmente que a energia mais baixa possível (correspondente ao estado fundamental da molécula para essa distância de ligação e conjunto de bases) seja encontrada.

Há muitas estratégias de otimização fornecidas por pacotes científicos como SciPy. Você pode obter mais informações na lição Loops de otimização do curso Projeto de algoritmo variacional. Aqui usaremos o COBYLA (Constrained Optimization BY Linear Approximations), um algoritmo de otimização adequado para cenários de energia complicados. Em particular, a COBYLA não tenta calcular um gradiente da função que está sendo estudada; isso é chamado de otimizador sem gradiente. Imagine que você está tentando encontrar o pico mais alto de uma cadeia de montanhas com os olhos fechados. Como não é possível ver toda a paisagem, você dá pequenos passos em diferentes direções, enquanto verifica se está subindo ou descendo. O COBYLA funciona de maneira semelhante: ele percorre o espaço de parâmetros, testando valores diferentes, melhorando gradualmente o resultado até encontrar o melhor.

Agora você está pronto para realizar um cálculo de VQE. Para isso, experimente a pergunta de check-in abaixo, que resume o processo geral.

Verifique sua compreensão

Preencha os espaços em branco com os termos corretos para completar o resumo do processo de VQE e, em seguida, clique para verificar suas respostas.

VQE is a variational quantum algorithm, which combines the power of (1) ________ and classical computing, used to find the (2) __________ of a molecule. O processo começa definindo o (3) _____________, que representa a energia total do sistema e atua como observável em medições quânticas. Em seguida, preparamos um (4) ____________, um circuito quântico com parâmetros ajustáveis que representa a função de onda experimental da molécula. Esses parâmetros são otimizados usando um (5) ____________, um algoritmo clássico que ajusta os parâmetros iterativamente para minimizar a energia medida. Na discussão acima, usamos o otimizador (6) ____________, que refina os parâmetros ansatz sem precisar de cálculos derivados. O processo continua até chegarmos a (7) ____________, o que significa que encontramos a menor energia possível da molécula.

Banco de palavras:

  • otimizador clássico
  • energia do estado fundamental
  • eficiente em termos de hardware
  • ansatz
  • hamiltoniano molecular
  • COBYLA
  • computação quântica
  • convergência
  • 1 → computação quântica

    2 → energia do estado fundamental

    3 → Hamiltoniano molecular

    4 → ansatz

    5 → otimizador clássico

    6 → COBYLA

    7 → convergência


Calcule a energia do estado fundamental de um átomo de hidrogênio com VQE

Agora, vamos usar o que aprendemos para calcular a energia do estado fundamental de um átomo de hidrogênio. Ao longo do módulo, usaremos uma estrutura para computação quântica conhecida como "padrões Qiskit", que divide os fluxos de trabalho nas seguintes etapas:

  • Etapa 1: mapear entradas clássicas para um problema quântico
  • Etapa 2: otimizar o problema para a execução quântica
  • Etapa 3: Executar usando as primitivas do site Qiskit Runtime
  • Etapa 4: Pós-processamento e análise clássica
Padrão Qiskit

Em geral, seguiremos estas etapas.

Vamos começar carregando alguns pacotes necessários, incluindo as primitivas do Qiskit Runtime. Também selecionaremos o computador quântico menos ocupado disponível para nós.

Há um código abaixo para salvar suas credenciais na primeira utilização. Certifique-se de excluir essas informações do notebook depois de salvá-lo em seu ambiente, para que suas credenciais não sejam compartilhadas acidentalmente quando você compartilhar o notebook. Consulte Configurar sua conta IBM Cloud e Inicializar o serviço em um ambiente não confiável para obter mais orientações.

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

# Load the Runtime primitive and session
from qiskit_ibm_runtime import EstimatorV2 as Estimator

# Syntax for first saving your token.  Delete these lines after saving your credentials.
# QiskitRuntimeService.save_account(channel='ibm_quantum_platform',
# instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR-API_KEY>', overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum_platform')

# Load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)

Output:

ibm_brisbane

A célula abaixo permitirá que você alterne entre usar o simulador ou o hardware real em todo o notebook. Recomendamos executá-lo agora:

# Load the Aer simulator and generate a noise model based on the currently-selected backend.
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel

# Alternatively, load a fake backend with generic properties and define a simulator.


noise_model = NoiseModel.from_backend(backend)

# Define a simulator using Aer, and use it in Sampler.
backend_sim = AerSimulator(noise_model=noise_model)

Passo 1: Mapear o problema para circuitos e operadores quânticos

Começamos nosso cálculo do VQE definindo o hamiltoniano da molécula de hidrogênio ( H2H_2 ) para uma distância de ligação específica. Este hamiltoniano representa a energia total do sistema em termos de operadores de qubits, tendo sido derivado e mapeado a partir do sistema molecular por meio de um procedimento padrão: 1) utilizando o conjunto de bases de STO-6G (um conjunto específico de funções matemáticas usado para aproximar os orbitais eletrônicos), 2) aplicando o mapeamento de Jordan-Wigner (uma técnica para traduzir operadores fermiónicos que descrevem elétrons em operadores de qubits), e 3) realizando a redução de qubits utilizando a paridade do hamiltoniano para simplificar o problema.

Como explicamos anteriormente, as energias do estado fundamental computadas dependem muito da seleção do conjunto de bases e da geometria molecular (como a distância da ligação). Para essa configuração específica e após essas transformações, o qubit Hamiltoniano resultante é simples:

H^=0.2355I+0.2355Z\hat{H} = -0.2355 I + 0.2355 Z

Aqui, II representa o operador de identidade e ZZ representa o operador Pauli-Z, atuando em um único qubit. Os coeficientes são derivados das integrais calculadas usando o conjunto de bases STO-6G nessa distância de ligação específica com a transformação adequada.

Com esse hamiltoniano definido, agora podemos usar o VQE para calcular a energia do estado fundamental. É útil comparar nossa energia de estado fundamental calculada com os valores esperados. Para um único átomo de hidrogênio isolado (H), a energia do estado fundamental é exatamente -0.5 Hartree (na ausência de efeitos relativísticos). Vamos calcular a energia exata do estado fundamental do nosso qubit Hamiltoniano específico, conforme definido acima, e compará-la com os valores conhecidos relevantes.

from qiskit.quantum_info import SparsePauliOp
import numpy as np

# Qubit Hamiltonian of the hydrogen atom generated by using STO-3G basis set and parity mapping
Hamiltonian = SparsePauliOp.from_list([("I", -0.2355), ("Z", 0.2355)])

# exact ground state energy of Hamiltonian

A = np.array(Hamiltonian)
eigenvalues, eigenvectors = np.linalg.eig(A)
print(
    "The exact ground state energy of the Hamiltonian is ",
    min(eigenvalues).real,
    "hartree",
)
h = min(eigenvalues.real)

Output:

The exact ground state energy of the Hamiltonian is  -0.471 hartree

Em seguida, precisamos de um circuito quântico parametrizado, um ansatz, para preparar uma função de onda experimental Ψtrial\Psi_\text{trial} para o estado fundamental. O objetivo é encontrar os parâmetros θ\theta que minimizem o valor de expectativa de energia ψ(θ)H^ψ(θ)\langle\psi(\theta)|\hat{H}|\psi(\theta)\rangle. A escolha do ansatz é crucial porque determina o conjunto de possíveis estados quânticos que nosso circuito pode preparar. Um "bom" ansatz é aquele que é flexível o suficiente para representar um estado muito próximo do verdadeiro estado fundamental do Hamiltoniano que estamos estudando, mas não tão complexo que exija muitos parâmetros ou um circuito muito profundo para os computadores quânticos atuais.

Aqui, tentaremos três diferentes respostas de um qubit para ver qual delas oferece melhor "cobertura" dos possíveis estados quânticos em que um único qubit pode estar. A "cobertura" refere-se ao intervalo de estados quânticos que o circuito ansatz pode produzir ao variar seus parâmetros.

Usaremos três respostas baseadas em diferentes combinações de portas rotacionais de um único qubit:

  • Ansatz de porta rotacional de 1 eixo: Esse ansatz usa rotações em torno de apenas um eixo ( Rx(θ)R_x(\theta) ). Na esfera de Bloch, isso corresponde a mover-se apenas ao longo de um círculo específico. Esse é o menos flexível e abrange um conjunto limitado de estados.
  • Duas ansätze de porta rotacional de 2 eixos: Essas ansätze combinam rotações em torno de dois eixos diferentes ( Rx(θ1)Rz(θ2)R_x(\theta_1) R_z(\theta_2) e Rx(θ1)Rz(θ2)Rx(θ3)R_x(\theta_1) R_z(\theta_2) R_x(\theta_3) ). Isso nos permite alcançar uma parte maior da esfera de Bloch, em comparação com uma rotação de eixo único.

Comparando os resultados de VQE obtidos com essas três ansätze, podemos ver como a flexibilidade e a cobertura do espaço de estado da ansatz afetam nossa capacidade de encontrar a verdadeira energia do estado fundamental de nosso Hamiltoniano simplificado. Um ansatz mais flexível tem o potencial de encontrar uma aproximação melhor, mas também pode ser mais difícil para o otimizador clássico.

from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from qiskit.quantum_info import Statevector, DensityMatrix, Pauli

theta = Parameter("θ")
phi = Parameter("φ")
lam = Parameter("λ")

ansatz1 = QuantumCircuit(1)
ansatz1.rx(theta, 0)

ansatz2 = QuantumCircuit(1)
ansatz2.rx(theta, 0)
ansatz2.rz(phi, 0)

ansatz3 = QuantumCircuit(1)
ansatz3.rx(theta, 0)
ansatz3.rz(phi, 0)
ansatz3.rx(lam, 0)

Output:

<qiskit.circuit.instructionset.InstructionSet at 0x1059def80>

Agora, vamos gerar 5.000 números aleatórios para cada parâmetro e traçar a distribuição de estados quânticos aleatórios, gerados pelas três ansätze com esses parâmetros aleatórios. Você pode pensar nesses parâmetros como rotações em torno de diferentes eixos em uma superfície esférica. Para ver a distribuição do estado quântico, usaremos a esfera de Bloch, uma esfera tridimensional que mostra o estado de um único qubit. Qualquer ponto da esfera representa um estado possível do qubit, em que os polos norte e sul são como os clássicos "0" e "1", mas o qubit também pode estar em qualquer lugar entre eles, mostrando propriedades quânticas especiais como a superposição. Primeiro, prepare as funções necessárias para traçar a esfera de 3D Bloch e prepare 5000 parâmetros aleatórios.

import matplotlib.pyplot as plt


def plot_bloch(bloch_vectors):
    # Extract X, Y, Z coordinates for 3D projection
    X_coords = bloch_vectors[:, 0]
    Z_coords = bloch_vectors[:, 2]

    # Compute Y coordinates from X and Z to approximate the full Bloch sphere projection
    Y_coords = bloch_vectors[:, 1]

    # Create 3D plot
    fig = plt.figure(figsize=(8, 8))
    ax = fig.add_subplot(111, projection="3d")
    ax.scatter(X_coords, Y_coords, Z_coords, color="blue", alpha=0.6)

    # Labels and title
    ax.set_xlabel("X")
    ax.set_ylabel("Y")
    ax.set_zlabel("Z")
    ax.set_title("Parameterized 1-Qubit Circuit on 3D Bloch Sphere")

    # Set axis limits and make them equal
    ax.set_xlim([-1, 1])
    ax.set_ylim([-1, 1])
    ax.set_zlim([-1, 1])

    # Ensure equal aspect ratio for all axes
    ax.set_box_aspect([1, 1, 1])  # Equal scaling for x, y, z axes

    # Show grid
    ax.grid(True)

    plt.show()


num_samples = 5000  # Number of random states
theta_vals = np.random.uniform(0, 2 * np.pi, num_samples)
phi_vals = np.random.uniform(0, 2 * np.pi, num_samples)
lam_vals = np.random.uniform(0, 2 * np.pi, num_samples)

Vejamos como funciona nosso primeiro ansatz.

# List to store Bloch Sphere XZ coordinates
bloch_vectors = []

# Generate quantum states and extract Bloch vectors
for i in range(num_samples):
    # Create a circuit and bind parameters
    qc = ansatz1
    bound_qc = qc.assign_parameters({theta: theta_vals[i]})  # , lam: lam_vals[i]})
    state = Statevector.from_instruction(bound_qc)
    rho = DensityMatrix(state)

    X = rho.expectation_value(Pauli("X")).real
    Y = rho.expectation_value(Pauli("Y")).real
    Z = rho.expectation_value(Pauli("Z")).real
    bloch_vectors.append([X, Y, Z])  # Store X, Z components

# Convert to a numpy array for plotting
bloch_vectors = np.array(bloch_vectors)

plot_bloch(bloch_vectors)

Output:

Output of the previous code cell

Podemos ver que nossa primeira ansatz retorna um estado quântico distribuído em forma de anel da esfera de Bloch. Isso faz sentido, pois fornecemos ao ansatz apenas um único parâmetro rotacional. Portanto, ele só pode produzir estados girados em torno de um eixo. Partindo do ponto (0,0,1)(0,0,1) e girando em torno de um eixo, sempre haverá um anel. Em seguida, vamos verificar nossa segunda ansatz, que tem duas portas rotacionais ortogonais - Rx e Rz.

bloch_vectors = []

# Generate quantum states and extract Bloch vectors
for i in range(num_samples):
    # Create circuit and bind parameters
    qc = ansatz2
    bound_qc = qc.assign_parameters(
        {theta: theta_vals[i], phi: phi_vals[i]}
    )  # , lam: lam_vals[i]})
    state = Statevector.from_instruction(bound_qc)
    rho = DensityMatrix(state)

    X = rho.expectation_value(Pauli("X")).real
    Y = rho.expectation_value(Pauli("Y")).real
    Z = rho.expectation_value(Pauli("Z")).real
    bloch_vectors.append([X, Y, Z])  # Store X, Z components

# Convert to numpy array for plotting
bloch_vectors = np.array(bloch_vectors)

plot_bloch(bloch_vectors)

Output:

Output of the previous code cell

Aqui, podemos ver que nossa segunda ansatz cobre uma porção maior da esfera de Bloch - mas observe que os pontos estão mais concentrados em torno dos polos e mais espalhados em torno do equador. Agora é hora de verificar nosso último ansatz.

bloch_vectors = []

# Generate quantum states and extract Bloch vectors
for i in range(num_samples):
    # Create circuit and bind parameters
    qc = ansatz3
    bound_qc = qc.assign_parameters(
        {theta: theta_vals[i], phi: phi_vals[i], lam: lam_vals[i]}
    )
    state = Statevector.from_instruction(bound_qc)
    rho = DensityMatrix(state)

    X = rho.expectation_value(Pauli("X")).real
    Y = rho.expectation_value(Pauli("Y")).real
    Z = rho.expectation_value(Pauli("Z")).real
    bloch_vectors.append([X, Y, Z])  # Store X, Z components

# Convert to numpy array for plotting
bloch_vectors = np.array(bloch_vectors)

plot_bloch(bloch_vectors)

Output:

Output of the previous code cell

Aqui você pode ver estados quânticos distribuídos de forma mais uniforme gerados por nosso último ansatz.

Conforme mencionado, a melhor coisa a fazer é obter conhecimento sobre o estado fundamental que você está buscando e usar um ansatz que seja adequado para sondar estados próximos a esse estado fundamental. Por exemplo, se soubéssemos que nosso estado fundamental estava próximo a um polo, poderíamos selecionar a ansatz 2. Para simplificar, vamos nos ater ao ansatz 3, que examina uniformemente toda a esfera de Bloch.

Agora que selecionamos nosso ansatz, vamos desenhar o circuito.

# Pre-defined ansatz circuit and operator class for Hamiltonian

ansatz = ansatz3

num_params = ansatz.num_parameters
print("This circuit has ", num_params, "parameters")

ansatz.draw("mpl", style="iqp")

Output:

This circuit has  3 parameters
Output of the previous code cell

Etapa 2: Otimizar para o hardware de destino

Ao executar um cálculo em um computador quântico real, não nos preocupamos apenas com a lógica do circuito quântico. Também nos preocupamos com coisas como quais operações podem ser realizadas por esse computador quântico específico e em que parte do computador quântico estão os qubits que estamos usando. Eles estão bem próximos um do outro? Eles estão muito distantes? Portanto, a próxima etapa é reescrever nosso circuito usando portas que sejam naturais para o computador quântico que usaremos e levando em conta o layout do qubit. Isso pode ser feito pelo site transpilation - após esse processo, você poderá ver nosso ansatz simples convertido em um conjunto diferente de portas, e nossos qubits abstratos serão mapeados em qubits físicos em um computador quântico real.

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

config = backend.configuration()

print("Backend: {config.backend_name}")
print("Native gates: ", config.supported_instructions, ",")


target = backend.target

pm = generate_preset_pass_manager(target=target, optimization_level=3)

ansatz_isa = pm.run(ansatz)

ansatz_isa.draw(output="mpl", idle_wires=False, style="iqp")

Output:

Backend: {config.backend_name}
Native gates:  ['ecr', 'id', 'delay', 'measure', 'reset', 'rz', 'sx', 'x'] ,
Output of the previous code cell

Você pode ver que os portões rx, rz de nosso ansatz foram convertidos em uma série de portões rz, sx , que são os portões nativos de nosso backend. Além disso, você pode ver que nosso q0 agora está mapeado no quinto qubit físico. Também precisamos mapear nosso Hamiltoniano de acordo com essas alterações, como no código a seguir:

Hamiltonian_isa = Hamiltonian.apply_layout(layout=ansatz_isa.layout)

Etapa 3: Executar no hardware de destino

Agora é hora de executar nosso VQE em uma QPU real. Para isso, primeiro precisamos de uma função de custo para o processo de otimização, que avalia o valor de expectativa do Hamiltoniano com um estado quântico, gerado pelo ansatz. Não se preocupe! Você não precisa codificar tudo sozinho. Preparamos uma função para isso, e tudo o que você precisa fazer é executar a célula abaixo.

def cost_func(params, ansatz, hamiltonian, estimator):
    """Return estimate of energy from estimator

    Parameters:
        params (ndarray): Array of ansatz parameters
        ansatz (QuantumCircuit): Parameterized ansatz circuit
        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
        estimator (EstimatorV2): Estimator primitive instance
        cost_history_dict: Dictionary for storing intermediate results

    Returns:
        float: Energy estimate
    """
    pub = (ansatz, [hamiltonian], [params])
    result = estimator.run(pubs=[pub]).result()
    energy = result[0].data.evs[0]

    cost_history_dict["iters"] += 1
    cost_history_dict["prev_vector"] = params
    cost_history_dict["cost_history"].append(energy)
    print(f"Iters. done: {cost_history_dict['iters']} [Current cost: {energy}]")

    return energy

Por fim, preparamos os parâmetros iniciais para nosso ansatz e seu processo de otimização. Você pode simplesmente usar todos os zeros ou valores aleatórios. Selecionamos os parâmetros iniciais abaixo, mas sinta-se à vontade para comentar ou descomentar as linhas na célula para obter amostras de parâmetros de forma aleatória, uniformemente de 0 a 2π2\pi.

# x0 = np.random.uniform(0, 2*pi, 3)
x0 = [1, 1, 0]
# QPU Est. 2min for ibm_brisbane

from scipy.optimize import minimize
from qiskit_ibm_runtime import Batch

batch = Batch(backend=backend)

cost_history_dict = {
    "prev_vector": None,
    "iters": 0,
    "cost_history": [],
}
estimator = Estimator(mode=batch)
estimator.options.default_shots = 10000

res = minimize(
    cost_func,
    x0,
    args=(ansatz_isa, Hamiltonian_isa, estimator),
    method="cobyla",
    options={"maxiter": 10, "tol": 0.01},
)

batch.close()

Output:

Iters. done: 1 [Current cost: -0.3361517318448143]
Iters. done: 2 [Current cost: -0.4682546422099432]
Iters. done: 3 [Current cost: -0.38985802144149584]
Iters. done: 4 [Current cost: -0.38319217316749354]
Iters. done: 5 [Current cost: -0.4628720756579032]
Iters. done: 6 [Current cost: -0.4683301936226905]
Iters. done: 7 [Current cost: -0.45480498699294747]
Iters. done: 8 [Current cost: -0.4690533242050814]
Iters. done: 9 [Current cost: -0.465867415110354]
Iters. done: 10 [Current cost: -0.4606882723137227]
h_vqe = res.fun
print("The reference ground state energy is ", min(eigenvalues))
print("The computed ground state energy is ", h_vqe)

Output:

The reference ground state energy is  (-0.471+0j)
The computed ground state energy is  -0.4690533242050814

Parabéns! Você acabou de concluir com sucesso seu primeiro experimento de química quântica. Podemos ver uma diferença entre a energia exata do estado fundamental do Hamiltoniano e a nossa, mas como usamos uma técnica padrão de atenuação de erros (que corrige erros de leitura), a diferença é pequena. Esse é um ótimo começo!

Observação: é possível obter um resultado melhor definindo um nível de mitigação de erros usando resilience_level. O valor padrão é 1; se você definir um valor maior, o programa consumirá mais tempo da QPU, mas poderá apresentar um resultado melhor.

Etapa 4: Pós-processamento

É hora de dar uma olhada em como o nosso otimizador clássico funcionava. Execute a célula abaixo e veja o padrão de convergência.

fig, ax = plt.subplots()
x = np.linspace(0, 10, 10)

# Define the constant function
y_constant = np.full_like(x, h)
ax.plot(
    range(cost_history_dict["iters"]), cost_history_dict["cost_history"], label="VQE"
)
ax.set_xlabel("Iterations")
ax.set_ylabel("Cost (Hartree)")
ax.plot(y_constant, label="Target")
plt.legend()
plt.draw()

Output:

Output of the previous code cell

Começamos com um valor inicial razoavelmente bom, de modo que obtivemos um bom valor final em apenas 10 etapas. Você pode ver picos grandes e pequenos, e esse é o recurso típico do otimizador COBYLA - ele pesquisa o espaço como se não pudesse ver a paisagem e ajusta os tamanhos das etapas a cada medição.

Verifique sua compreensão

Qual é a sua observação? Qual parte do processo acima pode ser aprimorada para obter resultados mais próximos dos valores teóricos ou mais próximos da energia precisa do estado fundamental do Hamiltoniano? Quais são alguns aspectos a serem considerados para isso?

  • O primeiro aspecto a ser considerado é a mudança no conjunto de bases usadas no cálculo do Hamiltoniano das moléculas. Conforme mencionado anteriormente, a energia do estado fundamental do átomo de H é -0.5 Hartree, como é bem conhecido, e a base STO-6G que escolhemos não é suficiente para derivar esse valor com precisão.

    A escolha de um tipo mais complexo de base aumenta o número de qubits usados pelo Hamiltoniano; portanto, precisamos selecionar um ansatz mais complexo e adequado para problemas químicos.

    O próximo item a ser otimizado é o gerenciamento de ruído na QPU. Técnicas mais avançadas de atenuação de erros produzem melhores resultados, mas podem levar mais tempo para serem usadas. Além disso, considere como o site shot_number afeta os resultados.

    Por fim, também é possível obter um melhor desempenho de convergência experimentando diferentes otimizadores.


Calcule a energia do estado fundamental da molécula de hidrogênio com VQE

Agora que examinamos o processo geral de VQE usando os átomos HH, calcularemos mais rapidamente a energia do estado fundamental da molécula H2H_2.

Passo 1: Mapear o problema para circuitos e operadores quânticos

Aqui também fornecemos um Hamiltoniano de um qubit que usa a base STO-6G e a transformação de Jordan-Wigner, com redução de qubit usando uma simetria do Hamiltoniano. Observe que usamos uma distância atômica entre dois átomos de hidrogênio de 0.735 A˚\mathring A.

Diferentemente do cálculo de um único átomo de hidrogênio ( HH ), para calcular o estado fundamental de uma molécula de hidrogênio ( H2H_2 ), devemos considerar também a força repulsiva que atua entre os núcleos dos dois átomos de hidrogênio, além da energia associada aos orbitais eletrônicos. Nesta etapa, forneceremos esse valor como uma constante e, na verdade, calcularemos esse valor no problema de check-in. H^=1.04886I+0.79674Z+0.18122X\hat{H} = -1.04886 I + -0.79674 Z + 0.18122 X

h2_hamiltonian = SparsePauliOp.from_list(
    [("I", -1.04886087), ("Z", -0.7967368), ("X", 0.18121804)]
)

# exact ground state energy of hamiltonian
nuclear_repulsion = 0.71997
A = np.array(h2_hamiltonian)
eigenvalues, eigenvectors = np.linalg.eig(A)
print("Electronic ground state energy (Hartree): ", min(eigenvalues).real)
print("Nuclear repulsion energy (Hartree): ", nuclear_repulsion)
print(
    "Total ground state energy (Hartree): ", min(eigenvalues).real + nuclear_repulsion
)
h2 = min(eigenvalues).real + nuclear_repulsion

Output:

Electronic ground state energy (Hartree):  -1.8659468547627318
Nuclear repulsion energy (Hartree):  0.71997
Total ground state energy (Hartree):  -1.1459768547627318

Etapa 2: Otimizar para o hardware de destino

Como o número de qubits usados pelo VQE e Hamiltoniano anteriores é o mesmo que o backend a ser usado para execução, usaremos o ansatz existente e sua forma otimizada.

h2_hamiltonian_isa = h2_hamiltonian.apply_layout(layout=ansatz_isa.layout)

Etapa 3: Executar no hardware de destino

Agora é hora de fazer os cálculos sobre a QPU real. Quase tudo é igual, mas usaremos o ponto inicial apropriado para ajustar o Hamiltoniano. Além disso, em uma parte iterativa, algumas das configurações do Estimator, que é usado para calcular as expectativas do Hamiltoniano para o ansatz no QPU, serão definidas de forma ligeiramente diferente dos cálculos anteriores. Discutiremos mais sobre essa mudança em uma pergunta de check-in.

x0 = [2, 0, 0]
# QPU time 4min for ibm_brisbane
batch = Batch(backend=backend)

cost_history_dict = {
    "prev_vector": None,
    "iters": 0,
    "cost_history": [],
}
estimator = Estimator(mode=batch)
estimator.options.default_shots = 10000

res = minimize(
    cost_func,
    x0,
    args=(ansatz_isa, h2_hamiltonian_isa, estimator),
    method="cobyla",
    options={"maxiter": 15},
)

batch.close()

Output:

Iters. done: 1 [Current cost: -0.710621837568328]
Iters. done: 2 [Current cost: -0.2603208441168329]
Iters. done: 3 [Current cost: -0.25548711201326424]
Iters. done: 4 [Current cost: -0.581129450619904]
Iters. done: 5 [Current cost: -1.722920997605439]
Iters. done: 6 [Current cost: -1.6633324849371915]
Iters. done: 7 [Current cost: -1.8066989598929164]
Iters. done: 8 [Current cost: -1.8051093803839542]
Iters. done: 9 [Current cost: -1.802692217571555]
Iters. done: 10 [Current cost: -1.8233585485263144]
Iters. done: 11 [Current cost: -1.6904116652617205]
Iters. done: 12 [Current cost: -1.8245120321245392]
Iters. done: 13 [Current cost: -1.6837021361383608]
Iters. done: 14 [Current cost: -1.8166632606115467]
Iters. done: 15 [Current cost: -1.863446212658907]
h2_vqe = res.fun + nuclear_repulsion
print(
    "The reference ground state energy is ", min(eigenvalues).real + nuclear_repulsion
)
print("The computed ground state energy is ", h2_vqe)

Output:

The reference ground state energy is  -1.1459768547627318
The computed ground state energy is  -1.143476212658907

Apesar de o VQE fornecer teoricamente um limite superior para a verdadeira energia do estado fundamental, as implementações práticas em hardware quântico real ou simulado com ruído, bem como as aproximações feitas na preparação do Hamiltoniano (como conjuntos de base ou redução de qubit), podem introduzir erros que, às vezes, resultam em uma energia medida ligeiramente inferior ao valor teórico exato ou a uma referência numérica específica. Embora haja alguns erros, os resultados parecem ser satisfatórios, especialmente devido ao pequeno número de etapas. Agora, vamos concluir esse cálculo de VQE analisando como o otimizador funcionou.

Etapa 4: Pós-processamento

fig, ax = plt.subplots()
x = np.linspace(0, 5, 15)

# Define the constant function
y_constant = np.full_like(x, min(eigenvalues))
ax.plot(
    range(cost_history_dict["iters"]), cost_history_dict["cost_history"], label="VQE"
)
ax.set_xlabel("Iterations")
ax.set_ylabel("Cost (Hartree)")
ax.plot(y_constant, label="Target")
plt.legend()
plt.draw()

Output:

Output of the previous code cell

Verifique sua compreensão

Vamos calcular a energia de repulsão nuclear da molécula H2H_2, que incluímos como um valor constante ( 0.71997 Hartree).

H2 molécula

Use a lei de Coulomb e as unidades atômicas para garantir que você obtenha o valor em Hartree.

  • Como os dois núcleos de hidrogênio são carregados positivamente, eles se repelem devido à força eletrostática. Essa repulsão é descrita pela lei de Coulomb:

    Erepulsive=e24πϵ0RE_{repulsive} = \frac{e^2}{4\pi\epsilon_0R}

    em que ee é a carga do próton, ϵ0\epsilon_0 é a permissividade do vácuo e RR é a distância entre os dois núcleos, medida em metros ou raios de Bohr em unidade de joules (J).

    Para calcular essa energia em Hartrees, precisamos converter a equação acima no sistema de unidades atômicas (AU). Em AU, e2=1e^2 = 1, 4πϵ0=14\pi\epsilon_0=1 e o raio de Bohr ( a0a_0 ) é 1 e se torna a escala de comprimento fundamental em AU. Com essas simplificações, a lei de Coulomb se reduz a:

    Erepulsion=1RE_{repulsion} = \frac{1}{R}

    em que RR deve ser medido em raios Bohr ( a0a_0 ).

    Para converter a separação nuclear dada em A˚\r{A} em a0a_0, precisamos dessa relação de conversão:

    1A˚=1.88973a01\r{A} = 1.88973 a_0

    portanto, 0.735A˚0.735\r{A} se torna 0.7351.88973=1.38895a00.735 * 1.88973 = 1.38895 a_0.

    Portanto, a energia de repulsão nuclear de um determinado H2H_2 é

    Erepulsion=1R=11.38895=0.71997HartreeE_{repulsion} = \frac{1}{R} = \frac{1}{1.38895} = 0.71997 Hartree


Calcule a energia de reação de um H+H=H2H + H = H_2

Agora vamos usar o que obtivemos! Você usou o VQE, um eigensolver quântico variacional, para calcular a energia do estado fundamental do átomo HH e da molécula H2H_2. O que resta é usar os valores calculados para obter a energia de reação do processo H+H=H2H+H=H_2.

A energia de reação é a mudança de energia que ocorre quando as substâncias reagem para formar novas substâncias. Imagine que você está construindo algo: às vezes, é preciso colocar energia (como empilhar blocos) e, às vezes, a energia é liberada (como uma bola rolando ladeira abaixo). Na química, as reações absorvem energia (endotérmicas) ou liberam energia (exotérmicas).

A energia de reação do processo H+H=H2H+H = H_2 pode ser calculada pela seguinte fórmula:

Ereaction=EH2(EH+EH)E_{reaction} = E_{H_2} - (E_H + E_H)

Executando a célula abaixo, vamos ver isso visualmente. Aqui, usaremos o valor exato do estado fundamental de cada hamiltoniano e compararemos a energia de reação da solução exata e os resultados da VQE.

# Theoretical values
E_H_theo = h.real
E_H2_theo = h2

# Experimental values
E_H_exp = h_vqe
E_H2_exp = h2_vqe

# Calculate reaction energies
E_reaction_theo = E_H2_theo - (2 * E_H_theo)
E_reaction_exp = E_H2_exp - (2 * E_H_exp)

# Set up the plot
fig, ax = plt.subplots(figsize=(8, 6))
ax.set_xlim(0, 3)
ax.set_ylim(-1.16, -0.93)  # Adjust y-axis range to highlight differences
ax.set_xticks([])
ax.set_ylabel("Energy (Hartree)")
ax.set_title("H + H → H₂ Reaction Energy Diagram")

# Plot theoretical energy levels
ax.hlines(
    y=2 * E_H_theo, xmin=0.5, xmax=1.3, linewidth=2, color="r", label="2H (Exact)"
)
ax.hlines(y=E_H2_theo, xmin=1.3, xmax=2, linewidth=2, color="b", label="H₂ (Exact)")

# Plot experimental energy levels
ax.hlines(
    y=2 * E_H_exp,
    xmin=0.5,
    xmax=1.5,
    linewidth=2,
    color="r",
    linestyle="dashed",
    label="2H (VQE)",
)
ax.hlines(
    y=E_H2_exp,
    xmin=1.5,
    xmax=2.5,
    linewidth=2,
    color="b",
    linestyle="dashed",
    label="H₂ (VQE)",
)

# Add labels
ax.text(
    1,
    2 * E_H_theo,
    f"2H: {2*E_H_theo:.4f}",
    verticalalignment="top",
    horizontalalignment="left",
)
ax.text(
    2,
    E_H2_theo,
    f"H₂: {E_H2_theo:.4f}",
    verticalalignment="top",
    horizontalalignment="left",
)
ax.text(
    1,
    2 * E_H_exp,
    f"2H_VQE: {2*E_H_exp:.4f}",
    verticalalignment="bottom",
    horizontalalignment="right",
)
ax.text(
    2,
    E_H2_exp,
    f"H₂_VQE: {E_H2_exp:.4f}",
    verticalalignment="bottom",
    horizontalalignment="right",
)

# Add arrows for reaction energy with ΔE label in the middle
mid_y_theo = (2 * E_H_theo + E_H2_theo) / 2
mid_y_exp = (2 * E_H_exp + E_H2_exp) / 2
ax.annotate(
    "",
    xy=(1.3, E_H2_theo),
    xytext=(1.3, 2 * E_H_theo),
    arrowprops=dict(arrowstyle="<->", color="g"),
)
ax.text(
    1.35, mid_y_theo, f"ΔE: {E_reaction_theo:.4f}", color="g", verticalalignment="top"
)

ax.annotate(
    "",
    xy=(1.5, E_H2_exp),
    xytext=(1.5, 2 * E_H_exp),
    arrowprops=dict(arrowstyle="<->", color="g", linestyle="dashed"),
)
ax.text(
    1.55,
    mid_y_exp,
    f"ΔE_VQE: {E_reaction_exp:.4f}",
    color="g",
    verticalalignment="center",
)

# Add legend
ax.legend()

plt.show()

Output:

Output of the previous code cell

Conforme mostrado na figura, embora haja alguns erros, a energia exata do estado fundamental dos hamiltonianos e a energia da reação calculada usando os resultados do VQE são semelhantes, próximas a -0.2 Hartree.

Deve-se observar aqui que a energia da reação desse processo tem um valor negativo, o que significa que a energia é liberada por meio do processo, e a molécula resultante tem uma energia menor do que a de dois átomos individuais.

  1. Conclusão

Vamos resumir o que aprendemos até agora.

Primeiro, examinamos duas importantes técnicas de aproximação necessárias para resolver problemas de química quântica: o princípio variacional e as escolhas do conjunto de bases, ambos fundamentais para a VQE. Exploramos o princípio variacional manualmente, calculando a energia do estado fundamental do oscilador harmônico simples.

Em seguida, exploramos o VQE, um algoritmo amplamente utilizado para calcular a energia do estado fundamental de um sistema quântico. Executamos o código para calcular as energias do estado fundamental do hidrogênio atômico ( HH ) e da molécula de hidrogênio ( H2H_2 ). Em particular, aprendemos que é necessário obter o Hamiltoniano molecular apropriado para o sistema e transformá-lo em uma forma executável em um computador quântico. Também vimos que o ansatz, um circuito quântico parametrizado, é necessário para preparar estados quânticos de teste no VQE, e discutimos a importância de escolher uma estrutura de circuito ansatz adequada. Também aprendemos que a VQE se baseia em um processo de otimização iterativo usando um computador clássico, orientando o circuito quântico para encontrar o estado de energia mais baixo, e vimos como o processo converge.

Por fim, usamos as energias do estado fundamental computadas de HH e H2H_2 obtidas por meio do VQE para calcular a energia da reação para o processo H+HH2H + H \rightarrow H_2.

O VQE é um poderoso algoritmo quântico de curto prazo, mas é importante estar ciente de suas limitações. O desempenho do VQE depende muito da escolha do ansatz - encontrar um ansatz eficientemente preparável que possa representar com precisão o verdadeiro estado fundamental torna-se um desafio para moléculas maiores e mais complexas. Além disso, o hardware quântico atual é suscetível a ruídos, o que pode afetar a precisão dos resultados de VQE, especialmente em circuitos mais profundos ou em um número maior de qubits. Apesar desses desafios, o VQE serve como um algoritmo fundamental, e a pesquisa em andamento está explorando métodos variacionais mais sofisticados e técnicas de atenuação de erros para ampliar os limites do que é possível na química quântica em computadores quânticos de curto prazo. Por exemplo, estão sendo desenvolvidos algoritmos como o Sample-based Quantum Diagonalization (SQD), que aproveita amostras obtidas de circuitos quânticos combinados com a diagonalização clássica em um subespaço para melhorar a estimativa de energia e abordar algumas das limitações enfrentadas pelo VQE, especialmente em relação à eficiência da medição e à robustez do ruído.


Revisão e perguntas

Conceitos críticos:

  • O algoritmo quântico variacional é um paradigma de computação no qual um computador clássico e um computador quântico trabalham juntos para resolver um problema.
  • No VQE, começamos com um Hamiltoniano do nosso sistema e o mapeamos em qubits para execução no computador quântico. Selecionamos um circuito quântico parametrizado, um ansatz, e fazemos medições repetidas, variando os parâmetros do ansatz, até que o valor de energia mais baixo seja alcançado. A pesquisa no espaço de parâmetros é feita usando um otimizador clássico. Para obter bons resultados, é necessário selecionar um bom ansatz e um otimizador adequado.
  • A energia de reação é a mudança total de energia em uma reação química, determinada pela diferença entre a energia dos reagentes e a dos produtos.

True/false

  1. O princípio variacional afirma que o valor esperado da energia para qualquer função de onda experimental é sempre maior ou igual à verdadeira energia do estado fundamental.
  2. Um conjunto de bases é uma coleção de funções usadas para aproximar as funções de onda quântica.
  3. O VQE é um algoritmo quântico usado para resolver exatamente a equação de Schrödinger para um determinado Hamiltoniano.
  4. No VQE, um circuito quântico parametrizado (um ansatz) é usado para preparar funções de onda de teste.
  5. A escolha do otimizador no VQE (por exemplo, COBYLA, SPSA ou ADAM) não afeta a qualidade do resultado.
  6. O site Estimator da Qiskit é usado para calcular diretamente os valores de expectativa dos Hamiltonianos no VQE.

Questões de múltipla escolha:

  1. Qual é a finalidade do Hamiltoniano no VQE?
  • A) Para gerar estados quânticos aleatórios
  • B) Para determinar a energia dos estados quânticos
  • C) Para otimizar os circuitos quânticos
  • D) Para criar emaranhamento
  1. Qual é o principal objetivo do algoritmo VQE?
  • A) Para encontrar a energia do estado fundamental de um Hamiltoniano
  • B) Para criar emaranhamento entre qubits
  • C) Para realizar a pesquisa de Grover
  • D) Para quebrar a criptografia RSA
  1. Quantos estados quânticos são gerados nesse notebook para comparar o ansatz?
  • A) 100
  • B) 1000
  • C) 5000
  • D) 10,000
  1. Por que é necessário um otimizador clássico no VQE?
  • A) Para realizar medições quânticas
  • B) Atualizar os parâmetros do ansatz para minimizar a energia
  • C) Para emaranhar qubits
  • D) Para gerar aleatoriedade quântica
  1. Por que o ansatz foi projetado para ser parametrizado?
  • A) Para permitir a preparação do estado quântico
  • B) Para permitir a pesquisa de um amplo espaço de estados quânticos
  • C) Para reduzir a complexidade do circuito
  • D) Medir diretamente os valores próprios
  1. Qual das afirmações a seguir é a mais correta sobre a escolha de um bom ansatz?
  • A) Uma ansatz deve produzir estados distribuídos uniformemente sobre a esfera de Bloch, caso contrário, ela falhará.
  • B) Um ansatz deve ser adaptado ao seu sistema para garantir que ele possa gerar estados próximos ao estado fundamental.
  • C) Um ansatz deve produzir estados aleatórios usando seus parâmetros variacionais.
  • D) Um ansatz melhor sempre tem mais parâmetros de variação.

(Opcional) Apêndice: Sobrecarga do otimizador pela complexidade da abordagem

A VQE enfrenta vários desafios bem conhecidosref [6], e os seguintes estão relacionados ao que aprendemos acima.

  1. Desafios da seleção de Ansatz

Há um desafio inerente na seleção do ansatz variacional correto. As ansätze inspiradas na química (como a UCCSD) fornecem precisão física, mas exigem circuitos profundos, enquanto as ansätze eficientes em termos de hardware têm circuitos mais rasos, mas podem não ter interpretabilidade física. Além disso, muitas ansätze introduzem parâmetros variacionais excessivos que contribuem pouco para melhorar a precisão, mas aumentam significativamente a dificuldade de otimização.

  1. Dificuldades de otimização

O cenário de otimização do VQE pode ter regiões em que os gradientes desaparecem exponencialmente (platôs estéreis), o que dificulta a atualização eficiente dos parâmetros variacionais pelos otimizadores clássicos. Para isso, os pesquisadores tentaram usar diferentes tipos de otimizadores - baseados em gradiente e sem gradiente, mas ambos enfrentam desafios. Os otimizadores baseados em gradiente sofrem com platôs estéreis, enquanto os métodos sem gradiente exigem um grande número de avaliações de funções.

  1. Sobrecarga do otimizador

Um desafio mais conhecido é a sobrecarga do otimizador, que está relacionada à escala do problema. Os circuitos quânticos necessários para a VQE aumentam em profundidade e complexidade à medida que o tamanho do problema aumenta; isso normalmente também aumenta o número de parâmetros a serem otimizados. O processo de otimização torna-se intratável à medida que o número de parâmetros aumenta, levando a uma convergência lenta e a dificuldades para encontrar a solução ideal.

Aqui, daremos uma olhada nesses desafios usando a VQE para uma molécula H2H_2, com dois tipos diferentes de respostas.

(Observação: isso pode exigir mais tempo da QPU, portanto, sinta-se à vontade para usar um simulador para isso se não tiver tempo suficiente)

from qiskit.circuit import ParameterVector

num_iter = 4
alpha = ParameterVector("alpha", 3)
beta = ParameterVector("beta", 3 * num_iter)

# step1: Map problem to quantum circuits and operators
hamiltonian = SparsePauliOp.from_list(
    [("I", -1.04886087), ("Z", -0.7967368), ("X", 0.18121804)]
)

ansatz_1 = ansatz3
ansatz_2 = QuantumCircuit(1)
for i in range(num_iter):
    ansatz_2.rx(beta[i * 3 + 0], 0)
    ansatz_2.rz(beta[i * 3 + 1], 0)
    ansatz_2.rx(beta[i * 3 + 2], 0)
ansatz_1.draw("mpl")

Output:

Output of the previous code cell
ansatz_2.draw("mpl")

Output:

Output of the previous code cell
# Step 2: Optimize for target hardware

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)

ansatz_isa_1 = pm.run(ansatz_1)
ansatz_isa_2 = pm.run(ansatz_2)
hamiltonian_isa_1 = hamiltonian.apply_layout(layout=ansatz_isa_1.layout)
hamiltonian_isa_2 = hamiltonian.apply_layout(layout=ansatz_isa_2.layout)

Agora, vamos executar um VQE com um ponto inicial composto por todas as unidades, com um máximo de 20 etapas, e comparar a convergência de ambas as execuções.

# QPU time 3m 40s for ibm_brisbane
# Step 3: Execute on target hardware

from scipy.optimize import minimize

x0 = np.ones(ansatz_1.num_parameters)

batch = Batch(backend=backend)


cost_history_dict = {
    "prev_vector": None,
    "iters": 0,
    "cost_history": [],
}
estimator = Estimator(mode=batch)
estimator.options.default_shots = 2048

res = minimize(
    cost_func,
    x0,
    args=(ansatz_isa_1, hamiltonian_isa_1, estimator),
    method="cobyla",
    options={"maxiter": 20},
)

batch.close()

Output:

Iters. done: 1 [Current cost: -0.8782202668652658]
Iters. done: 2 [Current cost: -0.43473160695469165]
Iters. done: 3 [Current cost: -0.4076372093159749]
Iters. done: 4 [Current cost: -1.3587839859772106]
Iters. done: 5 [Current cost: -1.774529906754082]
Iters. done: 6 [Current cost: -1.541934983115727]
Iters. done: 7 [Current cost: -1.2732403113465345]
Iters. done: 8 [Current cost: -1.820842221085785]
Iters. done: 9 [Current cost: -1.8065762857059005]
Iters. done: 10 [Current cost: -1.8126394095981146]
Iters. done: 11 [Current cost: -1.8205831886180421]
Iters. done: 12 [Current cost: -1.8086715778994924]
Iters. done: 13 [Current cost: -1.8307676638629322]
Iters. done: 14 [Current cost: -1.8177328827556327]
Iters. done: 15 [Current cost: -1.8179426218088064]
Iters. done: 16 [Current cost: -1.8109239667991088]
Iters. done: 17 [Current cost: -1.824271872489647]
Iters. done: 18 [Current cost: -1.813167587671394]
Iters. done: 19 [Current cost: -1.824647343397313]
Iters. done: 20 [Current cost: -1.8219785311686143]
# Save Cost_history as a new list
ansatz_1_history = cost_history_dict["cost_history"]
# QPU time 3m 40s for ibm_brisbane

x0 = np.ones(ansatz_2.num_parameters)

batch = Batch(backend=backend)


cost_history_dict = {
    "prev_vector": None,
    "iters": 0,
    "cost_history": [],
}
estimator = Estimator(mode=batch)
estimator.options.default_shots = 2048

res = minimize(
    cost_func,
    x0,
    args=(ansatz_isa_2, hamiltonian_isa_2, estimator),
    method="cobyla",
    options={"maxiter": 20},
)

batch.close()

Output:

Iters. done: 1 [Current cost: -0.738191173881188]
Iters. done: 2 [Current cost: -0.42636037194506304]
Iters. done: 3 [Current cost: -1.3503788613797374]
Iters. done: 4 [Current cost: -0.9109204349776897]
Iters. done: 5 [Current cost: -0.9060873157510835]
Iters. done: 6 [Current cost: -0.7735065414083984]
Iters. done: 7 [Current cost: -1.586889197437709]
Iters. done: 8 [Current cost: -1.659215191584943]
Iters. done: 9 [Current cost: -1.245445981794618]
Iters. done: 10 [Current cost: -1.1608385766138023]
Iters. done: 11 [Current cost: -1.1551733876027737]
Iters. done: 12 [Current cost: -1.8143337768286332]
Iters. done: 13 [Current cost: -1.2510951563756598]
Iters. done: 14 [Current cost: -1.6918311531865413]
Iters. done: 15 [Current cost: -1.8163783305531838]
Iters. done: 16 [Current cost: -1.8434877732947152]
Iters. done: 17 [Current cost: -1.8461898233304472]
Iters. done: 18 [Current cost: -1.0346471214915485]
Iters. done: 19 [Current cost: -1.8322518854150687]
Iters. done: 20 [Current cost: -1.717144678705999]
ansatz_2_history = cost_history_dict["cost_history"]
fig, ax = plt.subplots()

# Define the constant function)
ax.plot(
    range(cost_history_dict["iters"]),
    ansatz_1_history,
    label="Ansatz with 3 parameters",
)
ax.plot(
    range(cost_history_dict["iters"]),
    ansatz_2_history,
    label="Ansatz with 12 parameters",
)
ax.set_xlabel("Iterations")
ax.set_ylabel("Cost (Hartree)")
plt.legend()
plt.draw()

Output:

Output of the previous code cell

O gráfico acima demonstra claramente que o processo de otimização do ansatz com mais variáveis leva mais tempo para chegar a uma convergência estável.

Em vez de depender de circuitos simples de um único qubit e de um ansatz direto, a complexidade da otimização aumenta quando são necessários circuitos quânticos maiores e ansätze estruturados mais complexos. Isso destaca um desafio bem conhecido em VQEs: a sobrecarga do otimizador.

Os pesquisadores continuam a desenvolver várias metodologias avançadas que podem usar computadores quânticos para problemas químicos. Você pode acessar uma variedade de materiais educacionais em IBM Quantum Learning.


Referências

  • [ ref 1 ] Richard P. Feynman, Simulating Physics with Computers, International Journal of Theoretical Physics, 1982.
  • [ref 2] Marov, M.Y. (2015). A estrutura do universo. Em: The Fundamentals of Modern Astrophysics (Fundamentos da Astrofísica Moderna). Springer, Nova York, NY.
  • [ ref 3 ] How to solve difficult chemical engineering problems with quantum computing (Como resolver problemas difíceis de engenharia química com computação quântica), IBM Research Blog, 2023.
  • [ref 4 ] Y. Cao, J. Romero e A. Aspuru-Guzik, "Potential of quantum computing for drug discovery," em IBM Journal of Research and Development, vol. 62, no. 6, pp. 6:1-6:20, 1 Nov.-Dec. 2018
  • [Ref. 5 ] Estado atual do cálculo da estrutura molecular, REv. Mod. Física. 32, 170, 1960
  • [Ref. 6 ] Fedorov, D.A, Peng, B., Govind, N. et al. Método VQE: uma breve pesquisa e desenvolvimentos recentes. Mater Theory 6, 2 (2022)
Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.