Skip to content

Commit 00fd67e

Browse files
authored
Revise CplexOptimizer (#139)
* * Adds CplexOptimizer.cplex_parameters * Adds a check whether any solution is found or not * Simplifies CplexOptimizer * update
1 parent e7f7132 commit 00fd67e

File tree

3 files changed

+90
-40
lines changed

3 files changed

+90
-40
lines changed

qiskit_optimization/algorithms/cplex_optimizer.py

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,22 @@
1313
"""The CPLEX optimizer wrapped to be used within Qiskit's optimization module."""
1414

1515
import logging
16+
from typing import Any, Dict, Optional
17+
from warnings import warn
1618

1719
from qiskit.exceptions import MissingOptionalLibraryError
18-
from .optimization_algorithm import OptimizationAlgorithm, OptimizationResult
19-
from ..exceptions import QiskitOptimizationError
20+
21+
from .optimization_algorithm import (
22+
OptimizationAlgorithm,
23+
OptimizationResult,
24+
OptimizationResultStatus,
25+
)
2026
from ..problems.quadratic_program import QuadraticProgram
2127

2228
logger = logging.getLogger(__name__)
2329

2430
try:
25-
from cplex.exceptions import CplexSolverError
31+
from cplex import Cplex # pylint: disable=unused-import
2632

2733
_HAS_CPLEX = True
2834
except ImportError:
@@ -44,11 +50,14 @@ class CplexOptimizer(OptimizationAlgorithm):
4450
>>> if optimizer: result = optimizer.solve(problem)
4551
"""
4652

47-
def __init__(self, disp: bool = False) -> None:
53+
def __init__(
54+
self, disp: bool = False, cplex_parameters: Optional[Dict[str, Any]] = None
55+
) -> None:
4856
"""Initializes the CplexOptimizer.
4957
5058
Args:
5159
disp: Whether to print CPLEX output or not.
60+
cplex_parameters: The parameters for CPLEX.
5261
5362
Raises:
5463
MissingOptionalLibraryError: CPLEX is not installed.
@@ -61,6 +70,7 @@ def __init__(self, disp: bool = False) -> None:
6170
)
6271

6372
self._disp = disp
73+
self._cplex_parameters = cplex_parameters
6474

6575
@staticmethod
6676
def is_cplex_installed():
@@ -84,6 +94,19 @@ def disp(self, disp: bool):
8494
"""
8595
self._disp = disp
8696

97+
@property
98+
def cplex_parameters(self) -> Optional[Dict[str, Any]]:
99+
"""Returns parameters for CPLEX"""
100+
return self._cplex_parameters
101+
102+
@cplex_parameters.setter
103+
def cplex_parameters(self, parameters: Optional[Dict[str, Any]]):
104+
"""Set parameters for CPLEX
105+
Args:
106+
parameters: The parameters for CPLEX
107+
"""
108+
self._cplex_parameters = parameters
109+
87110
# pylint:disable=unused-argument
88111
def get_compatibility_msg(self, problem: QuadraticProgram) -> str:
89112
"""Checks whether a given problem can be solved with this optimizer.
@@ -116,33 +139,26 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
116139
QiskitOptimizationError: If the problem is incompatible with the optimizer.
117140
"""
118141

119-
# convert to CPLEX problem
120-
cplex = problem.to_docplex().get_cplex()
121-
122-
# set display setting
123-
if not self.disp:
124-
cplex.set_log_stream(None)
125-
cplex.set_error_stream(None)
126-
cplex.set_warning_stream(None)
127-
cplex.set_results_stream(None)
128-
129-
# solve problem
130-
try:
131-
cplex.solve()
132-
except CplexSolverError as ex:
133-
raise QiskitOptimizationError(str(ex)) from ex
134-
135-
# process results
136-
sol = cplex.solution
137-
138-
# create results
139-
result = OptimizationResult(
140-
x=sol.get_values(),
141-
fval=sol.get_objective_value(),
142-
variables=problem.variables,
143-
status=self._get_feasibility_status(problem, sol.get_values()),
144-
raw_results=sol,
145-
)
146-
147-
# return solution
148-
return result
142+
mod = problem.to_docplex()
143+
sol = mod.solve(log_output=self._disp, cplex_parameters=self._cplex_parameters)
144+
if sol is None:
145+
# no solution is found
146+
warn("CPLEX cannot solve the model")
147+
x = [0.0] * mod.number_of_variables
148+
return OptimizationResult(
149+
x=x,
150+
fval=problem.objective.evaluate(x),
151+
variables=problem.variables,
152+
status=OptimizationResultStatus.FAILURE,
153+
raw_results=None,
154+
)
155+
else:
156+
# a solution is found
157+
x = sol.get_values(mod.iter_variables())
158+
return OptimizationResult(
159+
x=x,
160+
fval=sol.get_objective_value(),
161+
variables=problem.variables,
162+
status=self._get_feasibility_status(problem, x),
163+
raw_results=sol,
164+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
features:
3+
- |
4+
Simplifies :class`qiskit_optimization.algorithms.CplexOptimizer` by
5+
calling CPLEX from `docplex.mp.model.Model.solve` directly.
6+
Also adds a fallback code if no solution is found by CPLEX.
7+
- |
8+
Adds ``cplex_parameters`` as a dictionary to
9+
:class`qiskit_optimization.algorithms.CplexOptimizer`
10+
so that users can set parameters for CPLEX such as time limit
11+
and number of threads.

test/algorithms/test_cplex_optimizer.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
""" Test Cplex Optimizer """
1414

1515
import unittest
16-
from test.optimization_test_case import (
17-
QiskitOptimizationTestCase,
18-
requires_extra_library,
19-
)
20-
from ddt import ddt, data
21-
from qiskit_optimization.algorithms import CplexOptimizer
16+
from test.optimization_test_case import QiskitOptimizationTestCase, requires_extra_library
17+
18+
import numpy as np
19+
from ddt import data, ddt
20+
21+
from qiskit_optimization.algorithms import CplexOptimizer, OptimizationResultStatus
2222
from qiskit_optimization.problems import QuadraticProgram
2323

2424

@@ -51,6 +51,29 @@ def test_cplex_optimizer(self, config):
5151
for i in range(problem.get_num_vars()):
5252
self.assertAlmostEqual(result.x[i], x[i])
5353

54+
@data(
55+
("op_ip1.lp", [0, 2], 6),
56+
("op_mip1.lp", [1, 1, 0], 6),
57+
("op_lp1.lp", [0.25, 1.75], 5.8750),
58+
)
59+
@requires_extra_library
60+
def test_cplex_optimizer_no_solution(self, config):
61+
"""Cplex Optimizer Test if no solution is found"""
62+
cplex_optimizer = CplexOptimizer(disp=False, cplex_parameters={"timelimit": 0})
63+
# unpack configuration
64+
filename, _, _ = config
65+
66+
# load optimization problem
67+
problem = QuadraticProgram()
68+
lp_file = self.get_resource_path(filename, "algorithms/resources")
69+
problem.read_from_lp_file(lp_file)
70+
71+
# solve problem with cplex
72+
result = cplex_optimizer.solve(problem)
73+
np.testing.assert_array_almost_equal(result.x, np.zeros(problem.get_num_vars()))
74+
self.assertEqual(result.status, OptimizationResultStatus.FAILURE)
75+
self.assertEqual(result.raw_results, None)
76+
5477

5578
if __name__ == "__main__":
5679
unittest.main()

0 commit comments

Comments
 (0)