Sari la conținutul principal

Optimization Solver: O Funcție Qiskit de Q-CTRL Fire Opal

notă

Funcțiile Qiskit sunt o funcționalitate experimentală disponibilă doar utilizatorilor IBM Quantum® cu abonament Premium Plan, Flex Plan și On-Prem (prin IBM Quantum Platform API) Plan. Acestea se află în stadiu de previzualizare și pot fi modificate.

Prezentare generală

Cu Fire Opal Optimization Solver, poți rezolva probleme de optimizare la scară utilă pe hardware cuantic fără a necesita expertiză în domeniul cuantic. Pur și simplu introduci definiția problemei la nivel înalt, iar Solver-ul se ocupă de restul. Întregul flux de lucru este conștient de zgomot și utilizează Fire Opal's Performance Management în fundal. Solver-ul oferă în mod constant soluții precise pentru probleme dificil de rezolvat clasic, chiar și la scara completă a dispozitivului, pe cele mai mari QPU-uri IBM®.

Solver-ul este flexibil și poate fi utilizat pentru a rezolva probleme de optimizare combinatorie definite ca funcții obiectiv sau grafuri arbitrare. Problemele nu trebuie să fie mapate la topologia dispozitivului. Atât problemele neconstrânse, cât și cele constrânse pot fi rezolvate, cu condiția că restricțiile pot fi formulate ca termeni de penalizare. Exemplele incluse în acest ghid demonstrează cum să rezolvi o problemă de optimizare neconstrânsă și una constrânsă la scară utilă, folosind diferite tipuri de intrări ale Solver-ului. Primul exemplu implică o problemă Max-Cut definită pe un graf 3-regulat cu 156 de noduri, în timp ce al doilea exemplu abordează o problemă de Acoperire Minimă a Vârfurilor cu 50 de noduri, definită printr-o funcție cost.

Pentru a obține acces la Optimization Solver, contactează Q-CTRL.

Descrierea funcției

Solver-ul optimizează și automatizează complet întregul algoritm, de la suprimarea erorilor la nivel hardware până la maparea eficientă a problemei și optimizarea clasică în buclă închisă. În fundal, pipeline-ul Solver-ului reduce erorile la fiecare etapă, permițând performanța îmbunătățită necesară pentru o scalare semnificativă. Fluxul de lucru de bază este inspirat de Quantum Approximate Optimization Algorithm (QAOA), care este un algoritm hibrid cuantic-clasic. Pentru un rezumat detaliat al fluxului complet de lucru al Optimization Solver, consultă manuscrisul publicat.

Vizualizarea fluxului de lucru al Optimization Solver

Pentru a rezolva o problemă generică cu Optimization Solver:

  1. Definește problema ta ca o funcție obiectiv, un graf sau un lanț de spin SparsePauliOp.
  2. Conectează-te la funcție prin Catalogul de Funcții Qiskit.
  3. Rulează problema cu Solver-ul și recuperează rezultatele.

Intrări și ieșiri

Intrări

NumeTipDescriereObligatoriuImplicitExemplu
problemstr sau SparsePauliOpUna dintre reprezentările enumerate la „Formate de problemă acceptate"DaN/APoly(2.0*n[0]*n[1] + 2.0*n[0]*n[2] - 3.13520241298341*n[0] + 2.0*n[1]*n[2] - 3.14469748506794*n[1] - 3.18897660121566*n[2] + 6.0, n[0], n[1], n[2], domain='RR')
problem_typestrNumele clasei de problemă; folosit doar pentru definițiile de problemă pe bază de graf și lanț de spin, limitate la „maxcut" sau „spin_chain"; nu este necesar pentru definițiile de funcție obiectiv arbitrareNuNone"maxcut"
backend_namestrNumele Backend-ului de utilizatNuBackend-ul cel mai puțin ocupat la care instanța ta are acces"ibm_fez"
optionsdictOpțiuni de intrare, inclusiv: (Opțional) session_id: str; comportamentul implicit creează o nouă SessionNuNone{"session_id": "cw2q70c79ws0008z6g4g"}

