Solucionador de otimização: uma função Qiskit da Q-CTRL Fire Opal
Consulte a referência da API
As funções do Qiskit são um recurso experimental disponível apenas para usuários dos planos IBM Quantum® Premium Plan, Flex Plan e On-Prem (via IBM Quantum Platform API). Eles estão no status de versão prévia e estão sujeitos a alterações.
O código desta página foi desenvolvido com base nos seguintes requisitos. Recomendamos o uso dessas versões ou versões mais recentes.
qiskit-ibm-runtime~=0.47.0 sympy~=1.14.0
Visão geral
Com o Fire Opal Optimization Solver, você pode resolver problemas de otimização em escala de serviços públicos em hardware quântico sem precisar de conhecimento quântico. Basta inserir a definição do problema de alto nível, e o Solver se encarrega do resto. Todo o fluxo de trabalho é sensível a ruídos e aproveita o Fire Opal's Performance Management. O Solver fornece de forma consistente soluções precisas para problemas classicamente desafiadores, mesmo em escala de dispositivo completo nas maiores QPUs IBM®.
O Solver é flexível e pode ser utilizado para resolver problemas de otimização combinatória definidos como funções-objetivo ou grafos arbitrários. Os problemas não precisam ser mapeados para a topologia do dispositivo. Tanto os problemas sem restrições quanto aqueles com restrições são solucionáveis, sendo as restrições aplicadas como restrições rígidas Hamming-weight-1, em vez de termos de penalidade. Os exemplos incluídos neste guia demonstram como resolver um problema de otimização em escala de utilidade, sem restrições e com restrições, utilizando diferentes tipos de entrada do Solver. O primeiro exemplo envolve um problema de corte máximo definido em um grafo regular de grau 3 com 156 vértices, enquanto o segundo exemplo aborda um problema de partição de grafo com 50 vértices definido por uma função de custo.
Para obter acesso ao Optimization Solver, entre em contato com a Q-CTRL.
Descrição da função
O Solver otimiza e automatiza totalmente o algoritmo inteiro, desde a supressão de erros no nível do hardware até o mapeamento eficiente do problema e a otimização clássica de loop fechado. Nos bastidores, o pipeline do Solver reduz os erros em todos os estágios, possibilitando o desempenho aprimorado necessário para um dimensionamento significativo. O fluxo de trabalho subjacente é inspirado no Algoritmo de Otimização Aproximada Quântica (QAOA), que é um algoritmo híbrido quântico-clássico. Para obter um resumo detalhado do fluxo de trabalho completo do Optimization Solver, consulte o manuscrito publicado.
Para resolver um problema genérico com o Optimization Solver:
- Defina seu problema como uma função objetiva, um gráfico ou uma cadeia de spin
SparsePauliOp. - Conecte-se à função por meio do Qiskit Functions Catalog.
- Execute o problema com o Solver e recupere os resultados.
Formatos de problemas aceitos
- Representação de expressão polinomial de uma função objetiva. Idealmente criado em Python com um objeto SymPy Poly existente e formatado em uma string usando sympy.srepr.
- Representação gráfica de um tipo específico de problema. O gráfico deve ser criado usando a biblioteca networkx, disponível em Python. Em seguida, deve ser convertido em uma string usando a função do networkx
nx.readwrite.json_graph.adjacency_data. - Representação da cadeia de spin de um problema específico. A cadeia de spin deve ser representada como um objeto
SparsePauliOp; consulte a documentação para obter mais detalhes.
Se você quiser usar um backend que esta função ainda não suporta, entre em contato com a Q-CTRL para solicitar a adição desse suporte.
Referências
O desempenho pode depender tanto da instância do problema quanto das etapas de processamento subsequentes. Em alguns casos, amostras clássicas e amostras geradas quânticas podem atingir uma qualidade final de solução semelhante após um pós-processamento equivalente. A avaliação deve, portanto, levar em conta todo o fluxo de trabalho de otimização.
Os resultados de benchmarking publicados mostram que o Solver resolve com sucesso problemas com mais de 120 qubits, superando até mesmo os resultados publicados anteriormente sobre recozimento quântico e dispositivos de íons presos. As métricas de referência a seguir fornecem uma indicação aproximada da precisão e do dimensionamento dos tipos de problemas com base em alguns exemplos. As métricas reais podem diferir com base em vários recursos do problema, como o número de termos na função objetiva (densidade) e sua localidade, número de variáveis e ordem polinomial.
O "Número de qubits" indicado não é uma limitação rígida, mas representa limites aproximados em que você pode esperar uma precisão de solução extremamente consistente. Problemas de tamanhos maiores foram resolvidos com sucesso, e os testes além desses limites são incentivados.
A conectividade arbitrária de qubit é suportada em todos os tipos de problemas.
Tipo de problema | Número de qubits | Exemplo | Precisão | Tempo Total (s) | Uso do tempo de execução (s) | Número de iterações |
|---|---|---|---|---|---|---|
| Problemas quadráticos com conexões esparsas | 156 | 3-corte máximo regular | 100% | 1764 | 293 | 16 |
| Otimização binária de ordem superior | 156 | Modelo de vidro de spin de Ising | 100% | 1461 | 272 | 16 |
| Problemas quadráticos densamente conectados | 50 | Corte máximo com conexão total | 100% | 1758 | 268 | 12 |
| Problema com restrições rígidas | 50 | Particionamento de grafos ponderados com densidade de arestas de 8% | 100% | 1074 | 215 | 22 |
Introdução
Primeiro, autentique-se usando sua chave de API IBM Quantum. Em seguida, selecione a função do Qiskit da seguinte maneira. (Este trecho pressupõe que você já tenha salvo sua conta no seu ambiente local.)
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")Exemplo: Otimização sem restrições
Resolva o problema do corte máximo (max-cut). O exemplo a seguir demonstra as capacidades do Solver em um problema de corte máximo em um grafo não ponderado de 156 nós e 3 arcos regulares, mas também é possível resolver problemas em grafos ponderados.
Além do qiskit-ibm-catalog, você também usará os seguintes pacotes para executar este exemplo: networkx e numpy. Você pode instalar esses pacotes descomentando a célula a seguir se estiver executando este exemplo em um notebook usando o kernel IPython.
# %pip install networkx numpy1. Defina o problema
Você pode resolver um problema de corte máximo definindo um problema de grafos e especificando 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:
O Solver aceita uma string como entrada de definição do problema.
# Convert graph to string
problem_as_str = nx.readwrite.json_graph.adjacency_data(maxcut_graph)2. Execute o problema
Ao usar o método de entrada baseado em gráficos, especifique o tipo de problema.
# Solve the problem
maxcut_job = solver.run(
problem=problem_as_str,
problem_type="maxcut",
backend_name=backend_name, # E.g. "ibm_fez"
)Verifique o status da sua carga de trabalho do Qiskit Function ou obtenha os resultados da seguinte maneira:
# 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. Recuperar o resultado
Recupere o valor de corte ideal do dicionário de resultados.
O mapeamento das variáveis para a cadeia de bits pode ter mudado. O dicionário de saída contém um variables_to_bitstring_index_map subdicionário, que ajuda a verificar a ordem.
# 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
Você pode verificar a precisão do resultado resolvendo o problema de forma clássica com solucionadores de código aberto, como PuLP se o gráfico não for densamente conectado. Problemas de alta densidade podem exigir solucionadores clássicos avançados para validar a solução.
Exemplo: Otimização restrita
O exemplo anterior de max-cut é um problema comum de otimização binária quadrática sem restrições. O Solucionador de Otimização do Q-CTRL também pode resolver problemas de otimização com restrições, passando as restrições rígidas diretamente para o Solucionador por meio da entrada constraint , em vez de codificá-las como termos de penalidade na função-objetivo. Atualmente, o Solver suporta restrições do tipo “ Hamming-weight-1 ”: cada restrição especifica um grupo de variáveis em que exatamente uma variável deve ser igual a 1 e as demais devem ser iguais a 0.
O exemplo a seguir demonstra como construir uma função de custo e um conjunto de restrições rígidas para um problema de otimização com restrições, a partição de grafos, atribuindo cada nó de um grafo a exatamente um dos vários grupos, ao mesmo tempo em que se minimiza o peso total das arestas cujos vértices terminais pertencem ao mesmo grupo.
Além dos pacotes qiskit-ibm-catalog e qiskit , você também usará os seguintes pacotes para executar este exemplo: numpy, networkx, e sympy. Você pode instalar esses pacotes descomentando a célula a seguir se estiver executando este exemplo em um notebook usando o kernel IPython.
# %pip install numpy networkx sympy1. Defina o problema
Defina um problema de particionamento de grafo aleatório gerando um grafo com nós ponderados aleatoriamente.
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:
Um modelo padrão de otimização para a partição de grafos ponderados pode ser formulado da seguinte maneira. Divida os nós do grafo em três grupos e defina que se o nó for atribuído ao grupo , e caso contrário. O objetivo é minimizar o peso total das arestas cujas extremidades estão atribuídas ao mesmo grupo, sendo que o peso de uma aresta é o peso combinado de suas duas extremidades, :
# 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)]
)Cada nó deve ser atribuído a exatamente um dos três grupos. Esta é uma restrição do tipo “ Hamming-weight-1 ”: para cada nó , exatamente um dos elementos deve ser igual a 1, e os demais devem ser iguais a 0:
Em vez de codificar essa restrição como um termo de penalidade na função de custo, passe-a diretamente ao Solver como uma restrição rígida usando a entrada constraint .
# 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}
Você não precisa adicionar todas as variáveis ao constraint. Qualquer variável que não seja incluída no dicionário permanece sem restrição; portanto, é possível combinar grupos de variáveis com restrições rígidas e variáveis livres no mesmo problema.
2. Execute o problema
# Solve the problem
partition_job = solver.run(
problem=srepr(cost_function),
constraint=constraint_dict,
backend_name="ibm_marrakesh", # E.g. "ibm_marrakesh"
)Verifique o status da sua carga de trabalho do Qiskit Function ou obtenha os resultados da seguinte maneira:
# 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. Obtenha o resultado
Recupere a solução e analise os resultados. O custo da solução representa o peso total das arestas cujos vértices finais acabaram no mesmo grupo; portanto, um custo menor indica um melhor particionamento do grafo.
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
Obtenha suporte
Em caso de dúvidas ou problemas, entre em contato com a Q-CTRL.
Log de mudanças
- 10/08/2026: Adicionou-se suporte para restrições rígidas (peso de Hamming 1) por meio do campo de entrada
constrainte atualizou-se o exemplo de otimização com restrições para utilizá-las. - 11/02/2026: Agora oferecemos suporte para
ibm_miami
Próximas etapas
- Solicite acesso ao Solucionador de Otimização Q-CTRL.
- Consulte a referência da API para esta função do Qiskit.
- Experimente o tutorial Solve higher-order binary optimization problems with Q-CTRL's Optimization Solver.
- Revisão Sachdeva, N., et al. (2024). A otimização quântica usando um computador quântico de 127 qubits com modelo de porta IBM pode superar os annealers quânticos em problemas de otimização binária não triviais. arXiv pré-impressão arXiv:2406.01743.
- Revisão Loco, D., et al. (2026). Previsão prática do local de hidratação do bolso proteico para a descoberta de medicamentos em um computador quântico. arXiv pré-impressão arXiv:2512.08390.
- Analise o estudo de caso da Mazda.
- Analise o estudo de caso da Network Rail.
- Analise o estudo de caso do Exército Australiano.
- Analise o estudo de caso da Transport for New South Wales.