Skip to content

Commit

Permalink
Warm start ADMM (qiskit-community#1202)
Browse files Browse the repository at this point in the history
* warm start admm

* Update qiskit/optimization/algorithms/admm_optimizer.py

Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com>

* updated documentation

* moved reno to a new file

* Update releasenotes/notes/admm-warm-start-6f5b5c801bcdf78f.yaml

Co-authored-by: Manoel Marques <Manoel.Marques@ibm.com>
Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com>
  • Loading branch information
3 people authored and pbark committed Sep 16, 2020
1 parent ad86dad commit 22c68ba
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
30 changes: 30 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: bool = False,
max_iter: Optional[int] = None) -> None:
"""Defines parameters for ADMM optimizer and their default values.
Expand All @@ -76,6 +77,11 @@ 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
by solving a relaxed (all variables are continuous) problem first. This option does
not guarantee the solution will optimal or even feasible. The option should be
used when tuning other options does not help and should be considered as a hint
to the optimizer where to start its iterative process.
max_iter: Deprecated, use maxiter.
"""
super().__init__()
Expand All @@ -97,6 +103,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 +299,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 +840,26 @@ def _get_solution_residuals(self, iteration: int) -> Tuple[float, float]:

return primal_residual, dual_residual

def _warm_start(self, problem: QuadraticProgram) -> None:
"""Solves a relaxed (all variables are continuous) and initializes the optimizer state with
the found solution.
Args:
problem: a problem to solve.
Returns:
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
7 changes: 7 additions & 0 deletions releasenotes/notes/admm-warm-start-6f5b5c801bcdf78f.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
features:
- |
Introduced an option `warm_start` that should be used when tuning other options does not help.
When this option is enabled, a relaxed problem (all variables are continuous) is solved first
and the solution is used to initialize the state of the optimizer before it starts the
iterative process in the `solve` method.
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

0 comments on commit 22c68ba

Please sign in to comment.