Mga cost function
Sa araling ito, matututunan natin kung paano suriin ang isang cost function:
- Una, matututunan natin ang tungkol sa Qiskit Runtime primitives
- I-define ang isang cost function na . Ito ay isang function na naka-depende sa problema na nagtatakda ng layunin ng problema para mapakinabangan (o mapinalaki) ng optimizer
- Pagtatakda ng estratehiya sa pagsukat gamit ang Qiskit Runtime primitives upang ma-optimize ang bilis kumpara sa katumpakan
Mga Primitive
Ang lahat ng pisikal na sistema, maging klasikal man o quantum, ay maaaring umiral sa iba't ibang estado. Halimbawa, ang isang sasakyan sa daan ay maaaring magkaroon ng tiyak na masa, posisyon, bilis, o akselerasyon na nagpapakilala ng kanyang estado. Katulad nito, ang mga quantum na sistema ay maaari ring magkaroon ng iba't ibang konpigurasyon o estado, ngunit naiiba ang mga ito sa mga klasikal na sistema pagdating sa pagsukat at ebolusyon ng estado. Nagbubunga ito ng natatanging katangian tulad ng superposition at entanglement na eksklusibo sa quantum mechanics. Tulad ng paglalarawan sa estado ng sasakyan gamit ang mga pisikal na katangian tulad ng bilis o akselerasyon, maaari rin nating ilarawan ang estado ng isang quantum na sistema gamit ang mga observable, na mga bagay na matematikal.
Sa quantum mechanics, ang mga estado ay kinakatawan ng mga normalized na kumplikadong column vector, o mga ket (), at ang mga observable ay mga Hermitian linear operator () na kumikilos sa mga ket. Ang isang eigenvector () ng isang observable ay kilala bilang eigenstate. Ang pagsukat ng isang observable para sa isa sa mga eigenstate nito () ay magbibigay sa atin ng kaukulang eigenvalue () bilang resulta.
Kung nag-iisip ka kung paano sukatin ang isang quantum na sistema at kung ano ang maaaring sukatin, nag-aalok ang Qiskit ng dalawang primitive na makakatulong:
Sampler: Binibigyan ng isang quantum na estado na , kinukuha ng primitive na ito ang posibilidad ng bawat posibleng computational basis state.Estimator: Binibigyan ng isang quantum observable na at isang estado na , kinakalkula ng primitive na ito ang inaasahang halaga ng .
Ang Sampler primitive
Ang Sampler primitive ay kinakalkula ang posibilidad ng pagkuha ng bawat posibleng estado na mula sa computational basis, binibigyan ng quantum circuit na naghahanda ng estado na . Kinakalkula nito ang
Kung saan ang ay ang bilang ng mga qubit, at ang ang integer na representasyon ng anumang posibleng output binary string na (iyon ay, mga integer base ).
Ang Qiskit Runtime Sampler ay nagpapatakbo ng circuit nang maraming beses sa isang quantum device, nagsasagawa ng mga pagsukat sa bawat takbo, at nagtatayo muli ng probability distribution mula sa mga nakuhang bitstring. Habang mas maraming takbo (o shot) ang isinasagawa nito, mas tumpak ang mga resulta, ngunit nangangailangan ito ng mas maraming oras at quantum na mapagkukunan.
Gayunpaman, dahil ang bilang ng mga posibleng output ay lumalaki nang eksponensyal sa bilang ng mga qubit na (iyon ay, ), ang bilang ng mga shot ay kailangang lumaki rin nang eksponensyal upang makuha ang isang siksik na probability distribution. Samakatuwid, ang Sampler ay epektibo lamang para sa mga maluwag na probability distribution; kung saan ang target na estado na ay dapat maipahayag bilang isang linear na kombinasyon ng mga computational basis state, na ang bilang ng mga termino ay lumalaki nang hindi hihigit sa polynomial sa bilang ng mga qubit:
Ang Sampler ay maaari ring i-configure upang makuha ang mga posibilidad mula sa isang subseksyon ng circuit, na kumakatawan sa isang subset ng kabuuang posibleng mga estado.
Ang Estimator primitive
Ang Estimator primitive ay kinakalkula ang inaasahang halaga ng isang observable na para sa isang quantum na estado na ; kung saan ang mga posibilidad ng observable ay maaaring ipahayag bilang , na ang ay mga eigenstate ng observable na . Ang inaasahang halaga ay tinukoy bilang average ng lahat ng posibleng kinalabasan na (iyon ay, ang mga eigenvalue ng observable) ng isang pagsukat ng estado na , na may timbang ayon sa kaukulang mga posibilidad:
Gayunpaman, ang pagkalkula ng inaasahang halaga ng isang observable ay hindi palaging posible, dahil madalas ay hindi natin alam ang eigenbasis nito. Ang Qiskit Runtime Estimator ay gumagamit ng isang kumplikadong algebraic na proseso upang tantiyahin ang inaasahang halaga sa isang tunay na quantum device sa pamamagitan ng pagbasag ng observable sa kombinasyon ng iba pang mga observable na alam na natin ang eigenbasis.
Sa mas simpleng salita, binabasag ng Estimator ang anumang observable na hindi nito alam kung paano sukatin sa mas simpleng, masusukat na mga observable na tinatawag na Pauli operator.
Ang anumang operator ay maaaring ipahayag bilang kombinasyon ng na Pauli operator.
kung kaya
kung saan ang ay ang bilang ng mga qubit, ang para sa (iyon ay, mga integer base ), at .
Pagkatapos isagawa ang decomposition na ito, nagmumula ang Estimator ng bagong circuit na para sa bawat observable na (mula sa orihinal na circuit), upang epektibong i-diagonalize ang Pauli observable sa computational basis at sukatin ito. Madali nating masusukat ang mga Pauli observable dahil alam natin ang nang maaga, na hindi palaging ganoon para sa ibang mga observable.
Para sa bawat , ang Estimator ay nagpapatakbo ng kaukulang circuit sa isang quantum device nang maraming beses, sinusukat ang output state sa computational basis, at kinakalkula ang posibilidad na ng pagkuha ng bawat posibleng output na . Pagkatapos, hinahanap nito ang eigenvalue na ng na kaukulang sa bawat output na , pinarami ng , at isinasama ang lahat ng resulta upang makuha ang inaasahang halaga ng observable na para sa ibinigay na estado na .
Dahil ang pagkalkula ng inaasahang halaga ng na Pauli ay hindi praktikal (iyon ay, lumalaki nang eksponensyal), ang Estimator ay epektibo lamang kapag malaki ang bilang ng mga na zero (iyon ay, maluwag na Pauli decomposition sa halip na siksik). Pormal na sinasabi natin na, para sa komputing na ito ay mahusay na masolvable, ang bilang ng mga non-zero na termino ay dapat lumaki nang hindi hihigit sa polynomial sa bilang ng mga qubit na :
Maaaring mapansin ng mambabasa ang implicit na palagay na ang posibilidad na sampling ay kailangan ding maging mahusay tulad ng ipinaliwanag para sa Sampler, na ibig sabihin ay
Patnubay na halimbawa para sa pagkalkula ng mga inaasahang halaga
Ipagpalagay nating ang single-qubit state na , at observable
na may sumusunod na teoretikal na inaasahang halaga na
Dahil hindi natin alam kung paano sukatin ang observable na ito, hindi natin direktang makalkula ang inaasahang halaga nito, at kailangan nating muling ipahayag ito bilang . Maaaring ipakita na ito ay nagbibigay ng parehong resulta sa pamamagitan ng paggawa ng katotohanan na , at .
Tingnan natin kung paano direktang kalkulahin ang at . Dahil ang at ay hindi nagkokomyut (iyon ay, hindi sila nagbabahagi ng parehong eigenbasis), hindi sila maaaring sukatin nang sabay-sabay, kaya kailangan natin ang mga auxiliary circuit:
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime rustworkx
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
# The following code will work for any other initial single-qubit state and observable
original_circuit = QuantumCircuit(1)
original_circuit.h(0)
H = SparsePauliOp(["X", "Z"], [2, -1])
aux_circuits = []
for pauli in H.paulis:
aux_circ = original_circuit.copy()
aux_circ.barrier()
if str(pauli) == "X":
aux_circ.h(0)
elif str(pauli) == "Y":
aux_circ.sdg(0)
aux_circ.h(0)
else:
aux_circ.id(0)
aux_circ.measure_all()
aux_circuits.append(aux_circ)
original_circuit.draw("mpl")
# Auxiliary circuit for X
aux_circuits[0].draw("mpl")
# Auxiliary circuit for Z
aux_circuits[1].draw("mpl")
Maaari na nating isagawa ang komputasyon nang manu-mano gamit ang Sampler at suriin ang mga resulta sa Estimator:
from qiskit.primitives import StatevectorSampler, StatevectorEstimator
from qiskit.result import QuasiDistribution
import numpy as np
## SAMPLER
shots = 10000
sampler = StatevectorSampler()
job = sampler.run(aux_circuits, shots=shots)
# Run the sampler job and step through results
expvals = []
for index, pauli in enumerate(H.paulis):
data_pub = job.result()[index].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()}
)
# Use the probabilities and known eigenvalues of Pauli operators to estimate the expectation value.
val = 0
if str(pauli) == "X":
val += -1 * quasi_dist.get(1, 0)
val += 1 * quasi_dist.get(0, 0)
if str(pauli) == "Y":
val += -1 * quasi_dist.get(1, 0)
val += 1 * quasi_dist.get(0, 0)
if str(pauli) == "Z":
val += 1 * quasi_dist.get(0, 0)
val += -1 * quasi_dist.get(1, 0)
expvals.append(val)
# Print expectation values
print("Sampler results:")
for pauli, expval in zip(H.paulis, expvals):
print(f" >> Expected value of {str(pauli)}: {expval:.5f}")
total_expval = np.sum(H.coeffs * expvals).real
print(f" >> Total expected value: {total_expval:.5f}")
# Use estimator for comparison
observables = [
*H.paulis,
H,
] # Note: run for individual Paulis as well as full observable H
estimator = StatevectorEstimator()
job = estimator.run([(original_circuit, observables)])
estimator_expvals = job.result()[0].data.evs
# Print results
print("Estimator results:")
for obs, expval in zip(observables, estimator_expvals):
if obs is not H:
print(f" >> Expected value of {str(obs)}: {expval:.5f}")
else:
print(f" >> Total expected value: {expval:.5f}")
Sampler results:
>> Expected value of X: 1.00000
>> Expected value of Z: 0.00420
>> Total expected value: 1.99580
Estimator results:
>> Expected value of X: 1.00000
>> Expected value of Z: 0.00000
>> Total expected value: 2.00000
Matematikal na katatagan (opsyonal)
Ipinahayag ang kaugnay ng basis ng mga eigenstate ng , ang , sumusunod na:
Dahil hindi natin alam ang mga eigenvalue o eigenstate ng target observable na , kailangan muna nating isaalang-alang ang diagonalization nito. Dahil ang ay Hermitian, mayroon isang unitary transformation na kung kaya kung saan ang ay ang diagonal eigenvalue matrix, kaya kung , at .
Ipinapahiwatig nito na ang inaasahang halaga ay maaaring muling isulat bilang:
Dahil kung ang isang sistema ay nasa estado na ang posibilidad ng pagsukat ng ay , ang inaasahang halaga sa itaas ay maaaring ipahayag bilang:
Napakahalaga na tandaan na ang mga posibilidad ay kinukuha mula sa estado na sa halip na . Ito ang dahilan kung bakit ganap na kinakailangan ang matrix na . Maaari kang magtaka kung paano makuha ang matrix na at ang mga eigenvalue na . Kung mayroon ka na sa mga eigenvalue, wala nang pangangailangan na gumamit ng quantum computer dahil ang layunin ng mga variational algorithm ay ang paghanap ng mga eigenvalue na ito ng .
Sa kabutihang palad, may paraan para makalusot dito: ang anumang na matrix ay maaaring isulat bilang isang linear na kombinasyon ng tensor product ng na Pauli matrix at mga identity, lahat ay parehong hermitian at unitary na may kilalang at . Ito ang ginagawa ng Runtime's Estimator sa loob nito sa pamamagitan ng pagbasag ng anumang Operator object sa isang SparsePauliOp.
Narito ang mga Operator na maaaring gamitin:
Kaya muling isulat natin ang kaugnay ng mga Pauli at identity:
kung saan ang para sa (iyon ay, base ), at :
kung saan ang at , kung kaya:
Mga cost function
Sa pangkalahatan, ginagamit ang mga cost function upang ilarawan ang layunin ng isang problema at kung gaano kahusay ang pagganap ng isang trial state kaugnay ng layuning iyon. Ang kahulugang ito ay maaaring ilapat sa iba't ibang halimbawa sa chemistry, machine learning, finance, optimization, at iba pa.
Isaalang-alang natin ang isang simpleng halimbawa ng paghahanap ng ground state ng isang sistema. Ang aming layunin ay i-minimize ang expectation value ng observable na kumakatawan sa enerhiya (Hamiltonian ):
Maaari nating gamitin ang Estimator upang suriin ang expectation value at ipasa ang halagang ito sa isang optimizer upang i-minimize. Kung matagumpay ang pag-optimize, magbabalik ito ng hanay ng mga pinakamainam na parameter value na , mula sa kung saan makakagawa tayo ng iminumungkahing solution state na at masusukat ang expectation value bilang .
Pansinin na ikukuha lamang natin ang kakayahang i-minimize ang cost function para sa limitadong hanay ng mga state na ating isinasaalang-alang. Nagdadala ito sa atin sa dalawang magkahiwalay na posibilidad:
- Hindi tinukoy ng ating ansatz ang solution state sa buong search space: Kung ganito ang kaso, hindi kailanman makikita ng ating optimizer ang solusyon, at kailangan nating mag-eksperimento sa ibang mga ansatz na maaaring mas tumpay na kumakatawan sa ating search space.
- Hindi makita ng ating optimizer ang valid na solusyong ito: Ang optimization ay maaaring tukuyin nang globally at locally. Susuriin natin ang ibig sabihin nito sa susunod na seksyon.
Sa kabuuan, magsasagawa tayo ng classical optimization loop ngunit maaasahan ang pagsusuri ng cost function sa isang quantum computer. Mula sa perspektibong ito, maaaring isipin ng isa ang optimization bilang isang purong klasikal na gawain kung saan tinatawagan natin ang ilang black-box quantum oracle sa tuwing kailangang suriin ng optimizer ang cost function.
def cost_func_vqe(params, circuit, 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 = (circuit, hamiltonian, params)
cost = estimator.run([pub]).result()[0].data.evs
return cost
from qiskit.circuit.library import TwoLocal
observable = SparsePauliOp.from_list([("XX", 1), ("YY", -3)])
reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)
variational_form = TwoLocal(
2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)
ansatz = reference_circuit.compose(variational_form)
theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()
ansatz.decompose().draw("mpl")
Una, isasagawa natin ito gamit ang isang simulator: ang StatevectorEstimator. Karaniwang ipinapayo ito para sa pag-debug, ngunit agad susundan natin ang debugging run ng isang kalkulasyon sa tunay na quantum hardware. Lalong hindi na makakatawid sa classical simulation ang mga problemang may interes nang walang pinakabagong supercomputing facilities.
estimator = StatevectorEstimator()
cost = cost_func_vqe(theta_list, ansatz, observable, estimator)
print(cost)
[-0.58744589]
Magpapatuloy na tayo sa pagpapatakbo sa tunay na quantum computer. Pansinin ang mga pagbabago sa syntax. Ang mga hakbang na kinasasangkutan ng pass_manager ay tatalakayin pa sa susunod na halimbawa. Ang isang hakbang na partikular na mahalaga sa mga variational algorithm ay ang paggamit ng Qiskit Runtime session. Ang pagsisimula ng isang session ay nagbibigay-daan sa iyo na magpatakbo ng maraming iteration ng isang variational algorithm nang hindi naghihintay sa bagong pila sa bawat oras na ma-update ang mga parameter. Mahalaga ito kung mahaba ang mga oras ng pila at/o kailangan ng maraming iteration. Tanging ang mga kasosyo sa IBM Quantum® Network lamang ang makakagamit ng Runtime sessions. Kung wala kang access sa mga session, maaari mong bawasan ang bilang ng mga iteration na isusumite mo sa isang pagkakataon, at i-save ang pinakabagong mga parameter para gamitin sa mga susunod na run. Kung magsumite ka ng masyadong maraming iteration o makatagpo ng masyadong mahabang oras ng pila, maaari kang makatagpo ng error code 1217, na tumutukoy sa matagal na pagkaantala sa pagitan ng mga pagsusumite ng job.
# Estimated usage: < 1 min. Benchmarked at 7 seconds on an Eagle processor
# Load necessary packages:
from qiskit_ibm_runtime import (
QiskitRuntimeService,
Session,
EstimatorOptions,
EstimatorV2 as Estimator,
)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
# Select the least busy backend:
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=ansatz.num_qubits, simulator=False
)
# Or get a specific backend:
# backend = service.backend("ibm_brisbane")
# Use a pass manager to transpile the circuit and observable for the specific backend being used:
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_ansatz = pm.run(ansatz)
isa_observable = observable.apply_layout(layout=isa_ansatz.layout)
# Set estimator options
estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)
# Open a Runtime session:
with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
cost = cost_func_vqe(theta_list, isa_ansatz, isa_observable, estimator)
session.close()
print(cost)
Pansinin na ang mga halagang nakuha mula sa dalawang kalkulasyon sa itaas ay halos magkapareho. Ang mga teknik para mapabuti ang mga resulta ay tatalakayin pa sa ibaba.
Halimbawa ng mapping sa mga hindi pisikal na sistema
Ang maximum cut (Max-Cut) na problema ay isang combinatorial optimization problem na kinasasangkutan ng paghati ng mga vertex ng isang graph sa dalawang disjoint na hanay upang mapakinabangan ang bilang ng mga gilid sa pagitan ng dalawang hanay. Mas pormal, sa isang undirected graph na , kung saan ang ay ang hanay ng mga vertex at ang ay ang hanay ng mga gilid, hinahangad ng Max-Cut problem na hatiin ang mga vertex sa dalawang disjoint na subset, at , upang mapakinabangan ang bilang ng mga gilid na may isang endpoint sa at ang isa pa sa .
Maaari nating ilapat ang Max-Cut upang malutas ang iba't ibang problema kabilang ang: clustering, network design, phase transitions, at iba pa. Magsisimula tayo sa pamamagitan ng paglikha ng isang problem graph:
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"
)
Ang problemang ito ay maaaring ipahayag bilang isang binary optimization problem. Para sa bawat node na , kung saan ang ay ang bilang ng mga node ng graph (sa kasong ito ay ), isasaalang-alang natin ang binary variable na . Ang variable na ito ay magkakaroon ng halagang kung ang node ay nasa isa sa mga grupo na ating ita-label na at kung ito ay nasa kabilang grupo, na ita-label natin bilang . Itatatanda rin natin bilang (elemento ng adjacency matrix na ) ang timbang ng gilid na mula sa node patungo sa node . Dahil ang graph ay undirected, . Pagkatapos ay maaari nating buuin ang ating problema bilang pag-maximize ng sumusunod na cost function:
Upang malutas ang problemang ito gamit ang isang quantum computer, ipapahayag natin ang cost function bilang expected value ng isang observable. Gayunpaman, ang mga observable na tinatanggap ng Qiskit nang native ay binubuo ng mga Pauli operator, na may eigenvalue na at sa halip na at . Kaya naman gagawa tayo ng sumusunod na pagbabago ng variable:
Kung saan ang . Maaari nating gamitin ang adjacency matrix na upang komportableng ma-access ang mga timbang ng lahat ng gilid. Gagamitin ito upang makuha ang ating cost function:
Nangangahulugan ito na:
Kaya ang bagong cost function na gustong i-maximize natin ay:
Bukod dito, ang natural na ugali ng isang quantum computer ay ang paghahanap ng minima (karaniwang ang pinakamababang enerhiya) sa halip na maxima, kaya sa halip na i-maximize ang ay i-minimize natin ang: