Skip to content

Typed primal–dual runtime

QQA4CO 0.11 extends the typed, proof-aware runtime around QQA. The central rule is that representation, differentiable primal search, repair, and exact proof are separate capabilities. A factor being readable as ModelIR does not automatically make it valid for every solver.

Diagnose, plan, solve

The production entry point accepts a goal and a wall-clock duration. Run the doctor first when a model comes from another system:

import qqa

report = qqa.doctor(model, replicas=128)
print(report.explain())

result = qqa.solve(
    model,
    goal="feasible",  # best | feasible | prove | diverse | pareto
    budget="30s",  # ms, s, m, and h are accepted
    device="auto",
    seed=0,
)

The doctor checks finite QQA bounds, factor capabilities, coefficient dynamic range, large coefficients, quadratic curvature, presolve contradictions, decomposition structure, route support, estimated working memory, and an initial replica/budget recommendation. It does not execute a solver.

goal="prove" selects the certification profile. It never turns a heuristic claim into a proof: an optimal or infeasible status is returned only when the selected exact backend reports it and the original-space solution passes feasibility evaluation.

Strict factor and bound semantics

Use qqa.model.inspect_capabilities(model_ir) to inspect every factor. The machine-readable capability set includes DIFFERENTIABLE, SUBGRADIENT, PROX, GPU_KERNEL, PROPAGATE, SEPARATE, REPAIR, LOWER_BOUND, EXACT_ENCODE, and PROOF_SAFE.

Pure QQA requires a valid derivative/prox route and finite domains. Missing or infinite integer/real bounds are never replaced by a guessed box. Native all-different, table, logical, subtour, and scheduling factors can instead be routed to CP/SAT/exact propagation when their capability says so. A custom black-box factor must explicitly declare whether it is differentiable.

The runtime generates the authoritative per-model matrix through inspect_capabilities; these broad built-in classes illustrate why the columns must remain separate:

Factor class Represent Pure QQA Fused GPU Repair Lower bound Registered proof route
sparse linear yes yes yes no yes CP-SAT for compatible integer forms
sparse quadratic yes yes yes no yes no generic registered route
clause / cardinality yes yes yes yes yes CP-SAT
assignment yes yes eager yes yes CP-SAT
all-different yes no no yes no CP-SAT
precedence / no-overlap / cumulative yes yes eager yes type-dependent CP-SAT
table / logical / subtour yes no no yes type-dependent no generic registered route
scenario and DRO yes yes eager no no no generic registered route
black-box yes declared capability only no no no no

“Registered proof route” means an encoder exists; a particular model is only certifiable when its domains, coefficients, factor combination, selected API route, and optional backend are all compatible. Inspect report.factors and the executed-stage diagnostics instead of inferring execution from this table.

Feasibility has three states: feasible, infeasible, and unknown. An empty constraint report is unknown unless it was explicitly created with ConstraintReport.unconstrained().

Termination and guarantee strength are independent. For example, limit_reached_with_incumbent can still carry verified_feasible, while only a proven terminal state may carry exact. The stable guarantee vocabulary is exact, certified_bound, verified_feasible, approximate, heuristic, and unknown. A valid exact-backend termination with no incumbent remains a typed result—such as infeasible_proven or limit_reached_no_incumbent—and retains any certified bound the backend supplied.

Factor-Split QQA and the solve DAG

qqa.model.compile_execution_plan(model_ir) groups factors by concrete backend registration. CUDA selects a fused backend only where a real lowering exists; unsupported factors remain eager or fail explicitly. The returned immutable plan exposes value/gradient and constraint operations without changing the immutable ModelIR.

qqa.plan(...) now exposes a budgeted stage DAG. qqa-primal is always the population primal-search stage. LP relaxation, repair/LNS, and exact certification depend on it and are labelled as warm-state, feasibility, bound, or proof roles. Exact budget allocation adapts to model scale and structure rather than using one fixed percentage.

Scale-stable heterogeneous QQA

The default stable profiles use robust median/MAD objective scaling. Replica objectives are averaged; binary penalties are normalized by the relaxed dimension; and diversity is dimension-normalized. Raw original-space objectives remain separate in SolveResult.

