주 콘텐츠로 건너뛰기

Optimization Solver: A Qiskit Function by Q-CTRL Fire Opal

참고

Qiskit Functions는 IBM Quantum® Premium Plan, Flex Plan, On-Prem(IBM Quantum Platform API 경유) Plan 사용자에게만 제공되는 실험적 기능입니다. 현재 미리 보기 출시 상태이며, 변경될 수 있습니다.

개요

Fire Opal Optimization Solver를 사용하면 양자 전문 지식 없이도 양자 하드웨어에서 유틸리티 규모의 최적화 문제를 해결할 수 있습니다. 고수준 문제 정의를 입력하기만 하면 Solver가 나머지를 처리합니다. 전체 워크플로는 노이즈를 인식하며, 내부적으로 Fire Opal의 성능 관리를 활용합니다. Solver는 가장 큰 IBM® QPU에서 전체 디바이스 규모로도 고전적으로 어려운 문제에 대해 지속적으로 정확한 해를 제공합니다.

Solver는 유연하며, 목적 함수 또는 임의의 그래프로 정의된 조합 최적화 문제를 푸는 데 사용할 수 있습니다. 문제를 디바이스 토폴로지에 매핑할 필요가 없습니다. 제약 조건을 페널티 항으로 공식화할 수 있다면 비제약 문제와 제약 문제 모두 풀 수 있습니다. 이 가이드에 포함된 예시는 서로 다른 Solver 입력 유형을 사용하여 비제약 및 제약 유틸리티 규모 최적화 문제를 푸는 방법을 보여줍니다. 첫 번째 예시는 156개 노드의 3-정규 그래프에서 정의된 Max-Cut 문제이며, 두 번째 예시는 비용 함수로 정의된 50개 노드의 최소 꼭짓점 커버 문제입니다.

Optimization Solver에 대한 접근 권한을 얻으려면 Q-CTRL에 문의하세요.

함수 설명

Solver는 하드웨어 수준의 오류 억제부터 효율적인 문제 매핑 및 폐루프 고전 최적화까지 전체 알고리즘을 완전히 최적화하고 자동화합니다. 내부적으로 Solver의 파이프라인은 모든 단계에서 오류를 줄여, 의미 있는 확장에 필요한 향상된 성능을 가능하게 합니다. 기반 워크플로는 하이브리드 양자-고전 알고리즘인 QAOA(Quantum Approximate Optimization Algorithm)에서 영감을 받았습니다. 전체 Optimization Solver 워크플로에 대한 자세한 요약은 게재된 논문을 참조하세요.

Optimization Solver 워크플로 시각화

Optimization Solver로 일반 문제를 풀려면:

  1. 문제를 목적 함수, 그래프, 또는 SparsePauliOp 스핀 체인으로 정의합니다.
  2. Qiskit Functions Catalog를 통해 함수에 연결합니다.
  3. Solver로 문제를 실행하고 결과를 가져옵니다.

입력 및 출력

입력

