Langkau ke kandungan utama

Contoh dan aplikasi

Dalam pelajaran ini, kita akan terokai beberapa contoh algoritma variational dan cara menggunakannya:

  • Cara menulis algoritma variational tersuai
  • Cara menggunakan algoritma variational untuk mencari nilai eigen minimum
  • Cara memanfaatkan algoritma variational bagi menyelesaikan kes penggunaan aplikasi

Perhatikan bahawa kerangka corak Qiskit boleh digunakan untuk semua masalah yang kami perkenalkan di sini. Walau bagaimanapun, untuk mengelakkan pengulangan, kami hanya akan menyebut langkah-langkah kerangka tersebut secara eksplisit dalam satu contoh sahaja, yang dijalankan pada perkakasan sebenar.

Definisi masalah​

Bayangkan kita ingin menggunakan algoritma variational untuk mencari nilai eigen bagi pemerhatian berikut:

O^1=2IIβˆ’2XX+3YYβˆ’3ZZ,\hat{O}_1 = 2 II - 2 XX + 3 YY - 3 ZZ,

Pemerhatian ini mempunyai nilai eigen berikut:

{Ξ»0=βˆ’6Ξ»1=4Ξ»2=4Ξ»3=6}\left\{ \begin{array}{c} \lambda_0 = -6 \\ \lambda_1 = 4 \\ \lambda_2 = 4 \\ \lambda_3 = 6 \end{array} \right\}

Dan keadaan eigen:

{βˆ£Ο•0⟩=12(∣00⟩+∣11⟩)βˆ£Ο•1⟩=12(∣00βŸ©βˆ’βˆ£11⟩)βˆ£Ο•2⟩=12(∣01βŸ©βˆ’βˆ£10⟩)βˆ£Ο•3⟩=12(∣01⟩+∣10⟩)}\left\{ \begin{array}{c} |\phi_0\rangle = \frac{1}{\sqrt{2}}(|00\rangle + |11\rangle)\\ |\phi_1\rangle = \frac{1}{\sqrt{2}}(|00\rangle - |11\rangle)\\ |\phi_2\rangle = \frac{1}{\sqrt{2}}(|01\rangle - |10\rangle)\\ |\phi_3\rangle = \frac{1}{\sqrt{2}}(|01\rangle + |10\rangle) \end{array} \right\}
# Added by doQumentation β€” required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime rustworkx scipy
from qiskit.quantum_info import SparsePauliOp

observable_1 = SparsePauliOp.from_list([("II", 2), ("XX", -2), ("YY", 3), ("ZZ", -3)])

VQE Tersuai​

Kita akan mula dengan meneroka cara membina contoh VQE secara manual untuk mencari nilai eigen terendah bagi O^1\hat{O}_1. Ini akan menggabungkan pelbagai teknik yang telah kita pelajari sepanjang kursus ini.

def cost_func_vqe(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 (Estimator): Estimator primitive instance

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

return cost
from qiskit.circuit.library.n_local import n_local
from qiskit import QuantumCircuit

import numpy as np

reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)

variational_form = n_local(
num_qubits=2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)

raw_ansatz = reference_circuit.compose(variational_form)
raw_ansatz.decompose().draw("mpl")

Output of the previous code cell

Kita akan mulakan dengan menyahpepijat pada simulator tempatan.

from qiskit.primitives import StatevectorEstimator as Estimator
from qiskit.primitives import StatevectorSampler as Sampler

estimator = Estimator()
sampler = Sampler()

Kita kini tetapkan set parameter awal:

x0 = np.ones(raw_ansatz.num_parameters)
print(x0)
[1. 1. 1. 1. 1. 1. 1. 1.]

Kita boleh meminimumkan fungsi kos ini untuk mengira parameter optimum

# SciPy minimizer routine
from scipy.optimize import minimize
import time

start_time = time.time()

result = minimize(
cost_func_vqe,
x0,
args=(raw_ansatz, observable_1, estimator),
method="COBYLA",
options={"maxiter": 1000, "disp": True},
)

end_time = time.time()
execution_time = end_time - start_time
Return from COBYLA because the trust region radius reaches its lower bound.
Number of function values = 103 Least value of F = -5.999999998357189
The corresponding X is:
[2.27483579e+00 8.37593091e-01 1.57080508e+00 5.82932911e-06
2.49973063e+00 6.41884255e-01 6.33686904e-01 6.33688223e-01]
result
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -5.999999998357189
x: [ 2.275e+00 8.376e-01 1.571e+00 5.829e-06 2.500e+00
6.419e-01 6.337e-01 6.337e-01]
nfev: 103
maxcv: 0.0

Memandangkan masalah mainan ini hanya menggunakan dua Qubit, kita boleh semak ini dengan menggunakan penyelesai aljabar linear NumPy.

from numpy.linalg import eigvalsh

solution_eigenvalue = min(eigvalsh(observable_1.to_matrix()))

print(f"""Number of iterations: {result.nfev}""")
print(f"""Time (s): {execution_time}""")

print(
f"Percent error: {100*abs((result.fun - solution_eigenvalue)/solution_eigenvalue):.2e}"
)
Number of iterations: 103
Time (s): 0.4394676685333252
Percent error: 2.74e-08

Seperti yang boleh kamu lihat, hasilnya amat hampir dengan nilai ideal.

Bereksperimen untuk meningkatkan kelajuan dan ketepatan​

Tambah keadaan rujukan​

Dalam contoh sebelumnya kita tidak menggunakan sebarang operator rujukan URU_R. Sekarang mari kita fikirkan bagaimana keadaan eigen ideal 12(∣00⟩+∣11⟩)\frac{1}{\sqrt{2}}(|00\rangle + |11\rangle) boleh diperoleh. Pertimbangkan Circuit berikut.

from qiskit import QuantumCircuit

ideal_qc = QuantumCircuit(2)
ideal_qc.h(0)
ideal_qc.cx(0, 1)

ideal_qc.draw("mpl")

Output of the previous code cell

Kita boleh semak dengan cepat bahawa Circuit ini memberikan keadaan yang kita inginkan.

from qiskit.quantum_info import Statevector

Statevector(ideal_qc)
Statevector([0.70710678+0.j, 0.        +0.j, 0.        +0.j,
0.70710678+0.j],
dims=(2, 2))

Sekarang bahawa kita telah melihat rupa bentuk circuit yang menyediakan keadaan penyelesaian, nampaknya munasabah untuk menggunakan Gate Hadamard sebagai circuit rujukan, supaya ansatz penuh menjadi:

