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:
Pemerhatian ini mempunyai nilai eigen berikut:
Dan keadaan eigen:
# 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 . 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")

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 . Sekarang mari kita fikirkan bagaimana keadaan eigen ideal 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")
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")

Untuk circuit baru ini, penyelesaian ideal boleh dicapai apabila semua parameter ditetapkan kepada , 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 yang berbeza. Khususnya kita akan menggunakan dan .
Ingat bahawa, seperti yang dibincangkan ketika keadaan rujukan diperkenalkan, penyelesaian ideal akan ditemui apabila semua parameter adalah , 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 :
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 , (di mana ).
Ingat bahawa fungsi kos VQD ialah:
Ini amat penting kerana vektor (dalam kes ini ) mesti dihantar sebagai argumen apabila kita mendefinisikan objek VQD.
Juga, dalam implementasi VQD Qiskit, berbanding menggunakan observable efektif yang diterangkan dalam notebook sebelumnya, kesetiaan dikira secara langsung melalui algoritma ComputeUncompute, yang memanfaatkan primitif Sampler untuk mensampel kebarangkalian memperoleh bagi Circuit
. Ini berfungsi tepat kerana kebarangkalian ini ialah
ansatz = n_local(
num_qubits=2,
rotation_blocks=["ry", "rz"],
entanglement_blocks="cz",
# entanglement="linear",
reps=1,
)
ansatz.decompose().draw("mpl")

Mari kita mulakan dengan memeriksa observable berikut:
Observable ini mempunyai nilai eigen berikut:
Dan keadaan eigen:
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 seharusnya lebih besar daripada perbezaan antara nilai eigen. Mari kita lihat apa yang berlaku apabila syarat itu tidak dipenuhi dengan
dengan nilai eigen
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 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 , 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 ):
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")

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 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 , di mana ialah set bucu dan ialah set tepi, masalah Max-Cut meminta kita membahagikan bucu kepada dua subset yang tidak bertindih, dan , supaya bilangan tepi dengan satu hujung dalam dan hujung lain dalam 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"
)
Masalah ini boleh dinyatakan sebagai masalah pengoptimuman binari. Bagi setiap nod , di mana ialah bilangan nod graf (dalam kes ini ), kita akan mempertimbangkan pemboleh ubah binari . Pemboleh ubah ini akan bernilai jika nod berada dalam salah satu kumpulan yang akan kita labelkan sebagai , dan jika berada dalam kumpulan lain yang akan kita labelkan sebagai . Kita juga akan menandakan (elemen matriks bersebelahan ) sebagai berat tepi yang menghubungkan nod ke nod . Kerana graf ini tidak terarah, . Kemudian kita boleh merumuskan masalah kita sebagai memaksimumkan fungsi kos berikut:
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 dan berbanding dan . Sebab itulah kita akan membuat perubahan pemboleh ubah berikut:
Di mana . Kita boleh menggunakan matriks bersebelahan untuk mengakses berat semua tepi dengan mudah. Ini akan digunakan untuk mendapatkan fungsi kos kita:
Ini bermakna:
Jadi fungsi kos baru yang ingin kita maksimumkan ialah:
Selain itu, kecenderungan semula jadi komputer kuantum adalah untuk mencari minimum (biasanya tenaga terendah) bukan maksimum, jadi sebagai ganti memaksimumkan , kita akan meminimumkan:
Kini setelah kita mempunyai fungsi kos untuk diminimumkan yang pemboleh ubahnya boleh mempunyai nilai dan , kita boleh membuat analogi berikut dengan Pauli :
Dengan kata lain, pemboleh ubah akan bersamaan dengan Gate yang bertindak pada Qubit . Selain itu: