Warm Starting¶
Warm starting accelerates convergence when solving a sequence of related problems. By initializing the solver from a previous solution, the interior-point method starts closer to the optimum and typically converges in fewer iterations.
When to Use Warm Starting¶
Warm starting is useful when you have:
A sequence of problems with slowly varying parameters (e.g., MPC time steps)
Sensitivity analysis where you perturb
qorbslightlyIterative algorithms that solve similar subproblems repeatedly
# Solve the initial problem
solver = moreau.CompiledSolver(n=n, m=m, ..., settings=settings)
solver.setup(P_values, A_values)
sol = solver.solve(qs=q_batch, bs=b_batch)
# Create warm start from previous solution
ws = sol.to_warm_start()
# Re-solve with perturbed parameters, warm starting from previous solution
sol2 = solver.solve(qs=q_perturbed, bs=b_perturbed, warm_start=ws)
API¶
The CompiledSolver.solve() method accepts an optional warm_start argument:
# Get warm start from a previous solution
ws = prev_solution.to_warm_start() # BatchedSolution -> BatchedWarmStart
solution = solver.solve(
qs=q_batch, # (batch, n) linear cost vectors
bs=b_batch, # (batch, m) constraint RHS vectors
warm_start=ws, # BatchedWarmStart or WarmStart object
)
The warm_start parameter accepts a WarmStart (from Solution.to_warm_start()) or BatchedWarmStart (from BatchedSolution.to_warm_start()).
Example: Perturbed QP¶
import moreau
import numpy as np
# Problem structure
cones = moreau.Cones(num_zero_cones=1, num_nonneg_cones=4)
settings = moreau.Settings(batch_size=1)
solver = moreau.CompiledSolver(
n=2, m=5,
P_row_offsets=[0, 1, 2], P_col_indices=[0, 1],
A_row_offsets=[0, 2, 3, 4, 5, 6],
A_col_indices=[0, 1, 0, 1, 0, 1],
cones=cones, settings=settings,
)
solver.setup(
P_values=[2.0, 2.0],
A_values=[1.0, 1.0, -1.0, -1.0, 1.0, 1.0],
)
q = np.array([[1.0, -1.0]])
b = np.array([[1.0, 0.0, 0.0, 1.0, 1.0]])
# Cold solve
sol = solver.solve(qs=q, bs=b)
cold_iters = solver.info.iterations[0]
# Warm solve (same problem — converges faster)
ws = sol.to_warm_start()
sol_warm = solver.solve(qs=q, bs=b, warm_start=ws)
warm_iters = solver.info.iterations[0]
print(f"Cold: {cold_iters} iterations, Warm: {warm_iters} iterations")
Batched Warm Starting¶
Warm start works with batched problems. Each problem in the batch gets its own warm start point:
settings = moreau.Settings(batch_size=64)
solver = moreau.CompiledSolver(n=n, m=m, ..., settings=settings)
solver.setup(P_values, A_values)
# Solve first batch
sol = solver.solve(qs=q_batch, bs=b_batch)
# Warm start next batch from previous solutions
ws = sol.to_warm_start() # BatchedWarmStart with shape (64, n/m)
sol2 = solver.solve(qs=q_batch_2, bs=b_batch_2, warm_start=ws)
Shape Requirements¶
Type |
Field |
Shape |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A WarmStart passed to a batched solver is automatically broadcast for batch_size=1.
How It Works¶
When warm start vectors are provided, the solver:
Scales the warm start point into the equilibrated (internal) problem space
Computes a warmness ratio from the residuals and duality gap at the warm start point
Applies central-path smoothing to push the point onto the central path, ensuring the iterate is interior to the cones
Starts the IPM from this smoothed point instead of the default initialization
The warm start point should be in the original (unscaled) problem space — typically a previous Solution’s x, z, s arrays.
Auto-Retry on Failure¶
If a warm-started solve produces a failure status (anything other than Solved, AlmostSolved, MaxIterations, or CallbackTerminated), the solver automatically retries without warm start and emits a warning.
You can customize which statuses are considered acceptable (i.e. do not trigger a cold retry) via IPMSettings.warm_start_no_retry:
from moreau import IPMSettings, SolverStatus
# Also accept MaxTime without retrying cold
ipm = moreau.IPMSettings(
warm_start_no_retry=frozenset({
SolverStatus.Solved,
SolverStatus.AlmostSolved,
SolverStatus.MaxIterations,
SolverStatus.MaxTime,
SolverStatus.CallbackTerminated,
})
)
Single Problem Warm Starting¶
The Solver class also supports warm starting:
import moreau
import numpy as np
from scipy import sparse
P = sparse.diags([1.0, 1.0], format='csr')
q = np.array([2.0, 1.0])
A = sparse.csr_array([[1.0, 1.0], [1.0, 0.0], [0.0, 1.0]])
b = np.array([1.0, 0.7, 0.7])
cones = moreau.Cones(num_zero_cones=1, num_nonneg_cones=2)
solver = moreau.Solver(P, q, A, b, cones=cones)
solution = solver.solve()
# Warm start a new solver with slightly different q
ws = solution.to_warm_start() # Solution -> WarmStart
solver2 = moreau.Solver(P, q + 0.1, A, b, cones=cones)
solution2 = solver2.solve(warm_start=ws)
PyTorch and JAX Warm Starting¶
Both moreau.torch.Solver and moreau.jax.Solver support warm starting:
PyTorch¶
from moreau.torch import Solver
import torch
solver = Solver(n=2, m=3, ..., cones=cones)
solver.setup(P_values, A_values)
solution = solver.solve(q, b)
ws = solution.to_warm_start()
# Re-solve with perturbed parameters
solution2 = solver.solve(q + 0.1, b, warm_start=ws)
JAX¶
from moreau.jax import Solver
import jax.numpy as jnp
solver = Solver(n=2, m=3, ..., cones=cones)
solver.setup(P_data, A_data)
solution = solver.solve(q, b)
ws = solution.to_warm_start()
# Re-solve with perturbed parameters
solution2 = solver.solve(q + 0.1, b, warm_start=ws)
Note
When using the functional API (moreau.jax.solver()), warm starting is not
supported — use the class-based Solver API instead.
Notes¶
Bad warm starts are safe: A poor warm start (e.g., random values) will still converge — it just won’t reduce iterations
CPU and CUDA: Warm starting is supported on both backends
Unchanged structure: Warm starting only affects the initial point. The problem structure (sparsity pattern, cones) must remain the same as at construction time