reference = QuantumCircuit(2)
reference.h(0)
reference.cx(0, 1)
# Include barrier to separate reference from variational form
reference.barrier()

ref_ansatz = variational_form.decompose().compose(reference, front=True)

ref_ansatz.draw("mpl")

Output of the previous code cell

Untuk circuit baru ini, penyelesaian ideal boleh dicapai apabila semua parameter ditetapkan kepada 00, jadi ini mengesahkan bahawa pilihan circuit rujukan adalah munasabah.

Sekarang mari kita bandingkan bilangan penilaian fungsi kos, iterasi pengoptimum dan masa yang diambil berbanding percubaan sebelumnya.

import time

start_time = time.time()

ref_result = minimize(
cost_func_vqe, x0, args=(ref_ansatz, observable_1, estimator), method="COBYLA"
)

end_time = time.time()
execution_time = end_time - start_time

Menggunakan parameter optimum kita untuk mengira nilai eigen minimum:

experimental_min_eigenvalue_ref = cost_func_vqe(
ref_result.x, ref_ansatz, observable_1, estimator
)
print(experimental_min_eigenvalue_ref)
-5.999999996759607
print("ADDED REFERENCE STATE:")
print(f"""Number of iterations: {ref_result.nfev}""")
print(f"""Time (s): {execution_time}""")
print(
f"Percent error: {100*abs((experimental_min_eigenvalue_ref - solution_eigenvalue)/solution_eigenvalue):.2e}"
)
ADDED REFERENCE STATE:
Number of iterations: 127
Time (s): 0.5620882511138916
Percent error: 5.40e-08

Bergantung pada sistem tertentu kamu, ini mungkin atau mungkin tidak menghasilkan peningkatan dalam kelajuan atau ketepatan pada contoh berskala sangat kecil ini. Yang penting ialah bermula dengan keadaan rujukan yang bermotivasi secara fizikal menjadi semakin penting dalam meningkatkan kelajuan dan ketepatan apabila masalah berskala lebih besar.

Tukar titik awal​

Sekarang bahawa kita telah melihat kesan penambahan keadaan rujukan, kita akan lihat apa yang berlaku apabila kita memilih titik awal ΞΈ0βƒ—\vec{\theta_0} yang berbeza. Khususnya kita akan menggunakan ΞΈ0βƒ—=(0,0,0,0,6,0,0,0)\vec{\theta_0}=(0,0,0,0,6,0,0,0) dan ΞΈ0βƒ—=(6,6,6,6,6,6,6,6,6)\vec{\theta_0}=(6,6,6,6,6,6,6,6,6).

Ingat bahawa, seperti yang dibincangkan ketika keadaan rujukan diperkenalkan, penyelesaian ideal akan ditemui apabila semua parameter adalah 00, jadi titik awal pertama sepatutnya memberikan lebih sedikit penilaian.

import time

start_time = time.time()

x0 = [0, 0, 0, 0, 6, 0, 0, 0]

x0_1_result = minimize(
cost_func_vqe, x0, args=(raw_ansatz, observable_1, estimator), method="COBYLA"
)

end_time = time.time()
execution_time = end_time - start_time
print("INITIAL POINT 1:")
print(f"""Number of iterations: {x0_1_result.nfev}""")
print(f"""Time (s): {execution_time}""")
INITIAL POINT 1:
Number of iterations: 108
Time (s): 0.4492197036743164

Melaraskan titik awal kepada ΞΈ0βƒ—=(6,6,6,6,6,6,6,6,6)\vec{\theta_0}=(6,6,6,6,6,6,6,6,6):

import time

start_time = time.time()

x0 = 6 * np.ones(raw_ansatz.num_parameters)

x0_2_result = minimize(
cost_func_vqe, x0, args=(raw_ansatz, observable_1, estimator), method="COBYLA"
)

end_time = time.time()
execution_time = end_time - start_time
print("INITIAL POINT 2:")
print(f"""Number of iterations: {x0_2_result.nfev}""")
print(f"""Time (s): {execution_time}""")
INITIAL POINT 2:
Number of iterations: 107
Time (s): 0.40889453887939453

Dengan bereksperimen menggunakan titik awal yang berbeza, kamu mungkin boleh mencapai penumpuan lebih cepat dengan lebih sedikit penilaian fungsi.

Bereksperimen dengan pengoptimum yang berbeza​

Kita boleh melaraskan pengoptimum menggunakan argumen method dalam minimize SciPy, dengan lebih banyak pilihan yang boleh ditemui di sini. Asalnya kita menggunakan pengecil tertakhluk (COBYLA). Dalam contoh ini, kita akan terokai menggunakan pengecil tidak tertakhlud (BFGS) sebagai ganti

import time

start_time = time.time()

result = minimize(
cost_func_vqe, x0, args=(raw_ansatz, observable_1, estimator), method="BFGS"
)

end_time = time.time()
execution_time = end_time - start_time
print("CHANGED TO BFGS OPTIMIZER:")
print(f"""Number of iterations: {result.nfev}""")
print(f"""Time (s): {execution_time}""")
CHANGED TO BFGS OPTIMIZER:
Number of iterations: 117
Time (s): 0.31656408309936523

Contoh VQD​

Di sini kita melaksanakan rangka kerja corak Qiskit secara eksplisit.

Langkah 1: Petakan input klasikal kepada masalah kuantum​

Kini, bukannya mencari hanya nilai eigen terendah bagi observable kita, kita akan mencari kesemua 44, (di mana k=4k=4).

Ingat bahawa fungsi kos VQD ialah:

Cl(ΞΈβƒ—):=⟨ψ(ΞΈβƒ—)∣H^∣ψ(ΞΈβƒ—)⟩+βˆ‘j=0lβˆ’1Ξ²j∣⟨ψ(ΞΈβƒ—)∣ψ(ΞΈjβƒ—)⟩∣2βˆ€l∈{1,⋯ ,k}={1,⋯ ,4}C_{l}(\vec{\theta}) := \langle \psi(\vec{\theta}) | \hat{H} | \psi(\vec{\theta})\rangle + \sum_{j=0}^{l-1}\beta_j |\langle \psi(\vec{\theta})| \psi(\vec{\theta^j})\rangle |^2 \quad \forall l\in\{1,\cdots,k\}=\{1,\cdots,4\}

