09. When should we trust the answer?

An optimization result involves three separate questions:

  1. Why did the algorithm stop?
  2. Does a usable primal or dual result exist, and how well does it satisfy the implemented model?
  3. Does the implemented model represent the intended mathematics?

A termination status addresses the first question. Separate primal and dual result statuses say whether corresponding results are available. Residuals and an independent model check are still needed for the second and third.

Can we verify the answer ourselves?

using LearnSDP
using Clarabel
using LinearAlgebra

W = course_graph()
problem = maxcut_problem(W)
result = solve_sdp(problem, Clarabel.Optimizer)
report = audit_solution(result.model, problem, result.numerical_X)

(termination = report.termination,
 primal_status = report.primal_status,
 dual_status = report.dual_status,
 equality_residual = report.equality_residual,
 symmetry_residual = report.symmetry_residual,
 minimum_eigenvalue = report.minimum_eigenvalue,
 relative_gap = report.relative_gap)
(termination = MathOptInterface.OPTIMAL, primal_status = MathOptInterface.FEASIBLE_POINT, dual_status = MathOptInterface.FEASIBLE_POINT, equality_residual = 0.0, symmetry_residual = 0.0, minimum_eigenvalue = 5.525347939469454e-9, relative_gap = 4.1710625595469897e-10)

The audit evaluates the original equality matrices and eigendecomposes the symmetrized result. It does not infer feasibility from OPTIMAL.

Why does scaling change numerical behavior?

Multiplying one equality and its right-hand side by one million leaves the feasible set unchanged. Predict whether it leaves every raw residual unchanged.

scaled_A = copy(problem.A)
scaled_b = copy(problem.b)
scaled_A[1] = 1e6 .* scaled_A[1]
scaled_b[1] *= 1e6
scaled_problem = StandardSDP(problem.C, scaled_A, scaled_b)

scaled_result = solve_sdp(scaled_problem, Clarabel.Optimizer)
scaled_report = audit_solution(
    scaled_result.model,
    scaled_problem,
    scaled_result.numerical_X,
)

(original_bound = -report.primal_objective,
 scaled_bound = -scaled_report.primal_objective,
 original_raw_equality_residual = report.equality_residual,
 scaled_raw_equality_residual = scaled_report.equality_residual,
 scaled_psd_violation = scaled_report.psd_violation)
(original_bound = 23.583132070108665, scaled_bound = 23.583132206024658, original_raw_equality_residual = 0.0, scaled_raw_equality_residual = 1.1641532182693481e-10, scaled_psd_violation = 0.0)

Raw residuals carry units and scale. A residual of 1e-6 may be excellent for a constraint whose natural magnitude is 1e6 and unacceptable for one whose natural magnitude is 1e-9. A defensible audit reports both the measurement and the tolerance or normalization used to interpret it.

Is a tiny negative eigenvalue a failure?

nearly_psd = [1.0 1.0 + 3e-9; 1.0 + 3e-9 1.0]
λmin = eigmin(Symmetric(nearly_psd))

(minimum_eigenvalue = λmin,
 feasible_at_1e_8 = λmin >= -1e-8,
 feasible_at_1e_10 = λmin >= -1e-10)
(minimum_eigenvalue = -3.0000000039720448e-9, feasible_at_1e_8 = true, feasible_at_1e_10 = false)

The mathematical matrix is indefinite. The two numerical decisions differ because they answer whether its violation is acceptable for two stated tolerances.

What if the solver does not say optimal?

OPTIMAL, an iteration limit, suspected infeasibility, and a numerical error do not support the same next action. Before reading variable values, check whether a primal result actually exists. If a model is reported infeasible, inspect the formulation and, when available, validate an infeasibility certificate. If an iteration limit is reached with a candidate point, report its residuals without calling it optimal.

Solver tolerances are stopping rules, not edits to the mathematical feasible set. Tightening them can increase work or expose poor scaling; it cannot turn an ill-posed model into a sound one. Comparing solvers can diagnose sensitivity, but agreement between two implementations is still numerical evidence rather than a symbolic proof.

NoteGo further: statuses and tolerances

MathOptInterface documents the distinction between termination and result statuses. Clarabel’s official solver settings name its absolute and relative gap, feasibility, and iteration tolerances. Read these when interpreting a run, after defining the scale-aware checks your model needs.

What should a defensible result say?

A useful report names:

  • termination, primal, and dual statuses;
  • the largest original equality residual;
  • symmetry error and minimum eigenvalue;
  • primal-dual gap when available;
  • the acceptance tolerance and relevant scale;
  • one independent check of the model encoding.

Avoid “the solver proved the matrix is PSD.” Prefer: “The returned point has a PSD violation of \(3\times10^{-9}\) and is accepted as numerically feasible at tolerance \(10^{-8}\).”

Once a matrix has passed those checks, one problem remains: it is a relaxed geometric object, not a two-slot schedule. The final chapter must turn it back into a feasible decision without confusing a randomized result with a proof.

Try it yourself

  1. Replace 1e6 by 1e10 and record which diagnostics change materially.
  2. Audit an intentionally nonsymmetric perturbation of the returned matrix.
  3. Write two conclusions for the same result using tolerances 1e-6 and 1e-10.
  4. Complete learner/09_trust_the_computation.jl.