Primeiros passos com compilação quântica aproximada com redes tensoriais (AQC-Tensor)
Versões dos pacotes
O código nesta página foi desenvolvido usando os seguintes requisitos. Recomendamos usar essas versões ou versões mais recentes.
qiskit[all]~=2.3.0
qiskit-aer~=0.17
qiskit-addon-utils~=0.3.0
qiskit-addon-aqc-tensor[aer,quimb-jax]~=0.2.0; sys.platform != 'darwin'
scipy~=1.16.3
Este guia demonstra um exemplo simples e funcional para começar a usar o AQC-Tensor. Neste exemplo, você utilizará um circuit de Trotter que simula a evolução de um modelo de Ising em campo transverso e usará o método AQC-Tensor para reduzir a profundidade do circuit resultante. Além disso, este exemplo requer o pacote qiskit-addon-utils para o gerador de problemas, qiskit-aer para a simulação de rede tensorial e scipy para a otimização de parâmetros.
Para começar, lembre-se de que o Hamiltoniano do modelo de Ising em campo transverso tem a forma
onde assumiremos condições de contorno periódicas, que implicam que para obtemos , e é a força de acoplamento entre dois sítios e é a força do campo magnético externo.
O trecho de código a seguir irá gerar o Hamiltoniano de uma cadeia de Ising com 10 sítios e condições de contorno periódicas.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-aer scipy
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_aer import AerSimulator
from scipy.optimize import OptimizeResult, minimize
# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
# Choose a 10-qubit circle on this coupling map
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 4, 15, 3, 9]
)
# Get a qubit operator describing the Ising field model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)
Dividir a evolução temporal em duas partes para execução clássica e quântica
O objetivo geral deste exemplo é simular a evolução temporal do Hamiltoniano modelo. Fazemos isso aqui por meio da evolução de Trotter, que será dividida em duas partes.
- Uma parte inicial que pode ser simulada usando estados de produto matricial (MPS). Isso é o que será "compilado" usando o AQC-Tensor.
- Uma parte subsequente que será executada em hardware quântico.
Optaremos por evoluir o sistema até o tempo e usar o AQC-Tensor para comprimir a evolução temporal até o tempo , para então evoluir usando passos de Trotter ordinários até .
A partir daqui, geraremos dois circuits: um que será comprimido usando o AQC-Tensor e outro que será executado em um QPU. Para o primeiro circuit, como este será simulado classicamente usando estados de produto matricial, usaremos um número generoso de camadas para minimizar o erro de Trotter. Enquanto isso, o segundo circuit que simula a evolução temporal de até usará muito menos camadas para minimizar a profundidade.
# Generate circuit to be compressed
aqc_evolution_time = 4.0
aqc_target_num_trotter_steps = 45
aqc_target_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
)
# Generate circuit to execute on hardware
subsequent_evolution_time = 1.0
subsequent_num_trotter_steps = 5
subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
Para fins de comparação, também geraremos um terceiro circuit: aquele que evolui até , mas com o mesmo número de camadas que o segundo circuit evoluindo de até . Este é o circuit que teríamos executado caso não tivéssemos usado a técnica AQC-Tensor. Chamaremos este de circuit de comparação por ora.
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
aqc_comparison_num_trotter_steps
comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Gerar o ansatz e construir a simulação MPS
Em seguida, geraremos o ansatz que otimizaremos. Ele evoluirá até o mesmo tempo de evolução que nosso primeiro circuit (de até ), mas com menos passos de Trotter.
Após a geração do circuit, passamos ele para a função generate_ansatz_from_circuit() do AQC-Tensor, que analisa a conectividade de dois Qubits e retorna duas coisas. A primeira é um circuit ansatz gerado com a mesma conectividade de dois Qubits, e a segunda é um conjunto de parâmetros que, quando inseridos no ansatz, reproduzem o circuit de entrada.
aqc_ansatz_num_trotter_steps = 5
aqc_good_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
)
aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit, qubits_initially_zero=True
)
aqc_ansatz.draw("mpl", fold=-1)
Em seguida, construiremos a representação MPS do estado a ser aproximado pelo AQC. Também calcularemos a fidelidade entre o estado preparado pelo circuit de comparação e o circuit que gera o estado alvo (que utilizou mais passos de Trotter).
# Generate MPS simulator settings and obtain the MPS representation of the target state
simulator_settings = AerSimulator(
method="matrix_product_state",
matrix_product_state_max_bond_dimension=100,
)
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
# Compute the fidelity between the MPS representation of the target state and the state produced by the comparison circuit
comparison_mps = tensornetwork_from_circuit(
comparison_circuit, simulator_settings
)
comparison_fidelity = (
abs(compute_overlap(comparison_mps, aqc_target_mps)) ** 2
)
print(f"Comparison fidelity: {comparison_fidelity}")
Comparison fidelity: 0.9997111919739693
Otimizar os parâmetros do ansatz usando o MPS
Por fim, otimizaremos o circuit ansatz para que ele produza o estado alvo com uma fidelidade maior do que a comparison_fidelity. A função de custo a minimizar será a MaximizeStateFidelity e será otimizada usando o otimizador L-BFGS do scipy.
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)
stopping_point = 1 - comparison_fidelity
def callback(intermediate_result: OptimizeResult):
print(f"Intermediate result: Fidelity {1 - intermediate_result.fun:.8}")
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration
result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": 100},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)
print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
Intermediate result: Fidelity 0.95084365
Intermediate result: Fidelity 0.98409893
Intermediate result: Fidelity 0.99142033
Intermediate result: Fidelity 0.99521405
Intermediate result: Fidelity 0.99566673
Intermediate result: Fidelity 0.99650054
Intermediate result: Fidelity 0.99683487
Intermediate result: Fidelity 0.99720426
Intermediate result: Fidelity 0.99761726
Intermediate result: Fidelity 0.99809073
Intermediate result: Fidelity 0.99838244
Intermediate result: Fidelity 0.99861841
Intermediate result: Fidelity 0.99874617
Intermediate result: Fidelity 0.99892696
Intermediate result: Fidelity 0.99908129
Intermediate result: Fidelity 0.99917737
Intermediate result: Fidelity 0.99925456
Intermediate result: Fidelity 0.99933134
Intermediate result: Fidelity 0.99947173
Intermediate result: Fidelity 0.99956469
Intermediate result: Fidelity 0.99964488
Intermediate result: Fidelity 0.99967419
Intermediate result: Fidelity 0.99968821
Intermediate result: Fidelity 0.9997448
Done after 24 iterations.
Neste ponto, temos um conjunto de parâmetros que geram o estado alvo com uma fidelidade maior do que o circuit de comparação teria produzido sem o uso do AQC. Com esses parâmetros otimizados, o circuit comprimido agora tem menos erro de Trotter e menos profundidade do que o circuit original.
Como etapa final, o trecho de código a seguir constrói o circuit completo de evolução temporal, que pode ser passado para um pipeline de Transpiler e executado em hardware quântico.
final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
final_circuit.compose(subsequent_circuit, inplace=True)
final_circuit.draw("mpl", fold=-1)
Próximas etapas
- Experimente o tutorial Compilação quântica aproximada para circuits de evolução temporal.