Ini amat penting kerana vektor Ξ²βƒ—=(Ξ²0,⋯ ,Ξ²kβˆ’1)\vec{\beta}=(\beta_0,\cdots,\beta_{k-1}) (dalam kes ini (Ξ²0,Ξ²1,Ξ²2,Ξ²3)(\beta_0, \beta_1, \beta_2, \beta_3)) mesti dihantar sebagai argumen apabila kita mendefinisikan objek VQD.

Juga, dalam implementasi VQD Qiskit, berbanding menggunakan observable efektif yang diterangkan dalam notebook sebelumnya, kesetiaan ∣⟨ψ(ΞΈβƒ—)∣ψ(ΞΈjβƒ—)⟩∣2|\langle \psi(\vec{\theta})| \psi(\vec{\theta^j})\rangle |^2 dikira secara langsung melalui algoritma ComputeUncompute, yang memanfaatkan primitif Sampler untuk mensampel kebarangkalian memperoleh ∣0⟩|0\rangle bagi Circuit UA†(ΞΈβƒ—)UA(ΞΈjβƒ—)U_A^\dagger(\vec{\theta})U_A(\vec{\theta^j}). Ini berfungsi tepat kerana kebarangkalian ini ialah

p0=∣⟨0∣UA†(ΞΈβƒ—)UA(ΞΈjβƒ—)∣0⟩∣2=∣(⟨0∣UA†(ΞΈβƒ—))(UA(ΞΈjβƒ—)∣0⟩)∣2=∣⟨ψ(ΞΈβƒ—)∣ψ(ΞΈjβƒ—)⟩∣2\begin{aligned} p_0 & = |\langle 0|U_A^\dagger(\vec{\theta})U_A(\vec{\theta^j})|0\rangle|^2 \\[1mm] & = |\big(\langle 0|U_A^\dagger(\vec{\theta})\big)\big(U_A(\vec{\theta^j})|0\rangle\big)|^2 \\[1mm] & = |\langle \psi(\vec{\theta}) |\psi(\vec{\theta^j}) \rangle|^2 \\[1mm] \end{aligned}
ansatz = n_local(
num_qubits=2,
rotation_blocks=["ry", "rz"],
entanglement_blocks="cz",
# entanglement="linear",
reps=1,
)

ansatz.decompose().draw("mpl")

Output of the previous code cell

Mari kita mulakan dengan memeriksa observable berikut:

O^2:=2IIβˆ’3XX+2YYβˆ’4ZZ\hat{O}_2 := 2 II - 3 XX + 2 YY - 4 ZZ

Observable ini mempunyai nilai eigen berikut:

{Ξ»0=βˆ’7Ξ»1=3Ξ»2=5Ξ»3=7}\left\{ \begin{array}{c} \lambda_0 = -7 \\ \lambda_1 = 3\\ \lambda_2 = 5 \\ \lambda_3 = 7 \end{array} \right\}

Dan keadaan eigen:

{βˆ£Ο•0⟩=12(∣00⟩+∣11⟩)βˆ£Ο•1⟩=12(∣00βŸ©βˆ’βˆ£11⟩)βˆ£Ο•2⟩=12(∣01⟩+∣10⟩)βˆ£Ο•3⟩=12(∣01βŸ©βˆ’βˆ£10⟩)}\left\{ \begin{array}{c} |\phi_0\rangle = \frac{1}{\sqrt{2}}(|00\rangle + |11\rangle)\\ |\phi_1\rangle = \frac{1}{\sqrt{2}}(|00\rangle - |11\rangle)\\ |\phi_2\rangle = \frac{1}{\sqrt{2}}(|01\rangle + |10\rangle)\\ |\phi_3\rangle = \frac{1}{\sqrt{2}}(|01\rangle - |10\rangle) \end{array} \right\}
from qiskit.quantum_info import SparsePauliOp

observable_2 = SparsePauliOp.from_list([("II", 2), ("XX", -3), ("YY", 2), ("ZZ", -4)])

Kita akan menggunakan fungsi berikut untuk mengira penalti pertindihan. Perhatikan bahawa ini masih sebahagian daripada pemetaan masalah kepada Circuit kuantum. Namun, seperti yang dibincangkan dalam pelajaran sebelumnya, fungsi ini mengira pertindihan antara Circuit variasi semasa dan Circuit yang telah dioptimumkan daripada keadaan tenaga/kos lebih rendah yang diperoleh sebelumnya. Circuit baharu yang dijana juga perlu ditranspil untuk dijalankan pada perkakasan sebenar. Kita telah melihat fungsi ini sebelum ini, digunakan pada simulator. Di sini, kita perlu sudah mempertimbangkan transpilasi dan pengoptimuman berkaitan untuk apabila kita menggunakan Backend sebenar, oleh itu baris di sekitar if realbackend == 1. Ini sedikit mencampurkan langkah 2, tetapi kita akan menyebut langkah 2 secara eksplisit kemudian.

import numpy as np
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

def calculate_overlaps(
ansatz, prev_circuits, parameters, sampler, realbackend, backend
):
def create_fidelity_circuit(circuit_1, circuit_2):
if len(circuit_1.clbits) > 0:
circuit_1.remove_final_measurements()
if len(circuit_2.clbits) > 0:
circuit_2.remove_final_measurements()

circuit = circuit_1.compose(circuit_2.inverse())
circuit.measure_all()
return circuit

overlaps = []

for prev_circuit in prev_circuits:
fidelity_circuit = create_fidelity_circuit(ansatz, prev_circuit)
if realbackend == 1:
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
fidelity_circuit = pm.run(fidelity_circuit)
sampler_job = sampler.run([(fidelity_circuit, parameters)])
meas_data = sampler_job.result()[0].data.meas

counts_0 = meas_data.get_int_counts().get(0, 0)
shots = meas_data.num_shots
overlap = counts_0 / shots
overlaps.append(overlap)

return np.array(overlaps)

Kini kita tambahkan fungsi kos VQD. Perhatikan bahawa berbanding pelajaran sebelumnya, kita kini mempunyai dua argumen tambahan (realbackend dan backend) untuk membantu kita dengan transpilasi apabila menggunakan Backend sebenar.

def cost_func_vqd(
parameters,
ansatz,
prev_states,
step,
betas,
estimator,
sampler,
hamiltonian,
realbackend,
backend,
):
estimator_job = estimator.run([(ansatz, hamiltonian, [parameters])])

total_cost = 0