The population contains convexification, exploration, discretization, noisy, incumbent, LP-centred, conflict-avoiding, and global roles. Roles receive different beta and learning-rate schedules. Coarse, Metropolis-inspired state exchange is a search-diversification heuristic: heterogeneous roles do not share one stationary distribution, so this mode makes no detailed-balance or parallel-tempering sampling claim. Exchange swaps latent and optimizer state together. For sparse QUBOs, a Gershgorin curvature bound sets the negative c=2 convexification strength and a factor-degree diagonal preconditioner rescales gradients. Negative beta adds curvature; it does not imply global convexity for an arbitrary objective.

The historical archive retains feasible and violation-ranked candidates while enforcing solution diversity. It supplies archive-centred restarts and remains available on the result for relinking, RINS-like neighborhoods, uncertainty analysis, and a solution pool. Constraint multipliers use residual-balanced Powell–Hestenes–Rockafellar updates rather than unbounded fixed penalty growth.

GPU factor runtime

qqa.gpu.compile_factor_graph compiles supported linear, quadratic, cardinality, and clause factors into device-resident parameter buffers, factor offsets, factor-to-variable indices, type IDs, and constraint metadata. Portable Torch kernels provide segmented sparse reductions, exact-k and one-hot projection, batched assignment repair, exact binary flip deltas, bounded GPU tabu/k-flip search, and batched 2-opt.

The sparse-QUBO core retains its fused custom operation, optional Triton kernel, CUDA Graph replay, and AOTInductor cache. KernelAutotuner performs a bounded first-use comparison. profile_kernel uses CUDA events and explicit event synchronization, while CPU timing uses a monotonic wall clock. Callback telemetry is buffered on the active device and transferred at solve end.

Distributed islands exchange only bounded elites. Replica, model, and scenario partitioning remain distinct policies; QQA4CO does not all-gather the full population as an implicit default.

Island deadlines include migration overhead. Elites are objective-ranked and diversity-filtered, the historical incumbent survives all rounds, no migration occurs after the final round, and the returned final population is always one that was actually evaluated.

Integral algebraic columns are never scaled. Row scaling and continuous-column scaling remain available, and ScalingFactors.preserves_integrality makes the invariant machine-readable.

Primal, dual, and exact feedback

For sparse linear algebraic models, qqa.dual.solve_lp_relaxation runs PDHG on CPU or CUDA and returns primal/dual vectors, reduced costs, KKT residuals, an optional infeasibility-ray candidate, and a dual bound only when the complete dual expression is finite. qqa.dual.crossover_lp resolves the identical LP with HiGHS dual simplex and returns a basic solution plus portable variable and row basis statuses.

ExactFeedbackBus carries versioned LP primal vectors, multipliers, reduced costs, fractionalities, branch scores, local bounds, incumbents, linear cuts, and no-goods. Cuts convert to typed constraints; no-goods convert to clause factors. The SCIP conditional heuristic publishes live node information and receives QQA incumbents and neighborhoods through this boundary.

General QUBO persistency uses bounded exact probing for small components and a valid termwise lower bound otherwise. It never labels a heuristic fixing as a proof. Exact results carry CertificateMetadata; the metadata says solver-reported-* unless an independently verifiable proof digest is actually present. Native exact adapters run in disposable processes by default so a solver ABI failure cannot terminate the caller.

CP, SAT, decomposition, global and uncertain models

The opt-in exact layer includes:

  • CP-SAT lowering for bounded binary/integer linear models, all-different, assignment, clauses, precedence, no-overlap, and cumulative resources;
  • PySAT RC2 lowering for SAT and integral weighted MaxSAT;
  • factor-graph decomposition and variable-separator detection;
  • generic Benders, column-generation, and progressive-hedging controllers;
  • McCormick envelopes and bounded spatial branch-and-bound with a caller- supplied valid relaxation bound;
  • mean, worst-case, CVaR, chance, Wasserstein, phi-divergence, and moment- ambiguity factors, scenario reduction, SAA confidence intervals, and held-out validation;
  • direct QQA acquisition optimization for black-box trust regions;
  • confidence/OOD-gated learned planning that always falls back to a deterministic planner outside its training distribution.

SAT/MaxSAT calls with a deadline run in a disposable process and terminate it at the hard wall-clock limit. Sparse QUBO neighbourhood APIs distinguish induced, conditioned, and connected-component subproblems, preventing dropped boundary energy from being mistaken for conditioning.