이름타입설명필수 여부기본값예시
problemstr or SparsePauliOp"허용된 문제 형식" 아래 나열된 표현 중 하나해당 없음Poly(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_typestr문제 클래스의 이름; 그래프 및 스핀 체인 문제 정의에만 사용되며 "maxcut" 또는 "spin_chain"으로 제한됨; 임의의 목적 함수 문제 정의에는 필요하지 않음아니오None"maxcut"
backend_namestr사용할 Backend의 이름아니오인스턴스가 접근 가능한 가장 덜 바쁜 Backend"ibm_fez"
optionsdict다음을 포함한 입력 옵션: (선택 사항) session_id: str; 기본 동작은 새 Session 생성아니오None{"session_id": "cw2q70c79ws0008z6g4g"}

허용된 문제 형식:

  • 목적 함수의 다항식 표현. Python에서 기존 SymPy Poly 객체로 생성하고 sympy.srepr을 사용하여 문자열로 포맷하는 것이 이상적입니다.
  • 특정 문제 유형의 그래프 표현. 그래프는 Python에서 networkx 라이브러리를 사용하여 생성해야 합니다. 그런 다음 networkx 함수 [nx.readwrite.json_graph.adjacency_data](http://nx.readwrite.json_graph.adjacency_data.)를 사용하여 문자열로 변환해야 합니다.
  • 특정 문제의 스핀 체인 표현. 스핀 체인은 SparsePauliOp 객체로 표현되어야 합니다. 자세한 내용은 문서를 참조하세요.

지원되는 Backend: 다음 코드를 실행하면 현재 지원되는 Backend 목록을 확인할 수 있습니다. 사용하려는 디바이스가 목록에 없으면 Q-CTRL에 문의하여 지원을 추가하세요.

# 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')>]

옵션:

이름타입설명기본값
session_idstr기존 Qiskit Runtime Session ID"cw4r3je6f0t010870y3g"
job_tagslist[str]작업 태그 목록[]

출력

이름타입설명예시
resultdict[str, Any]"결과 딕셔너리 내용" 아래 나열된 해와 메타데이터{'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': []}

결과 딕셔너리 내용:

이름타입설명
solution_bitstring_costfloat모든 반복에 걸쳐 최소 비용
final_bitstring_distributionCountsDict최소 비용과 관련된 비트스트링 카운트 딕셔너리
solution_bitstringstr최종 분포에서 최상의 비용을 가진 비트스트링
iteration_countintSolver가 수행한 총 QAOA 반복 횟수
variables_to_bitstring_index_mapfloat변수에서 비트스트링의 해당 비트로의 매핑
best_parameterslist[float]모든 반복에 걸쳐 최적화된 베타 및 감마 파라미터
warningslist[str]QAOA 컴파일 또는 실행 중 생성된 경고(기본값은 None)

벤치마크

게재된 벤치마킹 결과에 따르면 Solver는 120개 이상의 Qubit을 가진 문제를 성공적으로 해결하며, 심지어 양자 어닐링 및 트랩된 이온 디바이스에 대한 이전에 게재된 결과를 능가합니다. 다음 벤치마크 지표는 몇 가지 예시를 기반으로 문제 유형의 정확도 및 확장성에 대한 대략적인 지표를 제공합니다. 실제 지표는 목적 함수의 항 수(밀도) 및 그 지역성, 변수 수, 다항식 차수 등 다양한 문제 특성에 따라 다를 수 있습니다.

표시된 "Qubit 수"는 엄격한 제한이 아니라 매우 일관된 해 정확도를 기대할 수 있는 대략적인 임계값을 나타냅니다. 더 큰 문제 규모도 성공적으로 해결되었으며, 이러한 한계를 넘어서는 테스트를 권장합니다.

임의의 Qubit 연결성은 모든 문제 유형에서 지원됩니다.

문제 유형Qubit 수예시정확도총 시간(초)런타임 사용량(초)반복 횟수
희소 연결 이차 문제1563-정규 Max-Cut100%176429316
고차 이진 최적화156Ising 스핀 유리 모델100%146127216
밀집 연결 이차 문제50완전 연결 Max-Cut100%175826812
페널티 항이 있는 제약 문제508% 엣지 밀도의 가중 최소 꼭짓점 커버100%107421510

시작하기

먼저 IBM Quantum API 키를 사용하여 인증하세요. 그런 다음 아래와 같이 Qiskit Function을 선택합니다. (이 코드 조각은 이미 계정을 저장한 것을 가정합니다.)

from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

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

예시: 비제약 최적화

최대 컷 (Max-Cut) 문제를 실행합니다. 다음 예시는 156개 노드의 3-정규 비가중 그래프 Max-Cut 문제에서 Solver의 기능을 보여주지만, 가중 그래프 문제도 풀 수 있습니다. qiskit-ibm-catalog 외에도 이 예시를 실행하려면 networkxnumpy 패키지가 필요합니다. IPython 커널을 사용하는 노트북에서 이 예시를 실행하는 경우, 다음 셀의 주석을 해제하여 이 패키지들을 설치할 수 있습니다.

# %pip install networkx numpy

1. 문제 정의

그래프 문제를 정의하고 problem_type='maxcut'을 지정하여 Max-Cut 문제를 실행할 수 있습니다.

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
)

이전 코드 셀의 출력

Solver는 문자열을 문제 정의 입력으로 받습니다.

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

2. 문제 실행

그래프 기반 입력 방법을 사용할 때는 문제 유형을 지정하세요.

# 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"
)

