The solver handle¶
solve() builds SDPB’s solver, runs it, and throws it away. A
Solver keeps it alive:
solver = pmp.solver(options=None, **overrides) # also lmi.solver(...)
Construction converts the problem to an SDP and allocates the initial iterate
(\(x = y = 0\), \(X\) and \(Y\) multiples of the identity, or a checkpoint from
checkpoint_dir). Use it as a context manager, or call close(), to free the
C++ objects; a closed handle raises SDPBError on use.
Repeated runs¶
with pmp.solver(duality_gap_threshold="1e-30") as solver:
first = solver.run(max_iterations=20) # MAX_ITERATIONS_EXCEEDED
final = solver.run() # continues from iteration 21
solver.total_iterations # 20 + final.iterations
run() continues the interior-point iteration from the current
\((x, X, y, Y)\) with the options given (the handle’s options, overridden per
call). The trajectory is the same as an uninterrupted run, so splitting a solve
into stages costs nothing. Typical uses: tightening thresholds after a coarse
solve, extending the iteration budget, or changing want between stages.
state() returns the current iterate as a Solution
without iterating (its status is that of the last run).
Warm starts¶
reference = pmp.solve(want=("y", "X", "Y"))
with pmp.solver() as solver:
solver.warm_start(y=reference.y, X=reference.X, Y=reference.Y)
solution = solver.run() # converges in a few iterations
warm_start overwrites any of y (length N), X and Y (per block a pair
(even, odd) of matrices in the shapes SDPB uses, as returned in a
Solution). Shapes are checked. Note that a good warm start needs \(X\) and
\(Y\) as well as \(y\): setting only y with the default \(X, Y\) is not faster.
Checkpoints¶
solver.save_checkpoint("ck") # binary checkpoint_<g>_<rank> + checkpoint.json
later = pmp.solver(checkpoint_dir="ck") # loads it
This is the same format the sdpb executable writes; the two are
interchangeable at equal precision and rank count.
Interrupting¶
Pressing Ctrl-C (SIGINT) while run() is executing does not kill the process.
sdpb-python asks SDPB to stop, which it does at the end of the current
iteration, and run() raises SolverInterrupted, a
KeyboardInterrupt subclass:
try:
solution = solver.run()
except sdpb.SolverInterrupted as e:
partial = e.solution # state at the interruption, status SIGTERM_RECEIVED
solver.save_checkpoint("ck") # the handle is still usable
Because SDPB checks for the request once per iteration, a large problem can take one full iteration (seconds to minutes) to react. Python’s own SIGINT handler is restored after every run.
Without a handle (plain solve()), Ctrl-C likewise stops SDPB cleanly; the
returned Solution then has status SIGTERM_RECEIVED.