Formate de problemă acceptate:

  • Reprezentarea expresiei polinomiale a unei funcții obiectiv. Ideal creată în Python cu un obiect SymPy Poly existent și formatată într-un șir folosind sympy.srepr.
  • Reprezentarea grafului unui tip specific de problemă. Graful trebuie creat folosind biblioteca networkx în Python. Apoi trebuie convertit într-un șir folosind funcția networkx [nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.).
  • Reprezentarea lanțului de spin a unei probleme specifice. Lanțul de spin trebuie reprezentat ca obiect SparsePauliOp; consultă documentația pentru mai multe detalii.

Backend-uri suportate: Rulează următorul cod pentru a vedea lista Backend-urilor care sunt suportate în prezent. Dacă dispozitivul tău nu este listat, contactează Q-CTRL pentru a adăuga suport.

# Added by doQumentation — required packages for this notebook
!pip install -q networkx numpy qiskit-ibm-catalog qiskit-ibm-runtime sympy
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()

service.backends()
[<IBMBackend('ibm_fez')>,
<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_pittsburgh')>,
<IBMBackend('ibm_kingston')>,
<IBMBackend('ibm_torino')>,
<IBMBackend('ibm_marrakesh')>]

Opțiuni:

NumeTipDescriereImplicit
session_idstrID-ul unei Session Qiskit Runtime existente"cw4r3je6f0t010870y3g"
job_tagslist[str]O listă de etichete de job[]

Ieșiri

NumeTipDescriereExemplu
resultdict[str, Any]Soluția și metadatele enumerate la „Conținutul dicționarului de rezultate"{'solution_bitstring_cost': 3.0, 'final_bitstring_distribution': {'000001': 100, '000011': 2}, 'iteration_count': 3, 'solution_bitstring': '000001', 'variables_to_bitstring_index_map': {n[1]': 5, 'n[2]': 4, 'n[3]': 3, 'n[4]': 2, 'n[5]': 1}, 'best_parameters': [0.19628831763697097, -1.047052334523102], 'warnings': []}

Conținutul dicționarului de rezultate:

NumeTipDescriere
solution_bitstring_costfloatCel mai bun cost minim din toate iterațiile
final_bitstring_distributionCountsDictDicționarul de numărări ale șirurilor de biți asociat costului minim
solution_bitstringstrȘirul de biți cu cel mai bun cost din distribuția finală
iteration_countintNumărul total de iterații QAOA efectuate de Solver
variables_to_bitstring_index_mapfloatMaparea de la variabile la bitul echivalent din șirul de biți
best_parameterslist[float]Parametrii beta și gamma optimizați din toate iterațiile
warningslist[str]Avertismentele produse în timpul compilării sau rulării QAOA (implicit None)

Benchmark-uri

Rezultatele de benchmark publicate arată că Solver-ul rezolvă cu succes probleme cu peste 120 de Qubiți, depășind chiar rezultatele publicate anterior pe dispozitivele de tip quantum annealing și trapped-ion. Următoarele metrici de benchmark oferă o indicație aproximativă a acurateței și scalabilității tipurilor de probleme, bazate pe câteva exemple. Metricile reale pot diferi în funcție de diverse caracteristici ale problemei, cum ar fi numărul de termeni din funcția obiectiv (densitate) și localitatea lor, numărul de variabile și ordinul polinomului.

„Numărul de Qubiți" indicat nu este o limitare strictă, ci reprezintă praguri aproximative unde poți anticipa o acuratețe a soluției extrem de consistentă. Dimensiunile mai mari ale problemelor au fost rezolvate cu succes, iar testarea dincolo de aceste limite este încurajată.

Conectivitatea arbitrară a Qubiților este suportată pentru toate tipurile de probleme.

