최적화 솔버: Q-CTRL Fire Opal의 Qiskit 함수
API 참조 보기
키스킷 기능은 IBM 퀀텀 프리미엄 요금제, 플렉스 요금제 및 온프레미스( IBM 퀀텀 플랫폼 API를 통해) 요금제 사용자에게만 제공되는 실험적 기능입니다. 프리뷰 릴리스 상태이며 변경될 수 있습니다.
이 페이지의 코드는 다음 요구 사항을 바탕으로 개발되었습니다. 이 버전 이상을 사용하시기를 권장합니다.
qiskit-ibm-runtime~=0.47.0 sympy~=1.14.0
개요
파이어 오팔 최적화 솔버를 사용하면 양자 전문 지식이 없어도 양자 하드웨어에서 유틸리티 규모의 최적화 문제를 해결할 수 있습니다. 높은 수준의 문제 정의만 입력하면 나머지는 솔버가 알아서 처리합니다. 전체 워크플로는 노이즈를 인식하며 내부적으로 Fire Opal의 성능 관리 기능을 활용합니다. 솔버는 최대 규모의 최대 장치 규모( IBM® )에서도 고전적으로 까다로운 문제에 대한 정확한 솔루션을 일관되게 제공합니다.
이 솔버는 유연성이 뛰어나며, 목적 함수나 임의의 그래프로 정의된 조합 최적화 문제를 해결하는 데 사용할 수 있습니다. 문제를 반드시 장치 토폴로지에 매핑할 필요는 없습니다. 제약이 없는 문제와 제약이 있는 문제 모두 풀 수 있으며, 제약 조건은 페널티 항이 아닌 하드 Hamming-weight-1 제약 조건으로 적용됩니다. 이 가이드에 포함된 예제들은 다양한 솔버 입력 유형을 사용하여 제약 조건이 없는 최적화 문제와 제약 조건이 있는 대규모 최적화 문제를 해결하는 방법을 보여줍니다. 첫 번째 예제는 156개의 정점을 가진 3-정규 그래프에서 정의된 최대 절단 문제를 다루는 반면, 두 번째 예제는 비용 함수에 의해 정의된 50개의 정점을 가진 그래프 분할 문제를 다룬다.
최적화 솔버에 액세스하려면 Q-CTRL에 문의하세요.
기능 설명
솔버는 하드웨어 수준에서의 오류 억제부터 효율적인 문제 매핑 및 폐쇄 루프 클래식 최적화에 이르기까지 전체 알고리즘을 완전히 최적화하고 자동화합니다. 솔버의 파이프라인은 모든 단계에서 오류를 줄여 의미 있는 확장에 필요한 향상된 성능을 제공합니다. 기본 워크플로우는 양자-클래식 하이브리드 알고리즘인 양자 근사 최적화 알고리즘(QAOA)에서 영감을 받았습니다. 전체 최적화 솔버 워크플로우에 대한 자세한 요약은 게시된 원고를 참조하세요.
최적화 솔버로 일반적인 문제를 해결하려면:
- 문제를 목적 함수, 그래프 또는
SparsePauliOp스핀 체인으로 정의합니다. - 키스킷 함수 카탈로그를 통해 함수에 연결합니다.
- 솔버로 문제를 실행하고 결과를 검색합니다.
허용되는 문제 형식
- 목적함수의 다항식 표현. Python 에서 기존 SymPy 폴리 개체를 사용하여 생성하고 sympy.srepr.
- 특정 문제 유형을 그래프로 나타낸 것. 그래프는 Python 에 있는 networkx 라이브러리를 사용하여 작성해야 합니다. 그런 다음 networkx 함수를 사용하여 이를 문자열로 변환해야 합니다
nx.readwrite.json_graph.adjacency_data. - 특정 문제에 대한 스핀 체인 표현. 스핀 체인은
SparsePauliOp객체로 표시되어야 합니다. 자세한 내용은 문서를 참조하세요.
이 함수가 현재 지원하지 않는 백엔드를 사용하고자 하는 경우, Q-CTRL에 문의하여 지원 기능을 추가해 주시기 바랍니다.
벤치마크
성능은 문제 인스턴스와 후속 처리 단계 모두에 따라 달라질 수 있습니다. 경우에 따라서는, 고전적 샘플과 양자 방식으로 생성된 샘플이 동등한 후처리를 거친 후 유사한 최종 솔루션 품질을 달성할 수도 있다. 따라서 평가 시에는 최적화 워크플로우 전체를 고려해야 합니다.
공개된 벤치마킹 결과에 따르면 솔버는 120큐비트 이상의 문제를 성공적으로 해결했으며, 심지어 양자 어닐링 및 갇힌 이온 장치에 대해 이전에 발표된 결과보다 더 뛰어난 성능을 보였습니다. 다음 벤치마크 메트릭은 몇 가지 예를 바탕으로 문제 유형의 정확도와 확장성을 대략적으로 보여줍니다. 실제 지표는 목적 함수의 항 수(밀도)와 그 위치, 변수 수, 다항식 순서 등 다양한 문제 특징에 따라 달라질 수 있습니다.
표시된 '큐비트 수'는 엄격한 제한이 아니라 매우 일관된 솔루션 정확도를 기대할 수 있는 대략적인 임계값을 나타냅니다. 더 큰 규모의 문제도 성공적으로 해결되었으며, 이러한 한계를 넘어서는 테스트가 권장됩니다.
모든 문제 유형에서 임의 큐비트 연결이 지원됩니다.
문제점 유형 | 큐비트 수 | 예 | 정확도 | 총시간(초) | 런타임 사용량(초) | 반복 수 |
|---|---|---|---|---|---|---|
| 드물게 연결된 이차 문제 | 156 | 3-정규 최대 절단 | 100%로 | 1764 | 293 | 그림 16 |
| 고차 바이너리 최적화 | 156 | 이싱 스핀 글래스 모델 | 100%로 | 1461 | 272 | 그림 16 |
| 밀접하게 연결된 이차 문제 | 50 | 완전 연결 최대 절단 | 100%로 | 1758 | 268 | 12 |
| 엄격한 제약 조건이 있는 제약 문제 | 50 | 8%의 간선 밀도를 가진 가중 그래프 분할 | 100%로 | 1074 | 215 | 1,000만 |
시작하기
먼저, IBM Quantum API 키 를 사용하여 인증하십시오. 그런 다음 다음과 같이 Qiskit 함수를 선택합니다. (이 코드 조각은 이미 계정을 로컬 환경에 저장해 두었다고 가정합니다.)
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# Verify that you have access to the function
catalog.list()Output:
[QiskitFunction(qunova/hivqe-chemistry),
QiskitFunction(global-data-quantum/quantum-portfolio-optimizer),
QiskitFunction(algorithmiq/tem),
QiskitFunction(qedma/qesem),
QiskitFunction(multiverse/singularity),
QiskitFunction(ibm/circuit-function),
QiskitFunction(q-ctrl/optimization-solver),
QiskitFunction(colibritd/quick-pde),
QiskitFunction(q-ctrl/performance-management),
QiskitFunction(kipu-quantum/iskay-quantum-optimizer)]
# Access Function
solver = catalog.load("q-ctrl/optimization-solver")예시: 제약 없는 최적화
최대 절단 (max-cut) 문제를 실행하십시오. 다음 예제는 156개의 노드를 가진 3-정규 무가중 그래프의 최대 절단 문제에 대해 솔버의 기능을 보여줍니다. 하지만 가중 그래프 문제도 해결할 수 있습니다.
qiskit-ibm-catalog 외에도 다음 패키지를 사용하여 이 예제를 실행합니다: networkx 및 numpy. IPython 커널을 사용하는 노트북에서 이 예제를 실행하는 경우 다음 셀의 주석 처리를 해제하여 이러한 패키지를 설치할 수 있습니다.
# %pip install networkx numpy1. 문제 정의
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
)Output:
솔버는 문제 정의 입력으로 문자열을 받습니다.
# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)2. 문제를 실행하다
그래프 기반 입력 방법을 사용하는 경우 문제 유형을 지정합니다.
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)다음과 같이 Qiskit 함수 워크로드의 상태를 확인하거나 결과를 반환하세요:
# Print the ID so you can use it later, if necessary
print(maxcut_job.job_id)
# Get job status
print(maxcut_job.status())Output:
34b53970-d95a-4e24-8763-fc6f3d112843
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}")Output:
Optimal cut value: 210.0
다음과 같은 오픈 소스 솔버를 사용하여 고전적으로 문제를 해결하여 결과의 정확성을 확인할 수 있습니다 PuLP 와 같은 오픈 소스 솔버를 사용하여 고전적으로 문제를 풀면 정확성을 확인할 수 있습니다. 고밀도 문제는 솔루션을 검증하기 위해 고급 클래식 솔버가 필요할 수 있습니다.
예시: 제약 조건 최적화
앞서 살펴본 맥스컷 예제는 일반적인 2차 제약 조건이 없는 이진 최적화 문제입니다. Q-CTRL의 최적화 솔버는 제약 조건을 목적 함수 내의 페널티 항으로 인코딩하는 대신, constraint 입력을 통해 솔버에 직접 전달함으로써 제약 조건이 있는 최적화 문제도 해결할 수 있습니다. 현재 솔버는 ‘ Hamming-weight-1 ’ 제약 조건을 지원합니다. 각 제약 조건은 변수 집합을 지정하며, 이 집합 내에서는 정확히 하나의 변수만 1이 되어야 하고 나머지는 모두 0이어야 합니다.
다음 예제는 그래프 분할이라는 제약 최적화 문제에 대해, 그래프의 각 정점을 여러 그룹 중 정확히 하나에 할당하면서, 같은 그룹에 속하는 양 끝점이 있는 가장자리의 총 가중치를 최소화함으로써, 비용 함수와 일련의 강제약 조건을 구성하는 방법을 보여줍니다.
qiskit-ibm-catalog 및 qiskit 패키지 외에도 다음 패키지를 사용하여 이 예제를 실행합니다: numpy, networkx, 및 sympy. IPython 커널을 사용하는 노트북에서 이 예제를 실행하는 경우 다음 셀의 주석 처리를 해제하여 이러한 패키지를 설치할 수 있습니다.
# %pip install numpy networkx sympy1. 문제 정의
노드에 무작위 가중치를 부여하여 그래프를 생성함으로써, 무작위 그래프 분할 문제를 정의하라.
import networkx as nx
from sympy import Symbol, 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
graph = nx.erdos_renyi_graph(
node_count, edge_probability, seed=rng_seed, directed=False
)
# add node weights
min_weight = -1.0
max_weight = 1.0
for i in graph.nodes:
weight = (max_weight - min_weight) * _rng.random() + min_weight
graph.add_node(i, weight=weight)
# Optionally, visualize the graph
nx.draw_networkx(graph, nx.kamada_kawai_layout(graph), node_size=200)Output:
가중 그래프 분할에 대한 표준 최적화 모델은 다음과 같이 정식화할 수 있다. 그래프의 노드들을 세 그룹 으로 나누고, 노드 이 그룹 에 할당된 경우 로 하고, 그렇지 않은 경우 로 한다. 목표는 양 끝점이 동일한 그룹에 할당된 간선의 총 가중치를 최소화하는 것이며, 여기서 간선 의 가중치는 두 끝점 의 가중치를 합한 값이다:
# Construct the cost function.
group_count = 3
variables = [
Symbol(f"n[{i},{g}]")
for i in range(node_count)
for g in range(group_count)
]
node_group_var = {
(i, g): variables[i * group_count + g]
for i in range(node_count)
for g in range(group_count)
}
cost_function = Poly(0, *variables)
for i, j in graph.edges():
edge_weight = graph.nodes[i]["weight"] + graph.nodes[j]["weight"]
for g in range(group_count):
cost_function += (
edge_weight * node_group_var[(i, g)] * node_group_var[(j, g)]
)모든 노드는 세 그룹 중 정확히 하나에 속해야 합니다. 이는 ‘ Hamming-weight-1 ’ 제약 조건입니다. 모든 노드 에 대해, 중 정확히 하나만 1이어야 하며, 나머지는 모두 0이어야 합니다:
이 요구 사항을 비용 함수에 벌칙 항으로 반영하기보다는, 입력을 constraint 사용하여 이를 고정 제약 조건으로 Solver에 직접 전달하십시오.
# Build the hard constraint: exactly one group per node.
constraint_dict = {
str(tuple(f"n[{i},{g}]" for g in range(group_count))): 1
for i in range(node_count)
}
print(f"Problem constraints: {constraint_dict}")Output:
Problem constraints: {"('n[0,0]', 'n[0,1]', 'n[0,2]')": 1, "('n[1,0]', 'n[1,1]', 'n[1,2]')": 1, "('n[2,0]', 'n[2,1]', 'n[2,2]')": 1, "('n[3,0]', 'n[3,1]', 'n[3,2]')": 1, "('n[4,0]', 'n[4,1]', 'n[4,2]')": 1, "('n[5,0]', 'n[5,1]', 'n[5,2]')": 1, "('n[6,0]', 'n[6,1]', 'n[6,2]')": 1, "('n[7,0]', 'n[7,1]', 'n[7,2]')": 1, "('n[8,0]', 'n[8,1]', 'n[8,2]')": 1, "('n[9,0]', 'n[9,1]', 'n[9,2]')": 1, "('n[10,0]', 'n[10,1]', 'n[10,2]')": 1, "('n[11,0]', 'n[11,1]', 'n[11,2]')": 1, "('n[12,0]', 'n[12,1]', 'n[12,2]')": 1, "('n[13,0]', 'n[13,1]', 'n[13,2]')": 1, "('n[14,0]', 'n[14,1]', 'n[14,2]')": 1, "('n[15,0]', 'n[15,1]', 'n[15,2]')": 1, "('n[16,0]', 'n[16,1]', 'n[16,2]')": 1, "('n[17,0]', 'n[17,1]', 'n[17,2]')": 1, "('n[18,0]', 'n[18,1]', 'n[18,2]')": 1, "('n[19,0]', 'n[19,1]', 'n[19,2]')": 1, "('n[20,0]', 'n[20,1]', 'n[20,2]')": 1, "('n[21,0]', 'n[21,1]', 'n[21,2]')": 1, "('n[22,0]', 'n[22,1]', 'n[22,2]')": 1, "('n[23,0]', 'n[23,1]', 'n[23,2]')": 1, "('n[24,0]', 'n[24,1]', 'n[24,2]')": 1, "('n[25,0]', 'n[25,1]', 'n[25,2]')": 1, "('n[26,0]', 'n[26,1]', 'n[26,2]')": 1, "('n[27,0]', 'n[27,1]', 'n[27,2]')": 1, "('n[28,0]', 'n[28,1]', 'n[28,2]')": 1, "('n[29,0]', 'n[29,1]', 'n[29,2]')": 1, "('n[30,0]', 'n[30,1]', 'n[30,2]')": 1, "('n[31,0]', 'n[31,1]', 'n[31,2]')": 1, "('n[32,0]', 'n[32,1]', 'n[32,2]')": 1, "('n[33,0]', 'n[33,1]', 'n[33,2]')": 1, "('n[34,0]', 'n[34,1]', 'n[34,2]')": 1, "('n[35,0]', 'n[35,1]', 'n[35,2]')": 1, "('n[36,0]', 'n[36,1]', 'n[36,2]')": 1, "('n[37,0]', 'n[37,1]', 'n[37,2]')": 1, "('n[38,0]', 'n[38,1]', 'n[38,2]')": 1, "('n[39,0]', 'n[39,1]', 'n[39,2]')": 1, "('n[40,0]', 'n[40,1]', 'n[40,2]')": 1, "('n[41,0]', 'n[41,1]', 'n[41,2]')": 1, "('n[42,0]', 'n[42,1]', 'n[42,2]')": 1, "('n[43,0]', 'n[43,1]', 'n[43,2]')": 1, "('n[44,0]', 'n[44,1]', 'n[44,2]')": 1, "('n[45,0]', 'n[45,1]', 'n[45,2]')": 1, "('n[46,0]', 'n[46,1]', 'n[46,2]')": 1, "('n[47,0]', 'n[47,1]', 'n[47,2]')": 1, "('n[48,0]', 'n[48,1]', 'n[48,2]')": 1, "('n[49,0]', 'n[49,1]', 'n[49,2]')": 1}
모든 변수를 constraint.에 추가할 필요는 없습니다. 사전에 포함되지 않은 변수는 제약 조건이 적용되지 않으므로, 동일한 문제 내에서 엄격한 제약 조건이 적용된 변수 그룹과 제약 조건이 없는 변수를 함께 사용할 수 있습니다.
2. 문제를 실행하다
# Solve the problem
partition_job = solver.run(
problem=srepr(cost_function),
constraint=constraint_dict,
backend_name="ibm_marrakesh", # E.g. "ibm_marrakesh"
)다음과 같이 Qiskit 함수 워크로드의 상태를 확인하거나 결과를 반환하세요:
# Print the ID so you can use it later, if necessary
print(partition_job.job_id)
# Get job status
print(partition_job.status())Output:
b8085944-f313-444e-be39-ea61b1b47ebd
QUEUED
3. 결과를 얻다
해법을 확인하고 결과를 분석하십시오. 해의 비용은 양 끝점이 동일한 그룹에 속하게 된 변들의 총 무게를 나타내므로, 비용이 낮을수록 그래프의 분할이 더 우수함을 의미합니다.
partition_result = partition_job.result()
qctrl_cost = partition_result["solution_bitstring_cost"]
solution_bitstring = partition_result["solution_bitstring"]
# Print results
print(f"Total weight of same-group edges: {qctrl_cost}")
print(f"Solution bitstring: {solution_bitstring}")Output:
Total weight of same-group edges: -36.5539
Solution bitstring: 100100100100100001100100100100100100100100100100100001010100010100100100100010001001100100100001100001100001010001001010100100100100100010100100100100
지원 받기
질문이나 문제가 있는 경우 Q-CTRL로 문의하세요.
변경 로그
- 2026-08-10:
inputconstraint을 통해 하드 제약 조건(해밍 가중치 1)에 대한 지원을 추가했으며, 이를 사용하도록 제약 최적화 예제를 업데이트했습니다. - 2026-02-11: 이제 다음을 지원합니다.
ibm_miami
다음 단계
- Q-CTRL 최적화 솔버 에 대한 접근 권한을 요청합니다.
- 이 Qiskit 함수에 대한 API 참조 를 확인해 보세요.
- Q-CTRL의 최적화 솔버 튜토리얼로 고차 이진 최적화 문제 풀기를 사용해 보세요.
- 서평 사흐데바, N., 등 (2024). 127큐비트 게이트 모델 IBM 양자 컴퓨터를 이용한 양자 최적화는 비사소한 이진 최적화 문제에서 양자 어닐러보다 우수한 성능을 발휘할 수 있다. arXiv 사전 인쇄본 arXiv:2406.01743.
- 리뷰 로코, D., 등 (2026). 양자 컴퓨터를 활용한 신약 개발을 위한 실용적 단백질 포켓 수화 부위 예측 arXiv 사전 인쇄본 arXiv:2512.08390.
- 마즈다 사례 연구를 검토하십시오.
- 네트워크 레일 사례 연구를 검토하십시오.
- 호주 육군 사례 연구를 검토하십시오.
- 뉴사우스웨일스 교통청 사례 연구를 검토하십시오.