Study/Trial and Benchmark Hub

qqa.create_study provides resumable black-box campaigns with QQA selected by default for diverse batch acquisition. Cache identity includes seed, fidelity, replicate, and evaluator version; an optional evaluation timeout isolates and terminates a failed simulator process.

Benchmark Hub TOML/JSON manifests declare tracks, instances, checksums, budgets, seeds, solvers, and metrics without retaining local paths. Paired comparisons report wins/ties/losses, median differences, and deterministic bootstrap confidence intervals plus an exact paired sign test; holm_adjust controls family-wise error across declared comparisons. Load the portable starter manifest from the installed package, without relying on a checkout path:

from qqa.benchmarking import builtin_benchmark_manifest

manifest = builtin_benchmark_manifest("qqa-core")

Unsupported coefficients, domains, factors, or proof semantics raise an explicit exception. No route silently rounds coefficients, drops rows, or changes infinite bounds.

Events, cockpit, and decisions

Every SolveEvent uses schema version 2 and a monotone sequence number. Events cover solve boundaries, presolve, relaxation updates, candidate repair, incumbents, dual bounds, cuts, replica exchange/restart, constraint residuals, kernel profiles, and completion.

from qqa.visuals import decision_explorer, plot_optimization_cockpit

figure, _ = plot_optimization_cockpit(result, backend="matplotlib")
rows = decision_explorer(result, model_ir)

The cockpit separates primal and dual progress, phase timing, constraint residuals, and outcome. The decision explorer reports archive stability and one-coordinate counterfactual objective/violation deltas. Neither view reinterprets unknown feasibility as success.

Checkpoint and portable result packages

first = qqa.solve(
    problem,
    budget="10s",
    checkpoint_path="run.qqacp",
    checkpoint_interval=100,
)
resumed = qqa.solve(problem, budget="30s", resume_from="run.qqacp")

Checkpoint continuation reports resume_semantics="resume" when the epoch horizon is unchanged. Increasing num_epochs is explicitly reported as "extend": the saved trajectory is preserved, while future schedule and adaptive-update positions follow the new horizon and are not claimed to be bit-identical to a run that declared the longer horizon initially.

Checkpoints are atomic ZIP containers with JSON metadata, NumPy tensors, and a SHA-256 checksum for every tensor. They contain no pickle or executable code. The model fingerprint, optimizer state, CPU/CUDA RNG state, schedule state, incumbent, latent population, and historical archive are verified before resume. The loader requires the exact schema field/member set, simple tensor names, and lowercase SHA-256 digests before any tensor is decoded. Dynamics-changing options must match; a run may extend its epoch horizon. File paths are API arguments and are not written into provenance.

qqa.runtime.export_result_package creates an independently verifiable model summary/result/event bundle. Portable-payload validation rejects absolute paths, private endpoints, credentials, host/server/cluster fields, non-finite numbers, and executable objects. Verification requires exactly those three payloads plus the manifest, a checksum for every payload, valid portable JSON, and agreement between the result status and manifest.

Remote service and custom-code boundary

Install qqa[service] and construct qqa.service.create_app() for a bounded FastAPI job service. It accepts only schema-validated ModelIR dictionaries and a small allowlist of solve options. Jobs execute in a bounded process pool; tracebacks and filesystem paths are not returned. Set a bearer token at the deployment boundary and apply TLS, rate limits, request-size limits, and network policy in the reverse proxy.

Python source and pickle are trusted-local features. They are denied by default in the library, CLI, and shared GUI. A local operator must explicitly set trusted=True, pass --allow-unsafe-python, or set QQA_ALLOW_CUSTOM=1. Never enable that switch on a multi-tenant service.

Honest capability boundary

These APIs are composable solver building blocks, not a claim of universal commercial-solver parity. In particular, QQA4CO does not claim a native C++20 persistent factor runtime, a complete branch-cut-price implementation, a proof-producing CDCL/PB checker, or globally valid MINLP bounds unless the selected bound oracle supplies them. Benchmark claims must name the snapshot, hash, hardware-neutral budget, baselines, seeds, feasibility policy, and statistical interval. The audited registry verifies origins and hashes; it does not manufacture performance claims.