if step > 1:
overlaps = calculate_overlaps(
ansatz, prev_states, parameters, sampler, realbackend, backend
)
total_cost = np.sum(
[np.real(betas[state] * overlap) for state, overlap in enumerate(overlaps)]
)

estimator_result = estimator_job.result()[0]

value = estimator_result.data.evs[0] + total_cost

return value

Sekali lagi, kita akan menggunakan simulator untuk nyahpepijat, kemudian beralih ke perkakasan sebenar.

from qiskit.primitives import StatevectorSampler
from qiskit.primitives import StatevectorEstimator

sampler = StatevectorSampler(default_shots=4092)
estimator = StatevectorEstimator()

Di sini kita memperkenalkan bilangan keadaan yang ingin dikira, penalti, dan satu set parameter awal, x0.

from qiskit.quantum_info import SparsePauliOp

k = 4
betas = [50, 60, 40]
x0 = np.ones(8)

Kita akan menguji algoritma menggunakan simulator sekarang:

from scipy.optimize import minimize

prev_states = []
prev_opt_parameters = []
eigenvalues = []

realbackend = 0

for step in range(1, k + 1):
if step > 1:
prev_states.append(ansatz.assign_parameters(prev_opt_parameters))

result = minimize(
cost_func_vqd,
x0,
args=(
ansatz,
prev_states,
step,
betas,
estimator,
sampler,
observable_2,
realbackend,
None,
),
method="COBYLA",
options={"maxiter": 200, "tol": 0.000001},
)
print(result)

prev_opt_parameters = result.x
eigenvalues.append(result.fun)
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -6.9999999999996
x: [ 1.571e+00 1.571e+00 2.519e+00 2.100e+00 1.242e+00
6.935e-01 2.298e+00 1.991e+00]
nfev: 151
maxcv: 0.0
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: 3.698974255258432
x: [ 1.269e+00 1.109e+00 1.080e+00 1.200e+00 1.094e+00
1.163e+00 9.752e-01 9.519e-01]
nfev: 103
maxcv: 0.0
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: 4.731320121938101
x: [ 1.533e+00 2.451e+00 2.526e+00 2.406e+00 1.968e+00
2.105e+00 8.537e-01 8.442e-01]
nfev: 110
maxcv: 0.0
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: 7.008239313655201
x: [ 4.150e+00 2.120e+00 3.495e+00 7.262e-01 1.953e+00
-1.982e-01 3.263e-01 2.563e+00]
nfev: 126
maxcv: 0.0
eigenvalues
[np.float64(-6.9999999999996),
np.float64(3.698974255258432),
np.float64(4.731320121938101),
np.float64(7.008239313655201)]

Keputusan ini agak hampir dengan yang dijangkakan kecuali untuk ralat anggaran dan fasa global. Kita boleh menyesuaikan toleransi pada pengoptimum klasikal dan/atau penalti pertindihan keadaan vektor untuk mendapatkan nilai yang lebih tepat.

solution_eigenvalues = [-7, 3, 5, 7]

for index, experimental_eigenvalue in enumerate(eigenvalues):
solution_eigenvalue = solution_eigenvalues[index]

print(
f"Percent error: {abs((experimental_eigenvalue - solution_eigenvalue)/solution_eigenvalue):.2e}"
)
Percent error: 5.71e-14
Percent error: 2.33e-01
Percent error: 5.37e-02
Percent error: 1.18e-03

Tukar beta​

Seperti yang disebutkan dalam pelajaran sebelumnya, nilai Ξ²βƒ—\vec{\beta} seharusnya lebih besar daripada perbezaan antara nilai eigen. Mari kita lihat apa yang berlaku apabila syarat itu tidak dipenuhi dengan O^2\hat{O}_2

O^2=2IIβˆ’3XX+2YYβˆ’4ZZ\hat{O}_2 = 2 II - 3 XX + 2 YY - 4 ZZ

dengan nilai eigen

{Ξ»0=βˆ’7Ξ»1=3Ξ»2=5Ξ»3=7}\left\{ \begin{array}{c} \lambda_0 = -7 \\ \lambda_1 = 3\\ \lambda_2 = 5 \\ \lambda_3 = 7 \end{array} \right\}
from qiskit.quantum_info import SparsePauliOp

k = 4
betas = np.ones(3)
x0 = np.zeros(8)
from scipy.optimize import minimize

prev_states = []
prev_opt_parameters = []
eigenvalues = []

realbackend = 0

for step in range(1, k + 1):
if step > 1:
prev_states.append(ansatz.assign_parameters(prev_opt_parameters))

result = minimize(
cost_func_vqd,
x0,
args=(
ansatz,
prev_states,
step,
betas,
estimator,
sampler,
observable_2,
realbackend,
None,
),
method="COBYLA",
options={"tol": 0.01, "maxiter": 200},
)
print(result)

prev_opt_parameters = result.x
eigenvalues.append(result.fun)
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -6.999916534745094
x: [ 1.568e+00 -1.569e+00 1.385e-01 1.398e-01 -7.972e-01
7.835e-01 -2.375e-01 4.539e-02]
nfev: 125
maxcv: 0.0
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -1.515139929812874
x: [-5.317e-04 -2.514e-03 1.016e+00 9.998e-01 3.890e-04
1.772e-04 1.568e-04 8.497e-04]
nfev: 35
maxcv: 0.0
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: -0.509948114293115
x: [-3.796e-03 8.853e-03 3.015e-04 9.997e-01 6.271e-04
-2.554e-03 1.017e-04 2.766e-04]
nfev: 37
maxcv: 0.0
message: Return from COBYLA because the trust region radius reaches its lower bound.
success: True
status: 0
fun: 0.4914672235935682
x: [-7.178e-03 -8.652e-03 1.125e+00 -5.428e-02 -1.586e-03
2.031e-03 -3.462e-03 5.734e-03]
nfev: 35
maxcv: 0.0
solution_eigenvalues = [-7, 3, 5, 7]

for index, experimental_eigenvalue in enumerate(eigenvalues):
solution_eigenvalue = solution_eigenvalues[index]

print(
f"Percent error: {abs((experimental_eigenvalue - solution_eigenvalue)/solution_eigenvalue):.2e}"
)
Percent error: 1.19e-05
Percent error: 1.51e+00
Percent error: 1.10e+00
Percent error: 9.30e-01

