Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Warm start ADMM #1202

Merged
merged 10 commits into from
Aug 17, 2020
18 changes: 18 additions & 0 deletions qiskit/optimization/algorithms/admm_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def __init__(self,
tau_decr: float = 2,
mu_res: float = 10,
mu_merit: float = 1000,
warm_start: Optional[bool] = False,
adekusar-drl marked this conversation as resolved.
Show resolved Hide resolved
max_iter: Optional[int] = None) -> None:
"""Defines parameters for ADMM optimizer and their default values.

Expand All @@ -76,6 +77,8 @@ def __init__(self,
tau_decr: Parameter used in the rho update (UPDATE_RHO_BY_RESIDUALS).
mu_res: Parameter used in the rho update (UPDATE_RHO_BY_RESIDUALS).
mu_merit: Penalization for constraint residual. Used to compute the merit values.
warm_start: Start ADMM with pre-initialized values for binary and continuous variables
woodsp-ibm marked this conversation as resolved.
Show resolved Hide resolved
by solving a relaxed (all variables are continuous) problem first.
max_iter: Deprecated, use maxiter.
"""
super().__init__()
Expand All @@ -97,6 +100,7 @@ def __init__(self,
self.factor_c = factor_c
self.beta = beta
self.rho_initial = rho_initial
self.warm_start = warm_start

def __repr__(self) -> str:
props = ", ".join(["{}={}".format(key, value) for (key, value) in vars(self).items()])
Expand Down Expand Up @@ -292,6 +296,9 @@ def solve(self, problem: QuadraticProgram) -> ADMMOptimizationResult:
self._state.binary_indices = self._get_variable_indices(problem, Variable.Type.BINARY)
self._state.continuous_indices = self._get_variable_indices(problem,
Variable.Type.CONTINUOUS)
if self._params.warm_start:
# warm start injection for the initial values of the variables
self._warm_start(problem)

# convert optimization problem to a set of matrices and vector that are used
# at each iteration.
Expand Down Expand Up @@ -830,6 +837,17 @@ def _get_solution_residuals(self, iteration: int) -> Tuple[float, float]:

return primal_residual, dual_residual

def _warm_start(self, problem: QuadraticProgram) -> None:
qp_copy = copy.deepcopy(problem)
for variable in qp_copy.variables:
variable.vartype = VarType.CONTINUOUS
cts_result = self._continuous_optimizer.solve(qp_copy)
logger.debug("Continuous relaxation: %s", cts_result.x)

self._state.x0 = cts_result.x[self._state.binary_indices]
self._state.u = cts_result.x[self._state.continuous_indices]
self._state.z = cts_result.x[self._state.binary_indices]

@property
def parameters(self) -> ADMMParameters:
"""Returns current parameters of the optimizer.
Expand Down
34 changes: 34 additions & 0 deletions test/optimization/test_admm.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,40 @@ def test_admm_ex5(self):
self.assertIsNotNone(solution.state)
self.assertIsInstance(solution.state, ADMMState)

def test_admm_ex5_warm_start(self):
"""Example 5 but with a warm start"""
mdl = Model('ex5')

# pylint:disable=invalid-name
v = mdl.binary_var(name='v')
w = mdl.binary_var(name='w')
t = mdl.binary_var(name='t')

mdl.minimize(v + w + t)
mdl.add_constraint(2 * v + 2 * w + t <= 3, "cons1")
mdl.add_constraint(v + w + t >= 1, "cons2")
mdl.add_constraint(v + w == 1, "cons3")

op = QuadraticProgram()
op.from_docplex(mdl)

admm_params = ADMMParameters(
rho_initial=1001, beta=1000, factor_c=900,
maxiter=100, three_block=False, warm_start=True
)

solver = ADMMOptimizer(params=admm_params)
solution = solver.solve(op)

self.assertIsNotNone(solution)
self.assertIsInstance(solution, ADMMOptimizationResult)
self.assertIsNotNone(solution.x)
np.testing.assert_almost_equal([0., 1., 0.], solution.x, 3)
self.assertIsNotNone(solution.fval)
np.testing.assert_almost_equal(1., solution.fval, 3)
self.assertIsNotNone(solution.state)
self.assertIsInstance(solution.state, ADMMState)

def test_admm_ex6(self):
"""Example 6 as a unit test. Example 6 is reported in:
Gambella, C., & Simonetto, A. (2020).
Expand Down