Rulează primul tău circuit pe hardware
Versiuni de pachete
Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Recomandăm să folosești aceste versiuni sau unele mai noi.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
Acest exemplu conține două părți. Mai întâi vei crea un program cuantic simplu „Hello world" și îl vei rula pe o unitate de procesare cuantică (QPU). Deoarece cercetarea cuantică reală necesită programe mult mai robuste, în a doua secțiune (Scalare la un număr mare de qubiți), vei scala programul simplu până la nivelul de utilitate.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib qiskit qiskit-ibm-runtime
Instalare și autentificare
-
Dacă nu ai instalat deja Qiskit, găsești instrucțiunile în ghidul Quickstart.
-
Instalează Qiskit Runtime pentru a rula joburi pe hardware cuantic:
pip install qiskit-ibm-runtime -
Configurează un mediu pentru a rula notebook-uri Jupyter local:
pip install jupyter
-
-
Configurează-ți autentificarea pentru accesul la hardware-ul cuantic prin intermediul Open Plan gratuit.
(Dacă ai primit prin e-mail o invitație pentru a te alătura unui cont, urmează în schimb pașii pentru utilizatorii invitați.)
-
Accesează IBM Quantum Platform pentru a te autentifica sau a crea un cont.
ImportantDacă te conectezi printr-un server proxy, trebuie să folosești Qiskit Runtime v0.44.0 sau o versiune mai recentă.
-
Generează-ți cheia API (numită și token API) pe tabloul de bord, apoi copiaz-o într-un loc sigur.
-
Accesează pagina Instances și găsește instanța pe care dorești să o folosești. Plasează cursorul peste CRN-ul ei și dă clic pentru a-l copia.
-
Salvează-ți acreditările local cu acest cod:
from qiskit_ibm_runtime import QiskitRuntimeServiceQiskitRuntimeService.save_account(# For `token`, use the 44-character API_KEY you created# and saved from the IBM Quantum Platform Home dashboardtoken="<your-api-key>",instance="<CRN>", # Optional)
-
-
Acum poți folosi acest cod Python ori de câte ori dorești să te autentifici la Qiskit Runtime Service:
from qiskit_ibm_runtime import QiskitRuntimeService# Run every time you need the serviceservice = QiskitRuntimeService()
Dacă folosești un calculator public sau un alt mediu nesecurizat, urmează în schimb instrucțiunile de autentificare manuală pentru a-ți păstra în siguranță acreditările de autentificare.
Creează și rulează un program cuantic simplu
Cei patru pași pentru a scrie un program cuantic folosind Qiskit patterns sunt:
-
Mapează problema într-un format nativ cuantic.
-
Optimizează Circuit-urile și operatorii.
-
Execută folosind o funcție primitivă cuantică.
-
Analizează rezultatele.
Pasul 1. Mapează problema într-un format nativ cuantic
Într-un program cuantic, Circuit-urile cuantice sunt formatul nativ în care se reprezintă instrucțiunile cuantice, iar operatorii reprezintă observabilele ce urmează să fie măsurate. Când creezi un Circuit, de obicei vei crea un nou obiect QuantumCircuit, apoi vei adăuga instrucțiuni la acesta în secvență.
Următoarea celulă de cod creează un Circuit care produce o stare Bell, adică o stare în care doi Qubiți sunt complet întrețesuți unul cu celălalt.
SDK-ul Qiskit folosește numerotarea biților LSb 0, unde cifra are valoarea sau . Pentru mai multe detalii, consultă subiectul Ordinea biților în Qiskit SDK.
# This cell is hidden from users. It hides several unnecessary warnings.
import warnings
import logging
warnings.filterwarnings("ignore", message=".*Instance was not set*")
warnings.filterwarnings("ignore", message=".*loading instance*")
warnings.filterwarnings("ignore", message=".*using instance*")
# This cell is hidden from users. It hides several unnecessary warnings.
class IgnoreSpecificMessages(logging.Filter):
def filter(self, record):
for text in [
"Instance was not set",
"Loading default saved account",
"Loading instance",
"Instance was not set",
"using instance",
]:
if text in record.getMessage():
return False
return True
logger = logging.getLogger("qiskit_ibm_runtime")
logger.addFilter(IgnoreSpecificMessages())
for handler in logger.handlers:
handler.addFilter(IgnoreSpecificMessages())
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorOptions
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from matplotlib import pyplot as plt
# Uncomment the next line if you want to use a simulator:
# from qiskit_ibm_runtime.fake_provider import FakeBelemV2
# Create a new circuit with two qubits
qc = QuantumCircuit(2)
# Add a Hadamard gate to qubit 0
qc.h(0)
# Perform a controlled-X gate on qubit 1, controlled by qubit 0
qc.cx(0, 1)
# Return a drawing of the circuit using MatPlotLib ("mpl").
# These guides are written by using Jupyter notebooks, which
# display the output of the last line of each cell.
# If you're running this in a script, use `print(qc.draw())` to
# print a text drawing.
qc.draw("mpl")
Consultă QuantumCircuit din documentație pentru toate operațiunile disponibile.
Când creezi Circuit-uri cuantice, trebuie să iei în considerare și ce tip de date dorești să primești după execuție. Qiskit oferă două modalități de a returna date: poți obține rezultate de eșantionare pentru un set de Qubiți pe care alegi să îi măsori, sau poți obține valoarea așteptată a unui observabil. Pregătește volumul de lucru pentru a măsura Circuit-ul tău în unul dintre aceste două moduri cu primitivele Qiskit (explicate în detaliu la Pasul 3).
Acest exemplu măsoară valorile așteptate folosind submodulul qiskit.quantum_info, specificat prin operatori (obiecte matematice utilizate pentru a reprezenta o acțiune sau un proces care schimbă o stare cuantică). Următoarea celulă de cod creează șase operatori Pauli cu doi Qubiți: IZ, IX, ZI, XI, ZZ și XX.
# Set up six different observables.
observables_labels = ["IZ", "IX", "ZI", "XI", "ZZ", "XX"]
observables = [SparsePauliOp(label) for label in observables_labels]
Aici, ceva de genul operatorului ZZ este o prescurtare pentru produsul tensorial , ceea ce înseamnă că se măsoară Z pe Qubitul 1 și Z pe Qubitul 0 împreună, obținând informații despre corelația dintre Qubitul 1 și Qubitul 0. Valorile așteptate de acest tip sunt de obicei scrise și ca .
Dacă starea este întrețesută, atunci măsurarea ar trebui să fie diferită de măsurarea . Pentru starea întrețesută specifică creată de circuit-ul nostru descris mai sus, măsurarea ar trebui să fie 1, iar măsurarea ar trebui să fie zero.
Pasul 2. Optimizează Circuit-urile și operatorii
Când execuți Circuit-uri pe un dispozitiv, este important să optimizezi setul de instrucțiuni pe care le conține Circuit-ul și să minimizezi adâncimea totală (aproximativ numărul de instrucțiuni) a Circuit-ului. Aceasta asigură obținerea celor mai bune rezultate posibile prin reducerea efectelor erorii și ale zgomotului. În plus, instrucțiunile Circuit-ului trebuie să fie conforme cu Arhitectura Setului de Instrucțiuni (ISA) a unui dispozitiv Backend și trebuie să țină cont de porțile de bază și conectivitatea Qubiților dispozitivului.
Următorul cod instanțiază un dispozitiv real la care să se trimită un job și transformă Circuit-ul și observabilele pentru a se potrivi cu ISA-ul acelui Backend. Este necesar să fi salvat deja credențialele.
service = QiskitRuntimeService()
backend = service.least_busy(simulator=False, operational=True)
# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
isa_circuit.draw("mpl", idle_wires=False)
Pasul 3. Execută folosind primitivele cuantice
Calculatoarele cuantice pot produce rezultate aleatoare, de aceea de obicei colectezi un eșantion din rezultate rulând Circuit-ul de mai multe ori. Poți estima valoarea observabilului folosind clasa Estimator. Estimator este una dintre cele două primitive; cealaltă este Sampler, care poate fi folosită pentru a obține date de la un calculator cuantic. Aceste obiecte posedă o metodă run() care execută selecția de Circuit-uri, observabile și parametri (dacă este cazul), folosind un bloc unificat primitiv (PUB).
# Construct the Estimator instance.
estimator = Estimator(mode=backend)
estimator.options.resilience_level = 1
estimator.options.default_shots = 5000
mapped_observables = [
observable.apply_layout(isa_circuit.layout) for observable in observables
]
# One pub, with one circuit to run against five different observables.
job = estimator.run([(isa_circuit, mapped_observables)])
# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job.job_id()}")
>>> Job ID: d8286mfoha1c73bl8hrg
După ce un job este trimis, poți aștepta fie ca jobul să se finalizeze în instanța Python curentă, fie să folosești job_id pentru a recupera datele ulterior. (Consultă secțiunea despre recuperarea joburilor pentru detalii.)
După ce jobul se finalizează, examinează rezultatul acestuia prin atributul result() al jobului.
# This is the result of the entire submission. You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()
# This is the result from our single pub, which had six observables,
# so contains information on all six.
pub_result = job.result()[0]
# Check there are six observables.
# If not, edit the comments in the previous cell and update this test.
assert len(pub_result.data.evs) == 6
Când rulezi programul cuantic pe un dispozitiv real, volumul de lucru trebuie să aștepte la coadă înainte de a rula. Pentru a economisi timp, poți folosi în schimb codul de mai jos pentru a rula acest volum de lucru mic pe fake_provider cu modul de testare local Qiskit Runtime. Reține că acest lucru este posibil doar pentru un Circuit mic. Când scalezi în secțiunea următoare, va trebui să folosești un dispozitiv real.
# Use the following code instead if you want to run on a simulator:
from qiskit_ibm_runtime.fake_provider import FakeBelemV2
backend = FakeBelemV2()
estimator = Estimator(backend)
# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
mapped_observables = [
observable.apply_layout(isa_circuit.layout) for observable in observables
]
job = estimator.run([(isa_circuit, mapped_observables)])
result = job.result()
# This is the result of the entire submission. You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()
# This is the result from our single pub, which had five observables,
# so contains information on all five.
pub_result = job.result()[0]
Pasul 4. Analizează rezultatele
Pasul de analiză este, de obicei, acolo unde ai putea post-procesa rezultatele folosind, de exemplu, atenuarea erorilor de măsurare sau extrapolarea zerului de zgomot (ZNE). Ai putea introduce aceste rezultate într-un alt flux de lucru pentru analize suplimentare sau poți pregăti un grafic al valorilor și datelor cheie. În general, acest pas este specific problemei tale. Pentru acest exemplu, reprezintă grafic fiecare dintre valorile așteptate care au fost măsurate pentru Circuit-ul nostru.
Valorile așteptate și abaterile standard pentru observabilele pe care le-ai specificat Estimator-ului sunt accesate prin atributele PubResult.data.evs și PubResult.data.stds ale rezultatului jobului. Pentru a obține rezultatele de la Sampler, folosește funcția PubResult.data.meas.get_counts(), care va returna un dict de măsurători sub forma unor șiruri de biți ca chei și numărători ca valorile corespunzătoare. Pentru mai multe informații, consultă ghidul Noțiuni introductive despre Sampler.
# Plot the result
values = pub_result.data.evs
errors = pub_result.data.stds
# plotting graph
plt.plot(observables_labels, values, "-o")
plt.xlabel("Observables")
plt.ylabel("Values")
plt.show()
Observă că pentru Qubiții 0 și 1, valorile așteptate independente atât pentru X, cât și pentru Z sunt 0, în timp ce corelațiile (XX și ZZ) sunt 1. Acesta este un semn distinctiv al întrețeserii cuantice.
# Make sure the results follow the claim from the previous markdown cell.
# This can happen when the device occasionally behaves strangely. If this cell
# fails, you may just need to run the notebook again.
_results = {obs: val for obs, val in zip(observables_labels, values)}
for _label in ["IZ", "IX", "ZI", "XI"]:
assert abs(_results[_label]) < 0.2
for _label in ["XX", "ZZ"]:
assert _results[_label] > 0.8
Scalare la un număr mare de qubiți
În calculul cuantic, lucrul la scară de utilitate este esențial pentru a face progrese în domeniu. Un astfel de lucru necesită calcule la o scară mult mai mare; se lucrează cu Circuit-uri care pot folosi peste 100 de Qubit-uri și peste 1000 de Gate-uri. Acest exemplu demonstrează cum poți realiza lucrări la scară de utilitate pe QPU-urile IBM® prin crearea și analiza unei stări GHZ de 100 de Qubit-uri. Folosește fluxul de lucru Qiskit patterns și se încheie prin măsurarea valorii de așteptare pentru fiecare Qubit.
Pasul 1. Mapează problema
Scrie o funcție care returnează un QuantumCircuit ce pregătește o stare GHZ de Qubit-uri (practic o stare Bell extinsă), apoi folosește acea funcție pentru a pregăti o stare GHZ de 100 de Qubit-uri și colectează observabilele care urmează să fie măsurate.
def get_qc_for_n_qubit_GHZ_state(n: int) -> QuantumCircuit:
"""This function will create a qiskit.QuantumCircuit (qc)
for an n-qubit GHZ state.
Args:
n (int): Number of qubits in the n-qubit GHZ state
Returns:
QuantumCircuit: Quantum circuit that generate the n-qubit GHZ state,
assuming all qubits start in the 0 state
"""
if isinstance(n, int) and n >= 2:
qc = QuantumCircuit(n)
qc.h(0)
for i in range(n - 1):
qc.cx(i, i + 1)
else:
raise Exception("n is not a valid input")
return qc
# Create a new circuit with 100 qubits in the GHZ state
n = 100
qc = get_qc_for_n_qubit_GHZ_state(n)
Apoi, mapează la operatorii de interes. Acest exemplu folosește operatorii ZZ dintre Qubit-uri pentru a examina comportamentul pe măsură ce acestea se îndepărtează unele de altele. Valorile de așteptare din ce în ce mai inexacte (corupte) dintre Qubit-urile îndepărtate ar dezvălui nivelul de zgomot prezent.
# ZZII...II, ZIZI...II, ... , ZIII...IZ
operator_strings = [
"Z" + "I" * i + "Z" + "I" * (n - 2 - i) for i in range(n - 1)
]
operators = [SparsePauliOp(operator) for operator in operator_strings]
Pasul 2. Optimizează problema pentru execuție pe hardware cuantic
Următorul cod transformă Circuit-ul și observabilele pentru a corespunde ISA-ului Backend-ului. Acesta necesită să fi salvat deja credențialele
service = QiskitRuntimeService()
backend = service.least_busy(
simulator=False, operational=True, min_num_qubits=100
)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(qc)
isa_operators_list = [op.apply_layout(isa_circuit.layout) for op in operators]
Pasul 3. Execută pe hardware
Trimite job-ul și activează suprimarea erorilor folosind o tehnică de reducere a erorilor numită decuplare dinamică. Nivelul de reziliență specifică cât de multă reziliență să se construiască împotriva erorilor. Nivelurile mai ridicate generează rezultate mai precise, cu prețul unor timpi de procesare mai lungi. Pentru o explicație suplimentară a opțiunilor setate în codul următor, vezi Configurarea atenuării erorilor pentru Qiskit Runtime.
options = EstimatorOptions()
options.resilience_level = 1
options.dynamical_decoupling.enable = True
options.dynamical_decoupling.sequence_type = "XY4"
# Create an Estimator object
estimator = Estimator(backend, options=options)
# Submit the circuit to Estimator
job = estimator.run([(isa_circuit, isa_operators_list)])
job_id = job.job_id()
print(job_id)
d828aeo0bvlc73d1vs20
Pasul 4. Post-procesează rezultatele
După ce job-ul se finalizează, trasează rezultatele și observă că scade odată cu creșterea , chiar dacă într-o simulare ideală toți ar trebui să fie 1.
# data
data = list(range(1, len(operators) + 1)) # Distance between the Z operators
result = job.result()[0]
values = result.data.evs # Expectation value at each Z operator.
values = [
v / values[0] for v in values
] # Normalize the expectation values to evaluate how they decay with distance.
# plotting graph
plt.plot(data, values, marker="o", label="100-qubit GHZ state")
plt.xlabel("Distance between qubits $i$")
plt.ylabel(r"$\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle $")
plt.legend()
plt.show()
Graficul anterior arată că, pe măsură ce distanța dintre Qubit-uri crește, semnalul se atenuează din cauza prezenței zgomotului.
Pași următori
- Încearcă unul dintre aceste tutoriale:
- Estimarea energiei stării fundamentale a lanțului Heisenberg cu VQE
- Rezolvă probleme de optimizare folosind QAOA
- Antrenează modele de kernel cuantic pentru sarcini de învățare automată
- Găsește instrucțiuni detaliate de instalare în ghidul Instalează Qiskit.
- Dacă preferi să nu instalezi Qiskit local, citește despre opțiunile de a utiliza Qiskit într-un mediu de dezvoltare online.
- Pentru a salva mai multe credențiale de cont sau pentru a specifica alte opțiuni de cont, consultă instrucțiunile detaliate din ghidul Salvează-ți credențialele de autentificare.