Kali ini, pengoptimum mengembalikan keadaan yang sama βˆ£Ο•0⟩=12(∣00⟩+∣11⟩)|\phi_0\rangle = \frac{1}{\sqrt{2}}(|00\rangle + |11\rangle) sebagai penyelesaian cadangan untuk semua keadaan eigen: yang jelas salah. Ini berlaku kerana nilai beta terlalu kecil untuk menghukum keadaan eigen minimum dalam fungsi kos berturutan. Oleh itu, ia tidak dikecualikan daripada ruang carian efektif dalam iterasi algoritma seterusnya, dan sentiasa dipilih sebagai penyelesaian terbaik yang mungkin.

Kita mengesyorkan untuk bereksperimen dengan nilai Ξ²βƒ—\vec{\beta}, dan memastikan ia lebih besar daripada perbezaan antara nilai eigen.

Langkah 2: Optimumkan masalah untuk pelaksanaan kuantum​

Untuk menjalankan ini pada perkakasan sebenar, kita perlu mengoptimumkan Circuit kuantum untuk komputer kuantum pilihan kita. Untuk tujuan kita di sini, kita akan menggunakan Backend yang paling kurang sibuk.

from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Session, EstimatorOptions
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Or use a specific backend
# backend = service.backend("ibm_brisbane")
print(backend)
<IBMBackend('ibm_brisbane')>

Kita akan mentranspil Circuit kita menggunakan pengurus laluan prasetel dengan tahap pengoptimuman 3.

pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_ansatz = pm.run(ansatz)
isa_observable = observable_2.apply_layout(layout=isa_ansatz.layout)

Langkah 3: Laksanakan menggunakan primitif Qiskit​

Dengan memastikan nilai beta kita ditetapkan semula kepada nilai yang cukup tinggi, kita kini boleh menjalankan pengiraan kita pada perkakasan kuantum sebenar.

# Estimated compute resource usage: 25 minutes. Benchmarked at 24 min, 30 sec on an Eagle r3 processor on 5-30-24

k = 2
betas = [30, 50, 80]
x0 = np.zeros(8)

real_prev_states = []
real_prev_opt_parameters = []
real_eigenvalues = []

realbackend = 1

estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)

with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
sampler = Sampler(mode=session)

for step in range(1, k + 1):
if step > 1:
real_prev_states.append(isa_ansatz.assign_parameters(prev_opt_parameters))

result = minimize(
cost_func_vqd,
x0,
args=(
isa_ansatz,
real_prev_states,
step,
betas,
estimator,
sampler,
isa_observable,
realbackend,
backend,
),
method="COBYLA",
options={"maxiter": 200},
)
print(result)

real_prev_opt_parameters = result.x
real_eigenvalues.append(result.fun)

session.close()
print(real_eigenvalues)

Langkah 4: Pasca-proses, kembalikan keputusan dalam format klasikal​

Output kita secara strukturnya serupa dengan apa yang telah dibincangkan dalam pelajaran dan contoh sebelumnya. Tetapi ada sesuatu yang bermasalah dalam keputusan di atas, dari mana kita boleh mengambil peringatan penting dalam konteks keadaan teruja. Untuk mengehadkan masa pengiraan yang digunakan dalam contoh pembelajaran ini, kita menetapkan bilangan iterasi maksimum untuk pengoptimum klasikal yang berpotensi terlalu rendah: 200 iterasi. Pengiraan sebelumnya di atas, pada simulator, gagal menumpu dalam 200 iterasi. Di sini, kita memang menumpu... tetapi kepada toleransi yang mana? Kita tidak menentukan toleransi untuk COBYLA menganggap dirinya "menumpu". Sepintas lalu pada nilai fungsi dan perbandingan dengan jalankan sebelumnya memberitahu kita bahawa COBYLA tidak hampir menumpu kepada ketepatan yang kita perlukan.

Ada isu lain: tenaga keadaan teruja pertama kelihatan lebih rendah daripada tenaga keadaan asas! Cuba jelaskan bagaimana ini boleh berlaku. Petunjuk: ia berkaitan dengan titik penumpuan yang baru kita bincangkan. Tingkah laku ini diterangkan secara terperinci di bawah selepas VQD diaplikasikan pada molekul H2.

Kimia kuantum: penyelesai tenaga keadaan asas dan teruja​

Objektif kita adalah untuk meminimumkan nilai jangkaan bagi observable yang mewakili tenaga (Hamiltonian H^\hat{\mathcal{H}}):

minβ‘ΞΈβƒ—βŸ¨Οˆ(ΞΈβƒ—)∣H^∣ψ(ΞΈβƒ—)⟩\min_{\vec\theta} \langle\psi(\vec\theta)|\hat{\mathcal{H}}|\psi(\vec\theta)\rangle
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import efficient_su2

H2_op = SparsePauliOp.from_list(
[
("II", -1.052373245772859),
("IZ", 0.39793742484318045),
("ZI", -0.39793742484318045),
("ZZ", -0.01128010425623538),
("XX", 0.18093119978423156),
]
)

chem_ansatz = efficient_su2(H2_op.num_qubits)

chem_ansatz.decompose().draw("mpl")

Output of the previous code cell

from qiskit import QuantumCircuit