다음과 같이 Qiskit Function 워크로드의 상태를 확인하거나 결과를 가져올 수 있습니다:

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

3. 결과 가져오기

결과 딕셔너리에서 최적 컷 값을 가져옵니다.

참고
변수와 비트스트링 간의 매핑이 변경되었을 수 있습니다. 출력 딕셔너리에는 순서를 확인하는 데 도움이 되는 variables_to_bitstring_index_map 하위 딕셔너리가 포함되어 있습니다.
# 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

그래프가 밀집 연결되어 있지 않은 경우 PuLP와 같은 오픈 소스 솔버로 문제를 고전적으로 풀어 결과의 정확도를 확인할 수 있습니다. 밀도가 높은 문제는 해를 검증하는 데 고급 고전 솔버가 필요할 수 있습니다.

예시: 제약 최적화

이전 Max-Cut 예시는 일반적인 이차 비제약 이진 최적화 문제입니다. Q-CTRL의 Optimization Solver는 제약 최적화를 포함한 다양한 문제 유형에 사용할 수 있습니다. 제약 조건이 페널티 항으로 모델링된 다항식으로 표현된 문제 정의를 입력하여 임의의 문제 유형을 풀 수 있습니다.

다음 예시는 제약 최적화 문제인 최소 꼭짓점 커버 (MVC)의 비용 함수를 구성하는 방법을 보여줍니다. qiskit-ibm-catalogqiskit 패키지 외에도 이 예시를 실행하려면 numpy, networkx, sympy 패키지가 필요합니다. IPython 커널을 사용하는 노트북에서 이 예시를 실행하는 경우, 다음 셀의 주석을 해제하여 이 패키지들을 설치할 수 있습니다.

# %pip install numpy networkx sympy

1. 문제 정의

무작위로 가중된 노드를 가진 그래프를 생성하여 무작위 MVC 문제를 정의합니다.

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)

이전 코드 셀의 출력

가중 MVC의 표준 최적화 모델은 다음과 같이 공식화할 수 있습니다. 먼저, 엣지가 부분 집합의 꼭짓점에 연결되지 않은 경우에 대한 페널티를 추가해야 합니다. 따라서 꼭짓점 ii가 커버에 있으면(즉, 부분 집합에 있으면) ni=1n_i = 1, 그렇지 않으면 ni=0n_i = 0으로 설정합니다. 둘째, 목표는 부분 집합의 꼭짓점 총 수를 최소화하는 것으로, 다음 함수로 나타낼 수 있습니다:

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

이제 그래프의 모든 엣지에는 커버에서 최소 하나의 끝점이 포함되어야 하며, 이는 다음 부등식으로 나타낼 수 있습니다:

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

엣지가 커버의 꼭짓점에 연결되지 않은 경우에는 페널티를 부과해야 합니다. 이는 PP가 양의 페널티 상수인 P(1ninj+ninj)P(1-n_i-n_j+n_i n_j) 형태의 페널티를 추가하여 비용 함수로 나타낼 수 있습니다. 따라서 가중 MVC의 제약 부등식에 대한 비제약 대안은 다음과 같습니다:

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. 문제 실행

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

다음과 같이 Qiskit Function 워크로드의 상태를 확인하거나 결과를 가져올 수 있습니다:

print(mvc_job.status())

3. 결과 가져오기

해를 가져와 결과를 분석합니다. 이 문제에는 가중된 노드가 있으므로 해는 단순히 커버된 노드의 최소 수가 아닙니다. 대신, 해 비용은 꼭짓점 커버에 포함된 꼭짓점의 가중치 합계를 나타냅니다. 이는 선택된 꼭짓점을 사용하여 그래프의 모든 엣지를 커버하는 총 "비용" 또는 "가중치"를 나타냅니다.

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

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

지원 받기

질문이나 문제가 있으면 Q-CTRL에 문의하세요.

다음 단계