Tip de problemăNumăr de QubițiExempluAcuratețeTimp total (s)Utilizare Runtime (s)Număr de iterații
Probleme pătratice cu conectivitate redusă156Max-Cut 3-regulat100%176429316
Optimizare binară de ordin superior156Model Ising spin-glass100%146127216
Probleme pătratice cu conectivitate densă50Max-Cut complet conectat100%175826812
Problemă constrânsă cu termeni de penalizare50Acoperire Minimă Ponderată a Vârfurilor cu densitate de 8% a muchiilor100%107421510

Începe

Mai întâi, autentifică-te folosind cheia API IBM Quantum. Apoi, selectează Funcția Qiskit după cum urmează. (Acest fragment de cod presupune că ai salvat deja contul în mediul tău local.)

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# Access Function
solver = catalog.load("q-ctrl/optimization-solver")

Exemplu: Optimizare neconstrânsă

Rulează problema tăieturii maxime (Max-Cut). Următorul exemplu demonstrează capacitățile Solver-ului pe o problemă Max-Cut pe un graf 3-regulat neponderat cu 156 de noduri, dar poți rezolva și probleme pe grafuri ponderate. Pe lângă qiskit-ibm-catalog, vei folosi și următoarele pachete pentru a rula acest exemplu: networkx și numpy. Poți instala aceste pachete decomentând celula de mai jos dacă rulezi acest exemplu într-un notebook folosind kernelul IPython.

# %pip install networkx numpy

1. Definește problema

Poți rula o problemă Max-Cut definind o problemă pe bază de graf și specificând problem_type='maxcut'.

import networkx as nx
import numpy as np

# Generate a random graph with 156 nodes
maxcut_graph = nx.random_regular_graph(d=3, n=156, seed=8)
# Optionally, visualize the graph
nx.draw_networkx(
maxcut_graph, nx.kamada_kawai_layout(maxcut_graph), node_size=100
)

Rezultatul celulei de cod anterioare

Solver-ul acceptă un șir ca intrare pentru definiția problemei.

# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)

2. Rulează problema

Când folosești metoda de intrare bazată pe graf, specifică tipul problemei.

# This cell is hidden from users
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend_name = service.least_busy(n_qubits=156).name
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)

Verifică starea sarcinii din Funcția Qiskit sau recuperează rezultatele după cum urmează:

# Get job status
print(maxcut_job.status())
QUEUED

3. Recuperează rezultatul

Recuperează valoarea tăieturii optime din dicționarul de rezultate.

notă
Maparea variabilelor la șirul de biți poate fi modificată. Dicționarul de ieșire conține un sub-dicționar variables_to_bitstring_index_map, care ajută la verificarea ordinii.
# Poll for results
maxcut_result = maxcut_job.result()

# Take the absolute value of the solution since the cost function is minimized
qctrl_maxcut = abs(maxcut_result["solution_bitstring_cost"])

# Print the optimal cut value found by the Optimization Solver
print(f"Optimal cut value: {qctrl_maxcut}")
Optimal cut value: 209.0

Poți verifica acuratețea rezultatului rezolvând problema clasic cu solvere open-source precum PuLP, dacă graful nu este dens conectat. Problemele cu densitate ridicată pot necesita solvere clasice avansate pentru a valida soluția.

Exemplu: Optimizare constrânsă

Exemplul anterior Max-Cut este o problemă de optimizare binară pătratică neconstrânsă frecventă. Optimization Solver de la Q-CTRL poate fi utilizat pentru diverse tipuri de probleme, inclusiv optimizare constrânsă. Poți rezolva tipuri arbitrare de probleme introducând definiția problemei reprezentată ca un polinom, unde restricțiile sunt modelate ca termeni de penalizare.

Următorul exemplu demonstrează cum să construiești o funcție cost pentru o problemă de optimizare constrânsă, acoperirea minimă a vârfurilor (MVC). Pe lângă pachetele qiskit-ibm-catalog și qiskit, vei folosi și următoarele pachete pentru a rula acest exemplu: numpy, networkx și sympy. Poți instala aceste pachete decomentând celula de mai jos dacă rulezi acest exemplu într-un notebook folosind kernelul IPython.

