Skip to content

Commit 6a89391

Browse files
a-matsuomanoelmarquesCryoriswoodsp-ibm
authored
Added converters argument to some optimization algorithms (qiskit-community/qiskit-aqua#1260)
* added converter argument to opt algorithms * now penalty parameter is passed properly * added docstrings for penalty Co-authored-by: Manoel Marques <[email protected]> Co-authored-by: Cryoris <[email protected]> Co-authored-by: Steve Wood <[email protected]>
1 parent 85670aa commit 6a89391

12 files changed

+331
-107
lines changed

qiskit/optimization/algorithms/grover_optimizer.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from qiskit.circuit.library import QuadraticForm
2727
from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm,
2828
OptimizationResult)
29-
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo
29+
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter
3030
from ..problems import Variable
3131
from ..problems.quadratic_program import QuadraticProgram
3232

@@ -37,23 +37,35 @@ class GroverOptimizer(OptimizationAlgorithm):
3737
"""Uses Grover Adaptive Search (GAS) to find the minimum of a QUBO function."""
3838

3939
def __init__(self, num_value_qubits: int, num_iterations: int = 3,
40-
quantum_instance: Optional[Union[BaseBackend, QuantumInstance]] = None) -> None:
40+
quantum_instance: Optional[Union[BaseBackend, QuantumInstance]] = None,
41+
converters: Optional[Union[QuadraticProgramConverter,
42+
List[QuadraticProgramConverter]]] = None,
43+
penalty: Optional[float] = None) -> None:
4144
"""
4245
Args:
4346
num_value_qubits: The number of value qubits.
4447
num_iterations: The number of iterations the algorithm will search with
4548
no improvement.
4649
quantum_instance: Instance of selected backend, defaults to Aer's statevector simulator.
50+
converters: The converters to use for converting a problem into a different form.
51+
By default, when None is specified, an internally created instance of
52+
:class:`~qiskit.optimization.converters.QuadraticProgramToQubo` will be used.
53+
penalty: The penalty factor used in the default
54+
:class:`~qiskit.optimization.converters.QuadraticProgramToQubo` converter
55+
56+
Raises:
57+
TypeError: When there one of converters is an invalid type.
4758
"""
4859
self._num_value_qubits = num_value_qubits
4960
self._num_key_qubits = None
5061
self._n_iterations = num_iterations
5162
self._quantum_instance = None
52-
self._qubo_converter = QuadraticProgramToQubo()
5363

5464
if quantum_instance is not None:
5565
self.quantum_instance = quantum_instance
5666

67+
self._converters = self._prepare_converters(converters, penalty)
68+
5769
@property
5870
def quantum_instance(self) -> QuantumInstance:
5971
"""The quantum instance to run the circuits.
@@ -142,7 +154,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
142154
self._verify_compatibility(problem)
143155

144156
# convert problem to QUBO
145-
problem_ = self._qubo_converter.convert(problem)
157+
problem_ = self._convert(problem, self._converters)
146158
problem_init = deepcopy(problem_)
147159

148160
# convert to minimization problem
@@ -154,7 +166,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
154166
problem_.objective.linear[i] = -val
155167
for (i, j), val in problem_.objective.quadratic.to_dict().items():
156168
problem_.objective.quadratic[i, j] = -val
157-
self._num_key_qubits = len(problem_.objective.linear.to_array())
169+
self._num_key_qubits = len(problem_.objective.linear.to_array()) # type: ignore
158170

159171
# Variables for tracking the optimum.
160172
optimum_found = False
@@ -259,7 +271,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult:
259271
status=OptimizationResultStatus.SUCCESS)
260272

261273
# cast binaries back to integers
262-
result = self._qubo_converter.interpret(result)
274+
result = self._interpret(result, self._converters)
263275

264276
return GroverOptimizationResult(x=result.x, fval=result.fval, variables=result.variables,
265277
operation_counts=operation_count, n_input_qubits=n_key,

qiskit/optimization/algorithms/minimum_eigen_optimizer.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from .optimization_algorithm import (OptimizationResultStatus, OptimizationAlgorithm,
2121
OptimizationResult)
22-
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo
22+
from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter
2323
from ..problems.quadratic_program import QuadraticProgram, Variable
2424

2525

@@ -101,8 +101,9 @@ class MinimumEigenOptimizer(OptimizationAlgorithm):
101101
result = optimizer.solve(problem)
102102
"""
103103

