SQOA Solver - uma Qiskit Function da JIJ
Veja a referência da API.
As Qiskit Functions 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) Plan. Elas estão em status de versão preliminar (preview) e sujeitas a alterações.
Visão geral
Com o SQOA Solver da JIJ, você pode resolver grandes problemas de otimização combinatória em computadores quânticos IBM®. O solver é baseado no Sampling-based Quantum Optimization Algorithm with Quantum Relaxation (SQOA-QR) [1]. Diferentemente dos algoritmos quânticos que usam um mapeamento um-para-um entre variáveis de decisão clássicas e qubits, o SQOA Solver consegue lidar com problemas de otimização com mais variáveis do que o número de qubits disponíveis nos dispositivos quânticos atuais. Seu design baseado em amostragem também o torna menos sensível ao ruído físico do hardware quântico atual. Juntas, essas características tornam o SQOA Solver adequado para problemas de otimização nesta etapa da computação quântica.
O solver trata problemas de Quadratic Unconstrained Binary Optimization (QUBO) dados como uma formulação matemática clássica e mapeia automaticamente essa formulação clássica para o cenário quântico. Você também pode converter problemas com restrições em problemas sem restrições usando o método de penalidade.
As seções a seguir descrevem o workflow do solver, os resultados de benchmark e exemplos de ponta a ponta que modelam problemas com o qiskit-addon-opt-mapper e, opcionalmente, com o Open Mathematical prograMming eXchange (OMMX). O OMMX é um formato de dados aberto e um SDK que simplifica a troca de problemas de otimização entre ferramentas de software.
Descrição
O SQOA Solver implementa o Sampling-based Quantum Optimization Algorithm with Quantum Relaxation (SQOA-QR) e o envolve com pré-processamento e pós-processamento clássicos. Esta seção explica o framework SQOA e como ele difere do Quantum Approximate Optimization Algorithm (QAOA).
A figura a seguir mostra um esquema do workflow do SQOA Solver.
O framework SQOA
O SQOA é um framework baseado em amostragem para otimização combinatória [1]. Ele combina duas técnicas: Quantum Random Access Optimization (QRAO) [2], que codifica o problema em um relaxamento quântico, e diagonalização quântica baseada em amostras (SQD) [3], também conhecida como quantum-selected configuration interaction (QSCI) [4], que usa o computador quântico apenas para amostrar bitstrings e encontra o estado de baixa energia em um computador clássico. O SQOA é um framework no sentido de que fixa essa combinação, mas deixa os detalhes para cada implementação, como a forma de atribuir as variáveis aos qubits e quais circuitos geram as amostras.
-
Relaxamento quântico com QRAO. O QAOA e outras abordagens baseadas em Ising representam cada variável binária pelo operador do seu qubit. O QRAO também usa os operadores e . Com um código de acesso aleatório quântico (QRAC) (3,1), até três variáveis binárias compartilham um qubit, e cada variável é representada por um operador de Pauli diferente.
O QUBO então se torna um Hamiltoniano relaxado que precisa de menos qubits do que o Hamiltoniano de Ising. Variáveis que interagem no QUBO devem ser colocadas em qubits diferentes. Se compartilhassem um qubit, o produto de seus operadores de Pauli se reduziria a um único operador nesse qubit (por exemplo, ), e o Hamiltoniano relaxado deixaria de representar a interação entre elas.
A atribuição de variáveis a qubits determina, portanto, tanto o número de qubits quanto a qualidade do relaxamento, e o SQOA Solver projeta essa atribuição em seu pré-processamento.
-
Diagonalização quântica baseada em amostras (SQD). O computador quântico primeiro prepara um estado quântico que tem sobreposição com estados de baixa energia do Hamiltoniano relaxado e depois amostra bitstrings dele. Um computador clássico projeta o Hamiltoniano relaxado no subespaço gerado pelos bitstrings amostrados e o diagonaliza para aproximar o estado fundamental.
Como o Hamiltoniano relaxado não é diagonal, a diagonalização combina os bitstrings amostrados em uma superposição. Com um Hamiltoniano de Ising diagonal, todo bitstring já é um autoestado, e a diagonalização apenas selecionaria a melhor amostra. A mesma abordagem foi aplicada a problemas de grande escala em química quântica.
O estado fundamental aproximado obtido pelo SQD não corresponde diretamente a uma solução clássica do problema original, então a solução clássica precisa ser decodificada a partir dele. Como esse estado é calculado em um computador clássico, a decodificação, chamada de arredondamento, é um pós-processamento clássico. O SQOA Solver usa técnicas de decodificação projetadas para obter melhores soluções clássicas de forma heurística.
A tabela a seguir compara o SQOA com o QAOA, uma abordagem variacional comum para a mesma classe de problemas.
| QAOA | SQOA | |
|---|---|---|
| Qubits para variáveis binárias | Um qubit comporta até três das variáveis. O número exato depende da estrutura do problema | |
| Uso da QPU | Estima repetidamente a energia de circuitos parametrizados dentro de um laço de otimização clássico | Apenas amostra bitstrings. Nenhum parâmetro de circuito é treinado na QPU |
| Das amostras a uma solução | Seleciona o melhor bitstring medido | Diagonaliza o Hamiltoniano relaxado no subespaço amostrado em um computador clássico e arredonda o estado resultante |
Em torno do SQOA-QR, o SQOA Solver adiciona processamento clássico. Seu pré-processamento fixa as variáveis cujos valores ótimos podem ser determinados antecipadamente e projeta a atribuição de variáveis a qubits. Seu pós-processamento refina a solução arredondada com uma busca local, que você pode desativar com postprocessing_level=0.
Benchmarks
O desempenho pode depender tanto da instância do problema quanto das etapas de processamento posteriores. Em alguns casos, amostras clássicas e amostras geradas quanticamente podem alcançar uma qualidade de solução final semelhante após um pós-processamento equivalente. Portanto, a avaliação deve considerar o workflow de otimização completo.
As métricas de benchmark a seguir dão uma indicação geral da qualidade da solução, da compressão de qubits e do tempo de execução em tamanhos e estruturas de problema selecionados. A tabela lista uma seleção de instâncias de Maximum Independent Set da Quantum Optimization Benchmarking Library (QOBLIB) e duas instâncias de MaxCut 3-regulares. Você pode carregar as instâncias da QOBLIB como objetos ommx.v1.Instance, junto com suas soluções ótimas, usando o pacote ommx-quantum-benchmarks.
Cada instância foi executada 10 vezes em um processador IBM Heron r2. Number of qubits conta os qubits realmente usados no hardware após o pré-processamento e a codificação QRAC. A taxa de compressão depende da instância. Approximation ratio (AR) informa a qualidade da melhor solução encontrada nas 10 execuções:
em que é o melhor valor objetivo encontrado e é o valor objetivo ótimo conhecido. Todas as instâncias são problemas de maximização com valor ótimo positivo, então o AR é no máximo 100%, e 100% na coluna Approximation ratio indica que o solver encontrou uma solução ótima. Total time é o tempo médio combinado de QPU e CPU por execução. QPU time é o uso médio do IBM Quantum Compute por execução.
| Instância | Número de variáveis | Número de qubits | Razão de aproximação | Tempo total (segundos) | Tempo de QPU (segundos) |
|---|---|---|---|---|---|
| maxcut_384_nodes | 384 | 129 | 98.5% | 62.1 | 12.0 |
| gen200_p0-9_44 | 200 | 67 | 100% | 38.0 | 12.0 |
| es60fst02 | 186 | 42 | 100% | 24.7 | 12.0 |
| es60fst04 | 162 | 29 | 100% | 17.9 | 12.0 |
| maxcut_150_nodes | 150 | 51 | 100% | 23.1 | 12.0 |
| sloane_1dc_128 | 128 | 37 | 93.8% | 19.1 | 12.0 |
| sloane_2dc_128 | 128 | 8 | 100% | 12.3 | 11.5 |
| C125-9 | 125 | 43 | 100% | 22.3 | 12.0 |
| es60fst01 | 123 | 12 | 100% | 13.6 | 12.0 |
| es60fst03 | 113 | 26 | 100% | 14.9 | 12.0 |
| sloane_1dc_64 | 64 | 16 | 100% | 13.4 | 11.9 |
| chesapeake | 39 | 13 | 100% | 13.1 | 12.0 |
| football | 35 | 6 | 100% | 12.5 | 12.0 |
| karate | 34 | 2 | 100% | 6.2 | 6.0 |
| ibm32 | 32 | 11 | 100% | 12.9 | 12.0 |
| farm | 17 | 4 | 100% | 8.5 | 8.0 |
Primeiros passos
Encontre as informações de acesso a esta Qiskit Function no Qiskit Functions Catalog.
Esta seção mostra como autenticar e carregar a função, e como preparar um problema no formato de dicionário aceito pelo SQOA Solver usando OptimizationProblem do qiskit-addon-opt-mapper. Os exemplos seguintes executam a função e leem o resultado.
Autentique e carregue a função
Primeiro, autentique-se com sua chave de API do IBM Quantum. (O código a seguir pressupõe que você já salvou sua conta no seu ambiente local.) Em seguida, crie um cliente de catálogo da seguinte forma:
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"jijmodeling": "jijmodeling", "networkx": "networkx", "ommx": "ommx", "qiskit_addon_opt_mapper": "qiskit-addon-opt-mapper", "qiskit_ibm_catalog": "qiskit-ibm-catalog"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit_ibm_catalog import QiskitFunctionsCatalog
catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")
# verify that you have access to the function
catalog.list()
[QiskitFunction(jij/sqoa-solver)]
solver = catalog.load("jij/sqoa-solver")
Prepare a entrada com o qiskit-addon-opt-mapper
Este guia modela problemas com a classe OptimizationProblem do qiskit-addon-opt-mapper. O SQOA Solver aceita um problema QUBO como um dicionário de coeficientes no formato de tupla-string usado pelas funções de otimização da IBM, então todos os exemplos deste guia seguem o mesmo padrão de preparação no lado do cliente:
-
Escreva o modelo. Crie um
OptimizationProblem, adicione variáveis de decisão binárias e defina o objetivo e quaisquer restrições. -
Converta para QUBO. Converta o problema com
OptimizationProblemToQubo, que adiciona as restrições ao objetivo como termos de penalidade e retorna um problema de minimização equivalente sem restrições. -
Converta para um dicionário. Passe o QUBO para a função auxiliar
to_problem_dict()definida na próxima seção e passe o resultado parasolver.run()comoproblem, junto comvariable_type="binary".
O SQOA Solver sempre minimiza um problema em formato de dicionário, o que corresponde ao problema de minimização que OptimizationProblemToQubo retorna. Para um problema de maximização, o objective_value retornado é, portanto, o negativo do objetivo original. Para ler o resultado em termos do problema original, avalie a atribuição retornada com OptimizationProblem, como mostram os exemplos. Para detalhes do formato de dicionário, consulte Accepted problem formats na referência da API.
# %pip install qiskit-addon-opt-mapper
Defina a seguinte função auxiliar uma vez e reutilize-a em todos os exemplos. Ela converte o QUBO retornado por OptimizationProblemToQubo para o formato de dicionário. As chaves usam os índices de variáveis do QUBO, de modo que as chaves de solução x_0, x_1 e assim por diante no resultado correspondem a esses índices. Toda variável recebe uma entrada linear, mesmo com coeficiente zero, para que a solução retornada cubra todas as variáveis.
from qiskit_addon_opt_mapper import OptimizationProblem
from qiskit_addon_opt_mapper.converters import OptimizationProblemToQubo
def to_problem_dict(qubo: OptimizationProblem) -> dict[str, float]:
"""Convert a QUBO from OptimizationProblemToQubo to the SQOA Solver dictionary format."""
objective = qubo.objective
payload = {"()": objective.constant}
# Keep every variable in the problem, even with a zero linear coefficient.
payload.update({f"({i},)": 0.0 for i in range(qubo.get_num_vars())})
for i, coefficient in objective.linear.to_dict().items():
payload[f"({i},)"] += coefficient
for (i, j), coefficient in objective.quadratic.to_dict().items():
# Fold a binary diagonal term x_i * x_i = x_i into the linear term.
key = f"({i},)" if i == j else f"({i}, {j})"
payload[key] = payload.get(key, 0.0) + coefficient
return {key: float(value) for key, value in payload.items()}
Exemplo 1: MaxCut em um grafo 3-regular
Muitos problemas de grafos, como o MaxCut, são NP-difíceis e são candidatos naturais à otimização quântica. Instale o networkx como dependência adicional para executar este exemplo:
# %pip install networkx
1. Escreva o modelo
Atribua uma variável binária a cada vértice de um grafo para indicar seu lado do corte. Uma aresta é cortada quando suas extremidades estão em lados diferentes, então o tamanho do corte é:
Este objetivo já é um QUBO sem restrições, então OptimizationProblemToQubo apenas transforma a maximização na minimização equivalente e mantém as variáveis inalteradas. A classe de aplicação Maxcut em qiskit_addon_opt_mapper.applications constrói o mesmo modelo com Maxcut(G).to_optimization_problem().
import networkx as nx
G = nx.random_regular_graph(3, 40, seed=42)
linear = {v: 0 for v in G.nodes}
quadratic = {}
for u, v in G.edges:
linear[u] += 1
linear[v] += 1
quadratic[(u, v)] = -2
maxcut = OptimizationProblem("MaxCut")
maxcut.binary_var_list(G.number_of_nodes(), name="x")
maxcut.maximize(linear=linear, quadratic=quadratic)
2. Execute o solver
Converta o modelo para um QUBO e depois para um dicionário, e envie-o. problem e variable_type são obrigatórios. backend_name é opcional. Quando é omitido ou None, a função seleciona a QPU operacional menos ocupada disponível para a sua instância.
O dicionário options aceita shots, seed, session, postprocessing_level e job_tags. Para descrições e valores padrão, consulte a referência da API.
qubo = OptimizationProblemToQubo().convert(maxcut)
job = solver.run(
problem=to_problem_dict(qubo),
variable_type="binary",
backend_name="ibm_fez",
options={
"shots": 4096,
"seed": 42,
},
)
3. Obtenha o resultado
job.result() retorna um dicionário com quatro chaves:
-
solutioné a atribuição completa das variáveis, indexada porx_i, em queié o índice da variável (por exemplo,x_3). -
objective_valueefeasiblesão o objetivo e a viabilidade dessa atribuição, avaliados no dicionário enviado. -
metadatacontémsolver_infoeresource_usage.
Como o QUBO é a forma de minimização do MaxCut, objective_value é o negativo do tamanho do corte. Avalie a atribuição no OptimizationProblem original para ler o tamanho do corte diretamente.
result = job.result()
x = [result["solution"][f"x_{i}"] for i in range(maxcut.get_num_vars())]
print(f"Cut value: {maxcut.objective.evaluate(x)}")
print(f"Nodes on side 1: {[v for v, value in enumerate(x) if value > 0.5]}")
Cut value: 55.0
Nodes on side 1: [5, 7, 9, 11, 14, 16, 17, 19, 21, 23, 24, 26, 30, 31, 33, 34, 36, 37, 38]
4. Verifique o uso de recursos
result["metadata"]["resource_usage"] informa o tempo gasto em cada fase de execução. As fases diferentes de RUNNING: EXECUTING_QPU informam CPU_TIME, o tempo real (wall-clock) da fase em segundos, quando disponível.
A fase RUNNING: EXECUTING_QPU informa QPU_TIME, o uso de QPU de todos os jobs do Quantum Compute da execução, se o uso final estiver disponível para cada job. Para detalhes, consulte a referência da API.
for phase, metrics in result["metadata"]["resource_usage"].items():
line = f"{phase:36s}"
if "CPU_TIME" in metrics:
line += f" CPU {metrics['CPU_TIME']:8.1f} s"
if "QPU_TIME" in metrics:
line += f" QPU {metrics['QPU_TIME']:8.1f} s"
print(line)
RUNNING: MAPPING CPU 3.2 s
RUNNING: OPTIMIZING_FOR_HARDWARE CPU 5.0 s
RUNNING: WAITING_FOR_QPU CPU 1.1 s
RUNNING: EXECUTING_QPU QPU 3.0 s
RUNNING: POST_PROCESSING CPU 0.9 s
Exemplo 2: Converta um problema com restrições para QUBO
O SQOA Solver aceita problemas sem restrições. Este exemplo mostra como converter um problema com restrições em um QUBO com OptimizationProblemToQubo e mapear o resultado de volta para as variáveis do problema original.
selection = OptimizationProblem("ConstrainedExample")
selection.binary_var_list(2, name="x")
selection.maximize(linear={"x0": 1, "x1": 1})
selection.linear_constraint({"x0": 1, "x1": 1}, "<=", 1, name="at_most_one")
print(selection.prettyprint())
Problem name: ConstrainedExample
Maximize
x0 + x1
Subject to
Linear constraints (1)
x0 + x1 <= 1 'at_most_one'
Binary variables (2)
x0 x1
OptimizationProblemToQubo adiciona as restrições ao objetivo como termos de penalidade e converte o problema para minimização. O peso de penalidade apropriado depende do problema.
Após a execução, interpret() mapeia a atribuição do QUBO de volta para as variáveis do problema original.
Avalie o objetivo e a viabilidade no problema original. Para um problema em formato de dicionário, o campo feasible do resultado se refere apenas ao domínio das variáveis, porque o dicionário não tem restrições.
converter = OptimizationProblemToQubo(penalty=4.0)
qubo = converter.convert(selection)
job = solver.run(
problem=to_problem_dict(qubo),
variable_type="binary",
backend_name="ibm_fez",
)
result = job.result()
x_qubo = [result["solution"][f"x_{i}"] for i in range(qubo.get_num_vars())]
x = converter.interpret(x_qubo)
print("Objective:", selection.objective.evaluate(x))
print("Feasible:", selection.is_feasible(x))
print("x values:", x)
Objective: 1.0
Feasible: True
x values: [1. 0.]
Exemplo 3: Envie uma instância OMMX
O SQOA Solver também aceita um ommx.v1.Instance do SDK OMMX, que mantém juntos, com o problema, os nomes das variáveis, os subscritos e os metadados de conversão. Em comparação com um dicionário, uma entrada OMMX tem as seguintes vantagens:
-
O resultado também contém o
ommx.v1.Solutioncanônico emsolver_info["ommx_solution"]. -
objective_valueé avaliado no sentido da instância, então o objetivo de um problema de maximização não é negado. -
Para uma instância convertida no local com
Instance.to_qubo(),feasibleé avaliado em relação às restrições originais.
Este exemplo constrói o modelo MaxCut do Exemplo 1 com o JijModeling, um modelador de otimização matemática que compila modelos em instâncias OMMX. Instale o JijModeling, que também instala o pacote ommx:
# %pip install "jijmodeling~=2.8.0"
O SQOA Solver troca dados OMMX como strings Base64 padrão com preenchimento. Defina as seguintes funções auxiliares para essa conversão:
-
to_problem_payload()serializa umommx.v1.Instancena string que você passa comoproblem. -
to_ommx_solution()decodifica oommx.v1.Solutioncanônico a partir do resultado.
import base64
from ommx.v1 import Instance, Solution
def to_problem_payload(instance: Instance) -> str:
"""Serialize an OMMX instance into the `problem` string for SQOA Solver."""
return base64.b64encode(instance.to_bytes()).decode()
def to_ommx_solution(result: dict) -> Solution:
"""Decode the canonical OMMX solution from a SQOA Solver result."""
payload = result["metadata"]["solver_info"]["ommx_solution"]
return Solution.from_bytes(base64.b64decode(payload, validate=True))
1. Escreva o modelo com o JijModeling
A formulação de spin usual do MaxCut em um grafo atribui a cada vértice. Uma aresta é cortada quando , então o tamanho do corte é:
O JijModeling não oferece uma variável de decisão de spin nativa, e o SQOA Solver aceita variáveis QUBO binárias. Portanto, declare e defina a expressão com valor de spin dentro do modelo. Com essa expressão, o objetivo permanece na forma de spin padrão, e a instância compilada continua sendo um QUBO binário.
import jijmodeling as jm
@jm.Problem.define("MaxCut", sense=jm.ProblemSense.MAXIMIZE)
def maxcut_model(problem: jm.DecoratedProblem):
V = problem.Natural() # number of nodes
E = problem.Graph(dtype=V) # edge list
b = problem.BinaryVar(shape=(V,))
s = 1 - 2 * b # spin-valued expression
problem += jm.sum((1 - s[u] * s[v]) / 2 for u, v in E)
Se você exibir maxcut_model em um notebook, ele mostrará a seguinte formulação:
2. Compile a instância e execute o solver
Associe o grafo do Exemplo 1 para compilar um ommx.v1.Instance e envie-o com variable_type="binary". Uma instância OMMX é sempre enviada como um problema binário.
instance = maxcut_model.eval(
{"V": G.number_of_nodes(), "E": [list(edge) for edge in G.edges]}
)
job = solver.run(
problem=to_problem_payload(instance),
variable_type="binary",
backend_name="ibm_fez",
)
3. Obtenha o resultado
Para uma entrada OMMX, objective_value é o tamanho do corte, e to_ommx_solution() decodifica a solução canônica para que você possa ler a atribuição pelo nome da variável.
result = job.result()
print(f"Cut value: {result['objective_value']}")
solution = to_ommx_solution(result)
b_values = solution.extract_decision_variables("b")
print(
f"Nodes on side 1: {sorted(v for (v,), value in b_values.items() if value > 0.5)}"
)
Cut value: 55.0
Nodes on side 1: [5, 7, 9, 11, 14, 16, 17, 19, 21, 23, 24, 26, 30, 31, 33, 34, 36, 37, 38]
Obtenha suporte
Para suporte, envie um e-mail para info@j-ij.com.
Próximos passos
- Solicite uma avaliação gratuita do SQOA Solver preenchendo este formulário.
- Visite a referência da API desta Qiskit Function.
- Experimente um exemplo de ponta a ponta no tutorial do problema do conjunto independente máximo.
- Aprenda a modelar problemas de otimização com o qiskit-addon-opt-mapper.
- Aprenda a modelar e trocar problemas de otimização com o JijModeling e o OMMX.
- Leia o artigo do SQOA-QR [1] para saber mais sobre o framework SQOA.
Referências
[1] Matsuyama, H., & Yamashiro, Y. (2025). Sampling-based quantum optimization algorithm with quantum relaxation. 2025 IEEE International Conference on Quantum Computing and Engineering (QCE), 1, 2323–2333. DOI, arXiv:2504.12629
[2] Fuller, B., Hadfield, C., Glick, J. R., Imamichi, T., Itoko, T., Thompson, R. J., Jiao, Y., Kagele, M. M., Blom-Schieber, A. W., Raymond, R., et al. (2024). Approximate solutions of combinatorial problems via quantum relaxations. IEEE Transactions on Quantum Engineering, 5, 1–15. DOI, arXiv:2111.03167
[3] Robledo-Moreno, J., Motta, M., Haas, H., Javadi-Abhari, A., Jurcevic, P., Kirby, W., et al. (2025). Chemistry beyond the scale of exact diagonalization on a quantum-centric supercomputer. Science Advances, 11(25), eadu9991. DOI, arXiv:2405.05068
[4] Kanno, K., Kohda, M., Imai, R., Koh, S., Mitarai, K., Mizukami, W., & Nakagawa, Y. O. (2026). Quantum-selected configuration interaction: Classical diagonalization of Hamiltonians in subspaces selected by quantum computers. Physical Review Research, 8(2), 023268. DOI, arXiv:2302.11320