# %pip install numpy networkx sympy

1. Definește problema

Definește o problemă MVC aleatoare generând un graf cu noduri ponderate aleator.

import networkx as nx
from sympy import symbols, Poly, srepr

# To change the weights, change the seed to any integer.
rng_seed = 18
_rng = np.random.default_rng(rng_seed)
node_count = 50
edge_probability = 0.08
mvc_graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)

# add node weights
for i in mvc_graph.nodes:
mvc_graph.add_node(i, weight=_rng.random())

# Optionally, visualize the graph
nx.draw_networkx(mvc_graph, nx.kamada_kawai_layout(mvc_graph), node_size=200)

Rezultatul celulei de cod anterioare

Un model de optimizare standard pentru MVC ponderat poate fi formulat după cum urmează. Mai întâi, trebuie adăugată o penalizare pentru orice caz în care o muchie nu este conectată la un vârf din submulțime. Prin urmare, fie ni=1n_i = 1 dacă vârful ii se află în acoperiș (adică în submulțime) și ni=0n_i = 0 altfel. În al doilea rând, scopul este de a minimiza numărul total de vârfuri din submulțime, ceea ce poate fi reprezentat prin următoarea funcție:

Minimizey=iVωini\textbf{Minimize}\qquad y = \sum_{i\in V} \omega_i n_i

# Construct the cost function.
variables = symbols([f"n[{i}]" for i in range(node_count)])
cost_function = Poly(0, variables)

for i in mvc_graph.nodes():
weight = mvc_graph.nodes[i].get("weight", 0)
cost_function += variables[i] * weight

Acum, fiecare muchie din graf trebuie să includă cel puțin un capăt din acoperiș, ceea ce poate fi exprimat ca inegalitate:

ni+nj1 for all (i,j)En_i + n_j \ge 1 \texttt{ for all } (i,j)\in E

Orice caz în care o muchie nu este conectată la vârful acoperirii trebuie penalizat. Aceasta poate fi reprezentată în funcția cost prin adăugarea unei penalizări de forma P(1ninj+ninj)P(1-n_i-n_j+n_i n_j) unde PP este o constantă de penalizare pozitivă. Astfel, o alternativă neconstrânsă la inegalitatea constrânsă pentru MVC ponderat este:

Minimizey=iVωini+P((i,j)E(1ninj+ninj))\textbf{Minimize}\qquad y = \sum_{i\in V}\omega_i n_i + P(\sum_{(i,j)\in E}(1 - n_i - n_j + n_i n_j))

# Add penalty term.
penalty_constant = 2
for i, j in mvc_graph.edges():
cost_function += penalty_constant * (
1 - variables[i] - variables[j] + variables[i] * variables[j]
)

2. Rulează problema

# Solve the problem
mvc_job = solver.run(
problem=srepr(cost_function),
backend_name=backend_name, # E.g. "ibm_fez"
)

Verifică starea sarcinii din Funcția Qiskit sau recuperează rezultatele după cum urmează:

print(mvc_job.status())

3. Obține rezultatul

Recuperează soluția și analizează rezultatele. Deoarece această problemă are noduri ponderate, soluția nu este pur și simplu numărul minim de noduri acoperite. În schimb, costul soluției reprezintă suma ponderilor vârfurilor incluse în acoperirea vârfurilor. Acesta reprezintă „costul" sau „greutatea" totală a acoperirii tuturor muchiilor din graf folosind vârfurile selectate.

mvc_result = mvc_job.result()
qctrl_cost = mvc_result["solution_bitstring_cost"]

# Print results
print(f"Solution cost: {qctrl_cost}")
Solution cost: 10.248198273708624

Obține suport

Pentru orice întrebări sau probleme, contactează Q-CTRL.

Pași următori