104-
def __init__(self, min_eigen_solver: MinimumEigensolver, penalty: Optional[float] = None
105-
) -> None:
104+
def __init__(self, min_eigen_solver: MinimumEigensolver, penalty: Optional[float] = None,
105+
converters: Optional[Union[QuadraticProgramConverter,
106+
List[QuadraticProgramConverter]]] = None) -> None:
106107
"""
107108
This initializer takes the minimum eigen solver to be used to approximate the ground state
108109
of the resulting Hamiltonian as well as a optional penalty factor to scale penalty terms
@@ -112,10 +113,17 @@ def __init__(self, min_eigen_solver: MinimumEigensolver, penalty: Optional[float
112113
Args:
113114
min_eigen_solver: The eigen solver to find the ground state of the Hamiltonian.
114115
penalty: The penalty factor to be used, or ``None`` for applying a default logic.
116+
converters: The converters to use for converting a problem into a different form.
117+
By default, when None is specified, an internally created instance of
118+
:class:`~qiskit.optimization.converters.QuadraticProgramToQubo` will be used.
119+
120+
Raises:
121+
TypeError: When there one of converters is an invalid type.
115122
"""
116123
self._min_eigen_solver = min_eigen_solver
117124
self._penalty = penalty
118-
self._qubo_converter = QuadraticProgramToQubo()
125+
126+
self._converters = self._prepare_converters(converters, penalty)
119127