def cost_func_vqe(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 (Estimator): Estimator primitive instance

Returns:
float: Energy estimate
"""
pub = (ansatz, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
# cost = estimator.run(ansatz, hamiltonian, parameter_values=params).result().values[0]
return cost

Kita tetapkan satu set parameter awal:

import numpy as np

x0 = np.ones(chem_ansatz.num_parameters)

Kita boleh meminimumkan fungsi kos ini untuk mengira parameter optimum, dan kita boleh semak kod kita dahulu menggunakan simulator tempatan.

from qiskit.primitives import StatevectorEstimator as Estimator
from qiskit.primitives import StatevectorSampler as Sampler

estimator = Estimator()
sampler = Sampler()
# SciPy minimizer routine
from scipy.optimize import minimize
import time

start_time = time.time()

result = minimize(
cost_func_vqe, x0, args=(chem_ansatz, H2_op, estimator), method="COBYLA"
)

end_time = time.time()
execution_time = end_time - start_time

result
message: Optimization terminated successfully.
success: True
status: 1
fun: -1.857275029048451
x: [ 7.326e-01 1.354e+00 ... 1.040e+00 1.508e+00]
nfev: 242
maxcv: 0.0

Nilai minimum fungsi kos (-1.857...) ialah tenaga keadaan asas molekul H2, dalam unit hartree.

Keadaan Teruja​

Kita juga boleh menggunakan VQD untuk menyelesaikan k=2k=2 keadaan jumlah (keadaan asas dan keadaan teruja pertama).

from qiskit.quantum_info import SparsePauliOp
import numpy as np

k = 2
betas = [33, 33]
# x0 = np.zeros(ansatz.num_parameters)
x0 = [
1.164e00,
-2.438e-01,
9.358e-04,
6.745e-02,
1.990e00,
9.810e-02,
6.154e-01,
5.454e-01,
]

Kita akan tambah pengiraan pertindihan:

from scipy.optimize import minimize

prev_states = []
prev_opt_parameters = []
eigenvalues = []

realbackend = 0

for step in range(1, k + 1):
if step > 1:
prev_states.append(ansatz.assign_parameters(prev_opt_parameters))

result = minimize(
cost_func_vqd,
x0,
args=(
ansatz,
prev_states,
step,
betas,
estimator,
sampler,
H2_op,
realbackend,
None,
),
method="COBYLA",
options={"tol": 0.001, "maxiter": 2000},
)
print(result)

prev_opt_parameters = result.x
eigenvalues.append(result.fun)
message: Optimization terminated successfully.
success: True
status: 1
fun: -1.8572671093941977
x: [ 1.164e+00 -2.437e-01 2.118e-03 6.448e-02 1.990e+00
9.870e-02 6.167e-01 5.476e-01]
nfev: 58
maxcv: 0.0
message: Optimization terminated successfully.
success: True
status: 1
fun: -1.0322873777662176
x: [ 3.205e+00 1.502e+00 1.699e+00 -1.107e-02 3.086e+00
1.530e+00 4.445e-02 7.013e-02]
nfev: 99
maxcv: 0.0
eigenvalues
[-1.8572671093941977, -1.0322873777662176]

Perkakasan sebenar dan pesanan berhati-hati​

Untuk menjalankan ini pada perkakasan sebenar, kita perlu mengoptimumkan Circuit kuantum untuk komputer kuantum yang dipilih. Bagi tujuan kita di sini, kita hanya akan menggunakan Backend yang paling kurang sibuk.

from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Session, EstimatorOptions
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

Kita akan menggunakan pengurus laluan pratetap untuk transpilasi, dan kita akan mengoptimumkan Circuit kita secara maksimum menggunakan tahap pengoptimuman 3.

from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_ansatz = pm.run(ansatz)
isa_observable = H2_op.apply_layout(layout=isa_ansatz.layout)

Kerana VQD sangat berulang, kita akan menjalankan semua langkah dalam satu Runtime Session, supaya kerja-kerja kita hanya diantrekan pada awal sahaja, bukan di antara setiap kemas kini parameter. Tiada perkara lain yang berubah mengenai sintaks fungsi kos atau Estimator.

x0 = [
1.306e00,
-2.284e-01,
6.913e-02,
-2.530e-02,
1.849e00,
7.433e-02,
6.366e-01,
5.600e-01,
]
# Estimated hardware usage: 20 min benchmarked on an Eagle r3 processor on 5-30-24

real_prev_states = []
real_prev_opt_parameters = []
real_eigenvalues = []

realbackend = 1

estimator_options = EstimatorOptions(resilience_level=1, default_shots=4096)

with Session(backend=backend) as session:
estimator = Estimator(mode=session)
sampler = Sampler(mode=session)

for step in range(1, k + 1):
if step > 1:
real_prev_states.append(
isa_ansatz.assign_parameters(real_prev_opt_parameters)
)

result = minimize(
cost_func_vqd,
x0,
args=(
isa_ansatz,
real_prev_states,
step,
betas,
estimator,
sampler,
isa_observable,
realbackend,
backend,
),
method="COBYLA",
options={"tol": 0.001, "maxiter": 300},
)
print(result)

real_prev_opt_parameters = result.x
real_eigenvalues.append(result.fun)

session.close()
print(real_eigenvalues)

Tenaga keadaan asas yang diperoleh (-1.83 hartree) tidak terlalu jauh daripada nilai yang betul (-1.85 hartree). Namun, tenaga keadaan teruja agak berbeza. Ini serupa dengan tingkah laku yang salah yang kita lihat sebelum ini dalam pelajaran ini. Tenaga yang dilaporkan untuk keadaan teruja hampir sama dengan keadaan asas. Dalam kes sebelumnya, kita malah melihat tenaga keadaan teruja yang lebih rendah daripada tenaga keadaan asas yang dilaporkan.

Tidak mungkin pengiraan variasional menghasilkan tenaga yang lebih rendah daripada tenaga keadaan asas sebenar. Dalam kes terdahulu, tenaga keadaan asas yang kita peroleh tidak begitu hampir dengan keadaan asas sebenar. Kerana kita tidak mendapat tenaga keadaan asas yang benar dalam kes itu, tiada percanggahan. Dalam kes semasa, tenaga keadaan asas agak hampir dengan nilai yang betul, namun tenaga keadaan teruja nampaknya anehnya hampir dengan nilai yang sama.

Untuk memahami dengan lebih baik bagaimana ini berlaku, ingat bahawa cara kita mencari keadaan teruja ialah dengan mensyaratkan bahawa keadaan variasional berortogon dengan keadaan asas (menggunakan Circuit pertindihan dan sebutan penalti). Jika kita gagal mendapatkan tenaga keadaan asas yang tepat (atau tersasar beberapa peratus), maka kita juga gagal mendapatkan vektor keadaan asas yang tepat! Jadi apabila kita mensyaratkan bahawa keadaan teruja berortogon dengan keadaan pertama yang kita temui, kita tidak mengenakan ortogonaliti dengan keadaan asas sebenar, sebaliknya dengan suatu penghampiran (kadang-kadang penghampiran yang kurang tepat). Oleh itu, keadaan teruja tidak dipaksa untuk berortogon dengan keadaan asas sebenar, dan anggaran tenaga bagi keadaan teruja kita sebenarnya agak hampir dengan tenaga keadaan asas.

Ini akan sentiasa menjadi kebimbangan dalam VQD. Namun pada dasarnya, ini boleh diperbetulkan dengan meningkatkan bilangan maksimum lelaran untuk pengoptimum klasik, mengenakan toleransi yang lebih rendah untuk pengoptimum klasik, dan mungkin juga mencuba ansatz yang berbeza jika kita selalu terlepas keadaan asas sebenar. Seperti yang kita lihat, mungkin juga perlu mengubah suai penalti pertindihan (betas). Namun itu sebenarnya isu yang berasingan. Tiada penalti untuk pertindihan yang akan menjauhkan kamu daripada keadaan asas sebenar, jika kamu belum menemui anggaran yang sangat baik bagi keadaan asas sebenar untuk Circuit pertindihan.

Pengoptimuman: Max-Cut​

Masalah potongan maksimum (Max-Cut) ialah masalah pengoptimuman kombinatori yang melibatkan pembahagian bucu graf kepada dua set yang tidak bertindih supaya bilangan tepi antara dua set itu dimaksimumkan. Secara lebih formal, diberi graf tidak terarah G=(V,E)G=(V,E), di mana VV ialah set bucu dan EE ialah set tepi, masalah Max-Cut meminta kita membahagikan bucu kepada dua subset yang tidak bertindih, SS dan TT, supaya bilangan tepi dengan satu hujung dalam SS dan hujung lain dalam TT dimaksimumkan.

Kita boleh menggunakan Max-Cut untuk menyelesaikan pelbagai masalah, seperti pengelompokan, reka bentuk rangkaian, dan peralihan fasa. Mari kita mulakan dengan mencipta graf masalah:

import rustworkx as rx
from rustworkx.visualization import mpl_draw

n = 4
G = rx.PyGraph()
G.add_nodes_from(range(n))
# The edge syntax is (start, end, weight)
edges = [(0, 1, 1.0), (0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0), (2, 3, 1.0)]
G.add_edges_from(edges)

mpl_draw(
G, pos=rx.shell_layout(G), with_labels=True, edge_labels=str, node_color="#1192E8"
)

Output of the previous code cell

Masalah ini boleh dinyatakan sebagai masalah pengoptimuman binari. Bagi setiap nod 0≀i<n0 \leq i < n, di mana nn ialah bilangan nod graf (dalam kes ini n=4n=4), kita akan mempertimbangkan pemboleh ubah binari xix_i. Pemboleh ubah ini akan bernilai 11 jika nod ii berada dalam salah satu kumpulan yang akan kita labelkan sebagai 11, dan 00 jika berada dalam kumpulan lain yang akan kita labelkan sebagai 00. Kita juga akan menandakan wijw_{ij} (elemen (i,j)(i,j) matriks bersebelahan ww) sebagai berat tepi yang menghubungkan nod ii ke nod jj. Kerana graf ini tidak terarah, wij=wjiw_{ij}=w_{ji}. Kemudian kita boleh merumuskan masalah kita sebagai memaksimumkan fungsi kos berikut:

C(xβƒ—)=βˆ‘i,j=0nwijxi(1βˆ’xj)=βˆ‘i,j=0nwijxiβˆ’βˆ‘i,j=0nwijxixj=βˆ‘i,j=0nwijxiβˆ’βˆ‘i=0nβˆ‘j=0i2wijxixj\begin{aligned} C(\vec{x}) & =\sum_{i,j=0}^n w_{ij} x_i(1-x_j)\\[1mm] & = \sum_{i,j=0}^n w_{ij} x_i - \sum_{i,j=0}^n w_{ij} x_ix_j\\[1mm] & = \sum_{i,j=0}^n w_{ij} x_i - \sum_{i=0}^n \sum_{j=0}^i 2w_{ij} x_ix_j \end{aligned}

Untuk menyelesaikan masalah ini dengan komputer kuantum, kita akan menyatakan fungsi kos sebagai nilai jangkaan suatu boleh cerapan. Walau bagaimanapun, boleh cerapan yang Qiskit sokong secara asli terdiri daripada operator Pauli, yang mempunyai nilai eigen 11 dan βˆ’1-1 berbanding 00 dan 11. Sebab itulah kita akan membuat perubahan pemboleh ubah berikut:

Di mana xβƒ—=(x0,x1,⋯ ,xnβˆ’1)\vec{x}=(x_0,x_1,\cdots ,x_{n-1}). Kita boleh menggunakan matriks bersebelahan ww untuk mengakses berat semua tepi dengan mudah. Ini akan digunakan untuk mendapatkan fungsi kos kita:

zi=1βˆ’2xiβ†’xi=1βˆ’zi2z_i = 1-2x_i \rightarrow x_i = \frac{1-z_i}{2}

Ini bermakna:

xi=0β†’zi=1xi=1β†’zi=βˆ’1.\begin{array}{lcl} x_i=0 & \rightarrow & z_i=1 \\ x_i=1 & \rightarrow & z_i=-1.\end{array}

Jadi fungsi kos baru yang ingin kita maksimumkan ialah:

C(zβƒ—)=βˆ‘i,j=0nwij(1βˆ’zi2)(1βˆ’1βˆ’zj2)=βˆ‘i,j=0nwij4βˆ’βˆ‘i,j=0nwij4zizj=βˆ‘i=0nβˆ‘j=0iwij2βˆ’βˆ‘i=0nβˆ‘j=0iwij2zizj\begin{aligned} C(\vec{z}) & = \sum_{i,j=0}^n w_{ij} \bigg(\frac{1-z_i}{2}\bigg)\bigg(1-\frac{1-z_j}{2}\bigg)\\[1mm] & = \sum_{i,j=0}^n \frac{w_{ij}}{4} - \sum_{i,j=0}^n \frac{w_{ij}}{4} z_iz_j\\[1mm] & = \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} - \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} z_iz_j \end{aligned}

Selain itu, kecenderungan semula jadi komputer kuantum adalah untuk mencari minimum (biasanya tenaga terendah) bukan maksimum, jadi sebagai ganti memaksimumkan C(z⃗)C(\vec{z}), kita akan meminimumkan:

βˆ’C(zβƒ—)=βˆ‘i=0nβˆ‘j=0iwij2zizjβˆ’βˆ‘i=0nβˆ‘j=0iwij2-C(\vec{z}) = \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} z_iz_j - \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2}

Kini setelah kita mempunyai fungsi kos untuk diminimumkan yang pemboleh ubahnya boleh mempunyai nilai βˆ’1-1 dan 11, kita boleh membuat analogi berikut dengan Pauli ZZ:

zi≑Zi=I⏞nβˆ’1βŠ—...βŠ—Z⏞iβŠ—...βŠ—I⏞0z_i \equiv Z_i = \overbrace{I}^{n-1}\otimes ... \otimes \overbrace{Z}^{i} \otimes ... \otimes \overbrace{I}^{0}

Dengan kata lain, pemboleh ubah ziz_i akan bersamaan dengan Gate ZZ yang bertindak pada Qubit ii. Selain itu:

Zi∣xnβˆ’1β‹―x0⟩=zi∣xnβˆ’1β‹―x0βŸ©β†’βŸ¨xnβˆ’1β‹―x0∣Zi∣xnβˆ’1β‹―x0⟩=ziZ_i|x_{n-1}\cdots x_0\rangle = z_i|x_{n-1}\cdots x_0\rangle \rightarrow \langle x_{n-1}\cdots x_0 |Z_i|x_{n-1}\cdots x_0\rangle = z_i

Kemudian boleh cerapan yang akan kita pertimbangkan ialah:

H^=βˆ‘i=0nβˆ‘j=0iwij2ZiZj\hat{H} = \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2} Z_iZ_j

yang mana kita perlu menambah sebutan bebas selepas itu:

offset=βˆ’βˆ‘i=0nβˆ‘j=0iwij2\texttt{offset} = - \sum_{i=0}^n \sum_{j=0}^i \frac{w_{ij}}{2}

Operator ini ialah gabungan linear sebutan dengan operator Z pada nod yang dihubungkan oleh tepi (ingat bahawa Qubit ke-0 paling jauh di kanan): IIZZ+IZIZ+IZZI+ZIIZ+ZZIIIIZZ + IZIZ + IZZI + ZIIZ + ZZII. Setelah operator dibina, ansatz untuk algoritma QAOA boleh dibina dengan mudah menggunakan Circuit QAOAAnsatz dari pustaka Circuit Qiskit.

from qiskit.circuit.library import QAOAAnsatz
from qiskit.quantum_info import SparsePauliOp

max_hamiltonian = SparsePauliOp.from_list(
[("IIZZ", 1), ("IZIZ", 1), ("IZZI", 1), ("ZIIZ", 1), ("ZZII", 1)]
)

max_ansatz = QAOAAnsatz(max_hamiltonian, reps=2)
# Draw
max_ansatz.decompose(reps=3).draw("mpl")

Output of the previous code cell

# Sum the weights, and divide by 2

offset = -sum(edge[2] for edge in edges) / 2
print(f"""Offset: {offset}""")
Offset: -2.5
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 (Estimator): Estimator primitive instance

Returns:
float: Energy estimate
"""
pub = (ansatz, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
# cost = estimator.run(ansatz, hamiltonian, parameter_values=params).result().values[0]
return cost
from qiskit.primitives import StatevectorEstimator as Estimator
from qiskit.primitives import StatevectorSampler as Sampler

estimator = Estimator()
sampler = Sampler()

Kita kini menetapkan set awal parameter rawak:

import numpy as np

x0 = 2 * np.pi * np.random.rand(max_ansatz.num_parameters)
print(x0)
[6.0252949  0.58448176 2.15785731 1.13646074]

Mana-mana pengoptimum klasik boleh digunakan untuk meminimumkan fungsi kos. Pada sistem kuantum sebenar, pengoptimum yang direka bentuk untuk landskap fungsi kos yang tidak licin biasanya lebih baik. Di sini kita menggunakan rutin COBYLA dari SciPy melalui fungsi minimize.

Kerana kita melaksanakan banyak panggilan ke Runtime secara berulang, kita menggunakan Session untuk melaksanakan semua panggilan dalam satu blok. Selain itu, untuk QAOA, penyelesaian dikodkan dalam taburan output Circuit ansatz yang diikat dengan parameter optimum hasil minimisasi. Oleh itu, kita akan memerlukan primitif Sampler, dan akan membuat instantnya dengan Session yang sama. Kemudian jalankan rutin minimisasi kita:

result = minimize(
cost_func, x0, args=(max_ansatz, max_hamiltonian, estimator), method="COBYLA"
)
print(result)
message: Optimization terminated successfully.
success: True
status: 1
fun: -2.585287311689236
x: [ 7.332e+00 3.904e-01 2.045e+00 1.028e+00]
nfev: 80
maxcv: 0.0

Vektor penyelesaian sudut parameter (x), apabila dimasukkan ke dalam Circuit ansatz, menghasilkan pembahagian graf yang kita cari.

eigenvalue = cost_func(result.x, max_ansatz, max_hamiltonian, estimator)
print(f"""Eigenvalue: {eigenvalue}""")
print(f"""Max-Cut Objective: {eigenvalue + offset}""")
Eigenvalue: -2.585287311689236
Max-Cut Objective: -5.085287311689235
from qiskit.result import QuasiDistribution
from qiskit.primitives import StatevectorSampler

sampler = StatevectorSampler()

# Assign solution parameters to ansatz
qc = max_ansatz.assign_parameters(result.x)

# Add measurements to our circuit
qc.measure_all()

# Sample ansatz at optimal parameters
# samp_dist = sampler.run(qc).result().quasi_dists[0]

shots = 1024
job = sampler.run([qc], shots=shots)
qc.decompose().draw("mpl")

Output of the previous code cell

data_pub = job.result()[0].data
bitstrings = data_pub.meas.get_bitstrings()
counts = data_pub.meas.get_counts()
quasi_dist = QuasiDistribution(
{outcome: freq / shots for outcome, freq in counts.items()}
)
probabilities = quasi_dist

# Close the session since we are now done with it
# session.close()
from qiskit.visualization import plot_distribution

plot_distribution(counts)

Output of the previous code cell

binary_string = max(counts.items(), key=lambda kv: kv[1])[0]
x = np.asarray([int(y) for y in reversed(list(binary_string))])

colors = ["r" if x[i] == 0 else "c" for i in range(n)]
mpl_draw(
G, pos=rx.shell_layout(G), with_labels=True, edge_labels=str, node_color=colors
)

Output of the previous code cell

Ringkasan​

Dengan pelajaran ini, anda telah mempelajari:

  • Cara menulis algoritma variasi tersuai
  • Cara menggunakan algoritma variasi untuk mencari nilai eigen minimum
  • Cara memanfaatkan algoritma variasi untuk menyelesaikan kes penggunaan aplikasi

Teruskan ke pelajaran terakhir untuk menduduki penilaian anda dan mendapatkan lencana anda!

Source: IBM Quantum docs β€” updated 5 Mei 2026
English version on doQumentation β€” updated 7 Mei 2026
This translation based on the English version of approx. 27 Mac 2026