120128
def get_compatibility_msg(self, problem: QuadraticProgram) -> str:
121129
"""Checks whether a given problem can be solved with this optimizer.
@@ -158,7 +166,7 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
158166
self._verify_compatibility(problem)
159167

160168
# convert problem to QUBO
161-
problem_ = self._qubo_converter.convert(problem)
169+
problem_ = self._convert(problem, self._converters)
162170

163171
# construct operator and offset
164172
operator, offset = problem_.to_ising()
@@ -191,7 +199,8 @@ def solve(self, problem: QuadraticProgram) -> MinimumEigenOptimizationResult:
191199
# translate result back to integers
192200
result = OptimizationResult(x=x, fval=fval, variables=problem_.variables,
193201
status=OptimizationResultStatus.SUCCESS)
194-
result = self._qubo_converter.interpret(result)
202+
203+
result = self._interpret(result, self._converters)
195204

196205
return MinimumEigenOptimizationResult(x=result.x, fval=result.fval,
197206
variables=result.variables,

qiskit/optimization/algorithms/optimization_algorithm.py

Lines changed: 155 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
from .. import QiskitOptimizationError
2222
from ..problems.quadratic_program import QuadraticProgram, Variable
23+
from ..converters.quadratic_program_to_qubo import (QuadraticProgramToQubo,
24+
QuadraticProgramConverter)
2325

2426

2527
class OptimizationResultStatus(Enum):
@@ -35,85 +37,6 @@ class OptimizationResultStatus(Enum):
3537
"""the optimization algorithm obtained an infeasible solution."""
3638

3739

38-
class OptimizationAlgorithm(ABC):
39-
"""An abstract class for optimization algorithms in Qiskit's optimization module."""
40-
41-
@abstractmethod
42-
def get_compatibility_msg(self, problem: QuadraticProgram) -> str:
43-
"""Checks whether a given problem can be solved with the optimizer implementing this method.
44-
45-
Args:
46-
problem: The optimization problem to check compatibility.
47-
48-
Returns:
49-
Returns the incompatibility message. If the message is empty no issues were found.
50-
"""
51-
52-
def is_compatible(self, problem: QuadraticProgram) -> bool:
53-
"""Checks whether a given problem can be solved with the optimizer implementing this method.
54-
55-
Args:
56-
problem: The optimization problem to check compatibility.
57-
58-
Returns:
59-
Returns True if the problem is compatible, False otherwise.
60-
"""
61-
return len(self.get_compatibility_msg(problem)) == 0
62-
63-
@abstractmethod
64-
def solve(self, problem: QuadraticProgram) -> 'OptimizationResult':
65-
"""Tries to solves the given problem using the optimizer.
66-
67-
Runs the optimizer to try to solve the optimization problem.
68-
69-
Args:
70-
problem: The problem to be solved.
71-
72-
Returns:
73-
The result of the optimizer applied to the problem.
74-
75-
Raises:
76-
QiskitOptimizationError: If the problem is incompatible with the optimizer.
77-
"""
78-
raise NotImplementedError
79-
80-
def _verify_compatibility(self, problem: QuadraticProgram) -> None:
81-
"""Verifies that the problem is suitable for this optimizer. If the problem is not
82-
compatible then an exception is raised. This method is for convenience for concrete
83-
optimizers and is not intended to be used by end user.
84-
85-
Args:
86-
problem: Problem to verify.
87-
88-
Returns:
89-
None
90-
91-
Raises:
92-
QiskitOptimizationError: If the problem is incompatible with the optimizer.
93-
94-
"""
95-
# check compatibility and raise exception if incompatible
96-
msg = self.get_compatibility_msg(problem)
97-
if msg:
98-
raise QiskitOptimizationError('Incompatible problem: {}'.format(msg))
99-
100-
def _get_feasibility_status(self, problem: QuadraticProgram,
101-
x: Union[List[float], np.ndarray]) -> OptimizationResultStatus:
102-
"""Returns whether the input result is feasible or not for the given problem.
103-
104-
Args:
105-
problem: Problem to verify.
106-
x: the input result list.
107-
108-
Returns:
109-
The status of the result.
110-
"""
111-
is_feasible = problem.is_feasible(x)
112-
113-
return OptimizationResultStatus.SUCCESS if is_feasible \
114-
else OptimizationResultStatus.INFEASIBLE
115-
116-
11740
class OptimizationResult:
11841
"""A base class for optimization results.
11942
@@ -279,3 +202,156 @@ def variable_names(self) -> List[str]:
279202
The list of variable names of the optimization problem.
280203
"""
281204
return self._variable_names
205+
206+
207+
class OptimizationAlgorithm(ABC):
208+
"""An abstract class for optimization algorithms in Qiskit's optimization module."""
209+
210+
@abstractmethod
211+
def get_compatibility_msg(self, problem: QuadraticProgram) -> str:
212+
"""Checks whether a given problem can be solved with the optimizer implementing this method.
213+
214+
Args:
215+
problem: The optimization problem to check compatibility.
216+
217+
Returns:
218+
Returns the incompatibility message. If the message is empty no issues were found.
219+
"""
220+
221+
def is_compatible(self, problem: QuadraticProgram) -> bool:
222+
"""Checks whether a given problem can be solved with the optimizer implementing this method.
223+
224+
Args:
225+
problem: The optimization problem to check compatibility.
226+
227+
Returns:
228+
Returns True if the problem is compatible, False otherwise.
229+
"""
230+
return len(self.get_compatibility_msg(problem)) == 0
231+
232+
@abstractmethod
233+
def solve(self, problem: QuadraticProgram) -> 'OptimizationResult':
234+
"""Tries to solves the given problem using the optimizer.
235+
236+
Runs the optimizer to try to solve the optimization problem.
237+
238+
Args:
239+
problem: The problem to be solved.
240+
241+
Returns:
242+
The result of the optimizer applied to the problem.
243+
244+
Raises:
245+
QiskitOptimizationError: If the problem is incompatible with the optimizer.
246+
"""
247+
raise NotImplementedError
248+
249+
def _verify_compatibility(self, problem: QuadraticProgram) -> None:
250+
"""Verifies that the problem is suitable for this optimizer. If the problem is not
251+
compatible then an exception is raised. This method is for convenience for concrete
252+
optimizers and is not intended to be used by end user.
253+
254+
Args:
255+
problem: Problem to verify.
256+
257+
Returns:
258+
None
259+
260+
Raises:
261+
QiskitOptimizationError: If the problem is incompatible with the optimizer.
262+
263+
"""
264+
# check compatibility and raise exception if incompatible
265+
msg = self.get_compatibility_msg(problem)
266+
if msg:
267+
raise QiskitOptimizationError('Incompatible problem: {}'.format(msg))
268+
269+
def _get_feasibility_status(self, problem: QuadraticProgram,
270+
x: Union[List[float], np.ndarray]) -> OptimizationResultStatus:
271+
"""Returns whether the input result is feasible or not for the given problem.
272+
273+
Args:
274+
problem: Problem to verify.
275+
x: the input result list.
276+
277+
Returns:
278+
The status of the result.
279+
"""
280+
is_feasible = problem.is_feasible(x)
281+
282+
return OptimizationResultStatus.SUCCESS if is_feasible \
283+
else OptimizationResultStatus.INFEASIBLE
284+
285+
def _prepare_converters(self, converters: Optional[Union[QuadraticProgramConverter,
286+
List[QuadraticProgramConverter]]],
287+
penalty: Optional[float] = None) -> List[QuadraticProgramConverter]:
288+
"""Prepare a list of converters from the input.
289+
290+
Args:
291+
converters: The converters to use for converting a problem into a different form.
292+
By default, when None is specified, an internally created instance of
293+
:class:`~qiskit.optimization.converters.QuadraticProgramToQubo` will be used.
294+
penalty: The penalty factor used in the default
295+
:class:`~qiskit.optimization.converters.QuadraticProgramToQubo` converter
296+
297+
Returns:
298+
The list of converters.
299+
300+
Raises:
301+
TypeError: When the converters include those that are not
302+
:class:`~qiskit.optimization.converters.QuadraticProgramConverter type.
303+
"""
304+
converters_ = [] # type: List[QuadraticProgramConverter]
305+
if converters is None:
306+
converters_ = [QuadraticProgramToQubo(penalty=penalty)]
307+
elif isinstance(converters, QuadraticProgramConverter):
308+
converters_ = [converters]
309+
elif isinstance(converters, list) and \
310+
all(isinstance(converter, QuadraticProgramConverter) for converter in converters):
311+
converters_ = converters
312+
else:
313+
raise TypeError('`converters` must all be of the QuadraticProgramConverter type')
314+
315+
return converters_
316+
317+
def _convert(self, problem: QuadraticProgram,
318+
converters: Union[QuadraticProgramConverter,
319+
List[QuadraticProgramConverter]]) -> QuadraticProgram:
320+
"""Convert the problem with the converters
321+
322+
Args:
323+
problem: The problem to be solved
324+
converters: The converters to use for converting a problem into a different form.
325+
326+
Returns:
327+
The problem converted by the converters.
328+
"""
329+
problem_ = problem
330+
331+
if not isinstance(converters, list):
332+
converters = [converters]
333+
334+
for converter in converters:
335+
problem_ = converter.convert(problem_)
336+
337+
return problem_
338+
339+
def _interpret(self, result: OptimizationResult,
340+
converters: Union[QuadraticProgramConverter,
341+
List[QuadraticProgramConverter]]) -> OptimizationResult:
342+
"""Convert back the result of the converted problem to the result of the original problem.
343+
344+
Args:
345+
result: The result of the converted problem.
346+
converters: The converters to use for converting back the result of the problem
347+
to the result of the original problem.
348+
349+
Returns:
350+
The result of the original problem.
351+
"""
352+
if not isinstance(converters, list):
353+
converters = [converters]
354+
355+
for converter in converters[::-1]:
356+
result = converter.interpret(result)
357+
return result

0 commit comments

Comments
 (0)